From dfac04d1394ff68d68b99c13edf919007f8bb038 Mon Sep 17 00:00:00 2001 From: janat08 Date: Thu, 9 Nov 2017 05:17:28 +0600 Subject: Update introduction-to-smart-contracts.rst --- docs/introduction-to-smart-contracts.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index aedc0c09..1af2014c 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -119,8 +119,8 @@ that does not allow any arithmetic operations. It is suitable for storing addresses of contracts or keypairs belonging to external persons. The keyword ``public`` automatically generates a function that allows you to access the current value of the state variable. -Without this keyword, other contracts have no way to access the variable. -The function will look something like this:: +Without this keyword, other contracts have no way to access the variable, + but raw implementation would look something like this:: function minter() returns (address) { return minter; } -- cgit v1.2.3 From 088d0e5dc12f125ac00ee9a073ddada64100c033 Mon Sep 17 00:00:00 2001 From: Federico Bond Date: Sun, 24 Dec 2017 04:30:49 -0300 Subject: docs: Improve assembly grammar definition --- docs/assembly.rst | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/docs/assembly.rst b/docs/assembly.rst index 825892b3..effd736b 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -861,38 +861,37 @@ Grammar:: AssemblyItem = Identifier | AssemblyBlock | - FunctionalAssemblyExpression | + AssemblyExpression | AssemblyLocalDefinition | - FunctionalAssemblyAssignment | AssemblyAssignment | + AssemblyStackAssignment | LabelDefinition | AssemblyIf | AssemblySwitch | AssemblyFunctionDefinition | AssemblyFor | - 'break' | 'continue' | - SubAssembly | 'dataSize' '(' Identifier ')' | - LinkerSymbol | - 'errorLabel' | 'bytecodeSize' | - NumberLiteral | StringLiteral | HexLiteral + 'break' | + 'continue' | + SubAssembly + AssemblyExpression = AssemblyCall | Identifier | AssemblyLiteral + AssemblyLiteral = NumberLiteral | StringLiteral | HexLiteral Identifier = [a-zA-Z_$] [a-zA-Z_0-9]* - FunctionalAssemblyExpression = Identifier '(' ( AssemblyItem ( ',' AssemblyItem )* )? ')' - AssemblyLocalDefinition = 'let' IdentifierOrList ':=' FunctionalAssemblyExpression - FunctionalAssemblyAssignment = IdentifierOrList ':=' FunctionalAssemblyExpression + AssemblyCall = Identifier '(' ( AssemblyExpression ( ',' AssemblyExpression )* )? ')' + AssemblyLocalDefinition = 'let' IdentifierOrList ( ':=' AssemblyExpression )? + AssemblyAssignment = IdentifierOrList ':=' AssemblyExpression IdentifierOrList = Identifier | '(' IdentifierList ')' IdentifierList = Identifier ( ',' Identifier)* - AssemblyAssignment = '=:' Identifier + AssemblyStackAssignment = '=:' Identifier LabelDefinition = Identifier ':' - AssemblyIf = 'if' FunctionalAssemblyExpression AssemblyBlock - AssemblySwitch = 'switch' FunctionalAssemblyExpression AssemblyCase* + AssemblyIf = 'if' AssemblyExpression AssemblyBlock + AssemblySwitch = 'switch' AssemblyExpression AssemblyCase* ( 'default' AssemblyBlock )? - AssemblyCase = 'case' FunctionalAssemblyExpression AssemblyBlock + AssemblyCase = 'case' AssemblyExpression AssemblyBlock AssemblyFunctionDefinition = 'function' Identifier '(' IdentifierList? ')' ( '->' '(' IdentifierList ')' )? AssemblyBlock - AssemblyFor = 'for' ( AssemblyBlock | FunctionalAssemblyExpression) - FunctionalAssemblyExpression ( AssemblyBlock | FunctionalAssemblyExpression) AssemblyBlock + AssemblyFor = 'for' ( AssemblyBlock | AssemblyExpression ) + AssemblyExpression ( AssemblyBlock | AssemblyExpression ) AssemblyBlock SubAssembly = 'assembly' Identifier AssemblyBlock - LinkerSymbol = 'linkerSymbol' '(' StringLiteral ')' NumberLiteral = HexNumber | DecimalNumber HexLiteral = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'') StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"' -- cgit v1.2.3 From 18539e53530c812790fbb396a5e5ca5806efb6ba Mon Sep 17 00:00:00 2001 From: Raghav Dua Date: Tue, 23 Jan 2018 23:23:18 +0530 Subject: Abstract Contracts: Add note about function type --- docs/contracts.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/contracts.rst b/docs/contracts.rst index 273ea43a..69d47fef 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -1053,6 +1053,19 @@ but they can be used as base contracts:: If a contract inherits from an abstract contract and does not implement all non-implemented functions by overriding, it will itself be abstract. +Note that an abstract function is different from a :ref:`Function Type ` even though their syntax looks very similar. + +Example of Abstract function (a function declaration) +:: + + function foo(address) external returns (address); + +Example of a Function Type (a variable declaration, where the variable is of type ``function``) +:: + + function(address) external returns (address) foo; + + .. index:: ! contract;interface, ! interface contract ********** -- cgit v1.2.3 From 694fc6835f600c124bdc538fe454056aea024dc8 Mon Sep 17 00:00:00 2001 From: Raghav Dua Date: Tue, 23 Jan 2018 23:43:04 +0530 Subject: correct terminology --- docs/contracts.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 69d47fef..a9b42f15 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -1053,9 +1053,9 @@ but they can be used as base contracts:: If a contract inherits from an abstract contract and does not implement all non-implemented functions by overriding, it will itself be abstract. -Note that an abstract function is different from a :ref:`Function Type ` even though their syntax looks very similar. +Note that a function without implementation is different from a :ref:`Function Type ` even though their syntax looks very similar. -Example of Abstract function (a function declaration) +Example of function without implementation (a function declaration) :: function foo(address) external returns (address); -- cgit v1.2.3 From 5497a851582c8c0d5407ead926f9041b2701a626 Mon Sep 17 00:00:00 2001 From: William Entriken Date: Mon, 29 Jan 2018 14:45:36 -0500 Subject: Clarify name documentation for mixedCase --- docs/style-guide.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 4c0d44f0..ab1af13d 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -684,7 +684,7 @@ naming styles. * ``mixedCase`` (differs from CapitalizedWords by initial lowercase character!) * ``Capitalized_Words_With_Underscores`` -.. note:: When using abbreviations in CapWords, capitalize all the letters of the abbreviation. Thus HTTPServerError is better than HttpServerError. +.. note:: When using initialisms in CapWords, capitalize all the letters of the initialisms. Thus HTTPServerError is better than HttpServerError. When using initialisms is mixedCase, capitalize all the letters of the initialisms, except keep the first one lower case if it is the beginning of the name. Thus xmlHTTPRequest is better than XMLHTTPRequest. Names to Avoid -- cgit v1.2.3 From bbdc885478423edf7819bb9984de8a0a92c3230d Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 14 Feb 2018 05:02:02 +0100 Subject: Increment version number. --- CMakeLists.txt | 2 +- Changelog.md | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a0b52621..5d4ff161 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.20") +set(PROJECT_VERSION "0.4.21") 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 b2e88eca..ade9ac86 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,11 @@ +### 0.4.21 (unreleased) + +Features: + + +Bugfixes: + + ### 0.4.20 (2018-02-14) Features: -- cgit v1.2.3 From 3804f29a8d0c545a6d2cc1f60f7e5dbf5eb454cc Mon Sep 17 00:00:00 2001 From: Alexandre Bezroutchko Date: Wed, 14 Feb 2018 09:49:06 +0100 Subject: fix a typo --- docs/control-structures.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 1a8ee25b..7be92cfa 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -407,7 +407,7 @@ and the low-level functions ``call``, ``delegatecall`` and ``callcode`` -- those of an exception instead of "bubbling up". .. warning:: - The low-level ``call``, ``delegatecall`` and ``callcode`` will return success if the calling account is non-existent, as part of the design of EVM. Existence must be checked prior to calling if desired. + The low-level ``call``, ``delegatecall`` and ``callcode`` will return success if the called account is non-existent, as part of the design of EVM. Existence must be checked prior to calling if desired. Catching exceptions is not yet possible. -- cgit v1.2.3 From ca0e323f9f9447f3b31dcbe5986ca74af39a7d8a Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 14 Feb 2018 16:44:46 +0100 Subject: Fix PPA script. --- scripts/release_ppa.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/release_ppa.sh b/scripts/release_ppa.sh index b314d00d..ffb5717d 100755 --- a/scripts/release_ppa.sh +++ b/scripts/release_ppa.sh @@ -168,7 +168,7 @@ override_dh_shlibdeps: dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info override_dh_auto_configure: - dh_auto_configure -- -DINSTALL_LLLC=Off + dh_auto_configure -- -DINSTALL_LLLC=Off EOF cat < debian/copyright Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -- cgit v1.2.3 From e08a20607049175e241654efec90371d2693931c Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Feb 2018 11:58:50 +0100 Subject: Disallow uninitialized storage pointers as experimental 0.5.0 feature. --- Changelog.md | 1 + libsolidity/analysis/TypeChecker.cpp | 6 +++- test/libsolidity/SolidityNameAndTypeResolution.cpp | 32 ++++++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index ade9ac86..58604ca2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.4.21 (unreleased) Features: + * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. Bugfixes: diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index d67142e4..f62ddeb9 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -972,7 +972,11 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) string errorText{"Uninitialized storage pointer."}; if (varDecl.referenceLocation() == VariableDeclaration::Location::Default) errorText += " Did you mean ' memory " + varDecl.name() + "'?"; - m_errorReporter.warning(varDecl.location(), errorText); + solAssert(m_scope, ""); + if (m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + m_errorReporter.declarationError(varDecl.location(), errorText); + else + m_errorReporter.warning(varDecl.location(), errorText); } } else if (dynamic_cast(type(varDecl).get())) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 315c7c5f..2b102312 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -3021,6 +3021,20 @@ BOOST_AUTO_TEST_CASE(uninitialized_mapping_array_variable) CHECK_WARNING(sourceCode, "Uninitialized storage pointer"); } +BOOST_AUTO_TEST_CASE(uninitialized_mapping_array_variable_050) +{ + char const* sourceCode = R"( + pragma experimental "v0.5.0"; + contract C { + function f() pure public { + mapping(uint => uint)[] storage x; + x; + } + } + )"; + CHECK_ERROR(sourceCode, DeclarationError, "Uninitialized storage pointer"); +} + BOOST_AUTO_TEST_CASE(no_delete_on_storage_pointers) { char const* sourceCode = R"( @@ -3320,6 +3334,24 @@ BOOST_AUTO_TEST_CASE(non_initialized_references) CHECK_WARNING(text, "Uninitialized storage pointer"); } +BOOST_AUTO_TEST_CASE(non_initialized_references_050) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract c + { + struct s { + uint a; + } + function f() public { + s storage x; + } + } + )"; + + CHECK_ERROR(text, DeclarationError, "Uninitialized storage pointer"); +} + BOOST_AUTO_TEST_CASE(keccak256_with_large_integer_constant) { char const* text = R"( -- cgit v1.2.3 From 2b5a5a8669cee8698f3f2cf970417a7123fbbe25 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Feb 2018 13:04:40 +0100 Subject: Make addmod and mulmod revert if the last argument is zero. --- Changelog.md | 1 + docs/miscellaneous.rst | 4 ++-- docs/units-and-global-variables.rst | 4 ++-- libsolidity/codegen/ExpressionCompiler.cpp | 6 +++++- test/libsolidity/SolidityEndToEndTest.cpp | 27 +++++++++++++++++++++++++++ 5 files changed, 37 insertions(+), 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index 58604ca2..d4e91c26 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.4.21 (unreleased) Features: + * Code Generator: Assert that ``k != 0`` for ``molmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature. * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 1c4f918c..328ec6ea 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -327,8 +327,8 @@ Global Variables - ``sha256(...) returns (bytes32)``: compute the SHA-256 hash of the :ref:`(tightly packed) arguments ` - ``ripemd160(...) returns (bytes20)``: compute the RIPEMD-160 hash of the :ref:`(tightly packed) arguments ` - ``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: recover address associated with the public key from elliptic curve signature, return zero on error -- ``addmod(uint x, uint y, uint k) returns (uint)``: compute ``(x + y) % k`` where the addition is performed with arbitrary precision and does not wrap around at ``2**256`` -- ``mulmod(uint x, uint y, uint k) returns (uint)``: compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256`` +- ``addmod(uint x, uint y, uint k) returns (uint)``: compute ``(x + y) % k`` where the addition is performed with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0. +- ``mulmod(uint x, uint y, uint k) returns (uint)``: compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0. - ``this`` (current contract's type): the current contract, explicitly convertible to ``address`` - ``super``: the contract one level higher in the inheritance hierarchy - ``selfdestruct(address recipient)``: destroy the current contract, sending its funds to the given address diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index ce58cf56..8f49c600 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -107,9 +107,9 @@ Mathematical and Cryptographic Functions ---------------------------------------- ``addmod(uint x, uint y, uint k) returns (uint)``: - compute ``(x + y) % k`` where the addition is performed with arbitrary precision and does not wrap around at ``2**256``. + compute ``(x + y) % k`` where the addition is performed with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0. ``mulmod(uint x, uint y, uint k) returns (uint)``: - compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256``. + compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0. ``keccak256(...) returns (bytes32)``: compute the Ethereum-SHA-3 (Keccak-256) hash of the :ref:`(tightly packed) arguments ` ``sha256(...) returns (bytes32)``: diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 8e1cf019..61920592 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -765,7 +765,11 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) case FunctionType::Kind::AddMod: case FunctionType::Kind::MulMod: { - for (unsigned i = 0; i < 3; i ++) + arguments[2]->accept(*this); + utils().convertType(*arguments[2]->annotation().type, IntegerType(256)); + m_context << Instruction::DUP1 << Instruction::ISZERO; + m_context.appendConditionalInvalid(); + for (unsigned i = 1; i < 3; i ++) { arguments[2 - i]->accept(*this); utils().convertType(*arguments[2 - i]->annotation().type, IntegerType(256)); diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 0611e71d..4d20f4f5 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -7459,6 +7459,33 @@ BOOST_AUTO_TEST_CASE(addmod_mulmod) ABI_CHECK(callContractFunction("test()"), encodeArgs(u256(0))); } +BOOST_AUTO_TEST_CASE(addmod_mulmod_zero) +{ + char const* sourceCode = R"( + contract C { + function f() pure returns (uint) { + addmod(1, 2, 0); + return 2; + } + function g() pure returns (uint) { + mulmod(1, 2, 0); + return 2; + } + function h() pure returns (uint) { + mulmod(0, 1, 2); + mulmod(1, 0, 2); + addmod(0, 1, 2); + addmod(1, 0, 2); + return 2; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), encodeArgs()); + ABI_CHECK(callContractFunction("g()"), encodeArgs()); + ABI_CHECK(callContractFunction("h()"), encodeArgs(2)); +} + BOOST_AUTO_TEST_CASE(divisiod_by_zero) { char const* sourceCode = R"( -- cgit v1.2.3 From fa0ab6bb219def335b9b0bcf409eb35bbfa57346 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Fri, 16 Feb 2018 16:04:30 +0100 Subject: Fix compilation problem from issue 3530 --- libjulia/optimiser/SimplificationRules.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libjulia/optimiser/SimplificationRules.cpp b/libjulia/optimiser/SimplificationRules.cpp index e5dbb059..070d5484 100644 --- a/libjulia/optimiser/SimplificationRules.cpp +++ b/libjulia/optimiser/SimplificationRules.cpp @@ -41,7 +41,7 @@ SimplificationRule const* SimplificationRules::findFirstMatch(Expressio static SimplificationRules rules; - FunctionalInstruction const& instruction = boost::get(_expr); + FunctionalInstruction const& instruction = boost::get(_expr); for (auto const& rule: rules.m_rules[byte(instruction.instruction)]) { rules.resetMatchGroups(); @@ -100,7 +100,7 @@ bool Pattern::matches(Expression const& _expr) const { if (_expr.type() != typeid(Literal)) return false; - Literal const& literal = boost::get(_expr); + Literal const& literal = boost::get(_expr); if (literal.kind != assembly::LiteralKind::Number) return false; if (m_data && *m_data != u256(literal.value)) @@ -111,7 +111,7 @@ bool Pattern::matches(Expression const& _expr) const { if (_expr.type() != typeid(FunctionalInstruction)) return false; - FunctionalInstruction const& instr = boost::get(_expr); + FunctionalInstruction const& instr = boost::get(_expr); if (m_instruction != instr.instruction) return false; assertThrow(m_arguments.size() == instr.arguments.size(), OptimizerException, ""); @@ -168,7 +168,7 @@ Expression Pattern::toExpression(SourceLocation const& _location) const u256 Pattern::d() const { - Literal const& literal = boost::get(matchGroupValue()); + Literal const& literal = boost::get(matchGroupValue()); assertThrow(literal.kind == assembly::LiteralKind::Number, OptimizerException, ""); return u256(literal.value); } -- cgit v1.2.3 From 1d4547ab037db2f21fcb31219ae28a1d295ab270 Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Tue, 13 Feb 2018 21:40:16 +0100 Subject: Minor improvement: check sources - returns error, if "sources" is an array, an empty object or not defined - Added new test-cases in test/libsolidity/StandardCompiler.cpp --- Changelog.md | 2 +- libsolidity/interface/StandardCompiler.cpp | 6 ++++- test/libsolidity/StandardCompiler.cpp | 36 ++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index d4e91c26..c856839e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,7 +6,7 @@ Features: Bugfixes: - + * Standard JSON: catch errors properly when invalid "sources" are passed ### 0.4.20 (2018-02-14) diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 04f5bd25..84dedfb8 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -236,7 +236,11 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) return formatFatalError("JSONError", "Only \"Solidity\" is supported as a language."); Json::Value const& sources = _input["sources"]; - if (!sources) + + if (!sources.isObject() && !sources.isNull()) + return formatFatalError("JSONError", "\"sources\" is not a JSON object."); + + if (sources.empty()) return formatFatalError("JSONError", "No input sources specified."); Json::Value errors = Json::arrayValue; diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index e48624e5..8da536d8 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -154,6 +154,42 @@ BOOST_AUTO_TEST_CASE(no_sources) BOOST_CHECK(containsError(result, "JSONError", "No input sources specified.")); } +BOOST_AUTO_TEST_CASE(no_sources_empty_object) +{ + char const* input = R"( + { + "language": "Solidity", + "sources": {} + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "No input sources specified.")); +} + +BOOST_AUTO_TEST_CASE(no_sources_empty_array) +{ + char const* input = R"( + { + "language": "Solidity", + "sources": [] + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "\"sources\" is not a JSON object.")); +} + +BOOST_AUTO_TEST_CASE(sources_is_array) +{ + char const* input = R"( + { + "language": "Solidity", + "sources": ["aa", "bb"] + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "\"sources\" is not a JSON object.")); +} + BOOST_AUTO_TEST_CASE(smoke_test) { char const* input = R"( -- cgit v1.2.3 From bfcf4a86c320a2e2a606b17137909a44d46211a5 Mon Sep 17 00:00:00 2001 From: Evgeny Medvedev Date: Sun, 18 Feb 2018 23:43:09 +0700 Subject: Add gas forwarding details to address related functions in units-and-global-variables.rst --- docs/units-and-global-variables.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 7af97376..66a4b4a9 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -154,15 +154,15 @@ Address Related ``
.balance`` (``uint256``): balance of the :ref:`address` in Wei ``
.transfer(uint256 amount)``: - send given amount of Wei to :ref:`address`, throws on failure + send given amount of Wei to :ref:`address`, throws on failure, forwards 2300 gas stipend ``
.send(uint256 amount) returns (bool)``: - send given amount of Wei to :ref:`address`, returns ``false`` on failure + send given amount of Wei to :ref:`address`, returns ``false`` on failure, forwards 2300 gas stipend ``
.call(...) returns (bool)``: - issue low-level ``CALL``, returns ``false`` on failure + issue low-level ``CALL``, returns ``false`` on failure, forwards all available gas ``
.callcode(...) returns (bool)``: - issue low-level ``CALLCODE``, returns ``false`` on failure + issue low-level ``CALLCODE``, returns ``false`` on failure, forwards all available gas ``
.delegatecall(...) returns (bool)``: - issue low-level ``DELEGATECALL``, returns ``false`` on failure + issue low-level ``DELEGATECALL``, returns ``false`` on failure, forwards all available gas For more information, see the section on :ref:`address`. -- cgit v1.2.3 From d7532f7b9c33cd191f400e9af34f4f06a4aef0fa Mon Sep 17 00:00:00 2001 From: Federico Bond Date: Thu, 26 Oct 2017 17:56:00 -0300 Subject: Convert static SourceReferenceFormatter functions to member ones --- libsolidity/interface/SourceReferenceFormatter.cpp | 55 +++++++++------------- libsolidity/interface/SourceReferenceFormatter.h | 36 +++++++------- solc/CommandLineInterface.cpp | 23 +++++---- test/libjulia/Common.cpp | 8 ++-- test/libsolidity/GasMeter.cpp | 6 ++- test/libsolidity/SolidityExecutionFramework.h | 9 ++-- 6 files changed, 68 insertions(+), 69 deletions(-) diff --git a/libsolidity/interface/SourceReferenceFormatter.cpp b/libsolidity/interface/SourceReferenceFormatter.cpp index aeafaf2d..9d02c498 100644 --- a/libsolidity/interface/SourceReferenceFormatter.cpp +++ b/libsolidity/interface/SourceReferenceFormatter.cpp @@ -31,15 +31,11 @@ namespace dev namespace solidity { -void SourceReferenceFormatter::printSourceLocation( - ostream& _stream, - SourceLocation const* _location, - function const& _scannerFromSourceName -) +void SourceReferenceFormatter::printSourceLocation(SourceLocation const* _location) { if (!_location || !_location->sourceName) return; // Nothing we can print here - auto const& scanner = _scannerFromSourceName(*_location->sourceName); + auto const& scanner = m_scannerFromSourceName(*_location->sourceName); int startLine; int startColumn; tie(startLine, startColumn) = scanner.translatePositionToLineColumn(_location->start); @@ -64,21 +60,22 @@ void SourceReferenceFormatter::printSourceLocation( endColumn = startColumn + locationLength; } - _stream << line << endl; + m_stream << line << endl; + for_each( line.cbegin(), line.cbegin() + startColumn, - [&_stream](char const& ch) { _stream << (ch == '\t' ? '\t' : ' '); } + [this](char const& ch) { m_stream << (ch == '\t' ? '\t' : ' '); } ); - _stream << "^"; + m_stream << "^"; if (endColumn > startColumn + 2) - _stream << string(endColumn - startColumn - 2, '-'); + m_stream << string(endColumn - startColumn - 2, '-'); if (endColumn > startColumn + 1) - _stream << "^"; - _stream << endl; + m_stream << "^"; + m_stream << endl; } else - _stream << + m_stream << scanner.lineAtPosition(_location->start) << endl << string(startColumn, ' ') << @@ -86,50 +83,44 @@ void SourceReferenceFormatter::printSourceLocation( "Spanning multiple lines.\n"; } -void SourceReferenceFormatter::printSourceName( - ostream& _stream, - SourceLocation const* _location, - function const& _scannerFromSourceName -) +void SourceReferenceFormatter::printSourceName(SourceLocation const* _location) { if (!_location || !_location->sourceName) return; // Nothing we can print here - auto const& scanner = _scannerFromSourceName(*_location->sourceName); + auto const& scanner = m_scannerFromSourceName(*_location->sourceName); int startLine; int startColumn; tie(startLine, startColumn) = scanner.translatePositionToLineColumn(_location->start); - _stream << *_location->sourceName << ":" << (startLine + 1) << ":" << (startColumn + 1) << ": "; + m_stream << *_location->sourceName << ":" << (startLine + 1) << ":" << (startColumn + 1) << ": "; } void SourceReferenceFormatter::printExceptionInformation( - ostream& _stream, Exception const& _exception, - string const& _name, - function const& _scannerFromSourceName + string const& _name ) { SourceLocation const* location = boost::get_error_info(_exception); auto secondarylocation = boost::get_error_info(_exception); - printSourceName(_stream, location, _scannerFromSourceName); + printSourceName(location); - _stream << _name; + m_stream << _name; if (string const* description = boost::get_error_info(_exception)) - _stream << ": " << *description << endl; + m_stream << ": " << *description << endl; else - _stream << endl; + m_stream << endl; - printSourceLocation(_stream, location, _scannerFromSourceName); + printSourceLocation(location); if (secondarylocation && !secondarylocation->infos.empty()) { for (auto info: secondarylocation->infos) { - printSourceName(_stream, &info.second, _scannerFromSourceName); - _stream << info.first << endl; - printSourceLocation(_stream, &info.second, _scannerFromSourceName); + printSourceName(&info.second); + m_stream << info.first << endl; + printSourceLocation(&info.second); } - _stream << endl; + m_stream << endl; } } diff --git a/libsolidity/interface/SourceReferenceFormatter.h b/libsolidity/interface/SourceReferenceFormatter.h index e8676d60..c212f822 100644 --- a/libsolidity/interface/SourceReferenceFormatter.h +++ b/libsolidity/interface/SourceReferenceFormatter.h @@ -38,22 +38,23 @@ namespace solidity class Scanner; // forward class CompilerStack; // forward -struct SourceReferenceFormatter +class SourceReferenceFormatter { public: using ScannerFromSourceNameFun = std::function; - /// Prints source location if it is given. - static void printSourceLocation( + + explicit SourceReferenceFormatter( std::ostream& _stream, - SourceLocation const* _location, ScannerFromSourceNameFun const& _scannerFromSourceName - ); - static void printExceptionInformation( - std::ostream& _stream, - Exception const& _exception, - std::string const& _name, - ScannerFromSourceNameFun const& _scannerFromSourceName - ); + ): + m_stream(_stream), + m_scannerFromSourceName(_scannerFromSourceName) + {} + + /// Prints source location if it is given. + void printSourceLocation(SourceLocation const* _location); + void printExceptionInformation(Exception const& _exception, std::string const& _name); + static std::string formatExceptionInformation( Exception const& _exception, std::string const& _name, @@ -61,16 +62,17 @@ public: ) { std::ostringstream errorOutput; - printExceptionInformation(errorOutput, _exception, _name, _scannerFromSourceName); + + SourceReferenceFormatter formatter(errorOutput, _scannerFromSourceName); + formatter.printExceptionInformation(_exception, _name); return errorOutput.str(); } private: /// Prints source name if location is given. - static void printSourceName( - std::ostream& _stream, - SourceLocation const* _location, - ScannerFromSourceNameFun const& _scannerFromSourceName - ); + void printSourceName(SourceLocation const* _location); + + std::ostream& m_stream; + ScannerFromSourceNameFun const& m_scannerFromSourceName; }; } diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index adcfee9c..8383afed 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -777,7 +777,10 @@ bool CommandLineInterface::processInput() } m_compiler.reset(new CompilerStack(fileReader)); + auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return m_compiler->scanner(_sourceName); }; + SourceReferenceFormatter formatter(cerr, scannerFromSourceName); + try { if (m_args.count(g_argMetadataLiteral) > 0) @@ -796,11 +799,9 @@ bool CommandLineInterface::processInput() bool successful = m_compiler->compile(); for (auto const& error: m_compiler->errors()) - SourceReferenceFormatter::printExceptionInformation( - cerr, + formatter.printExceptionInformation( *error, - (error->type() == Error::Type::Warning) ? "Warning" : "Error", - scannerFromSourceName + (error->type() == Error::Type::Warning) ? "Warning" : "Error" ); if (!successful) @@ -808,7 +809,7 @@ bool CommandLineInterface::processInput() } catch (CompilerError const& _exception) { - SourceReferenceFormatter::printExceptionInformation(cerr, _exception, "Compiler error", scannerFromSourceName); + formatter.printExceptionInformation(_exception, "Compiler error"); return false; } catch (InternalCompilerError const& _exception) @@ -828,7 +829,7 @@ bool CommandLineInterface::processInput() if (_error.type() == Error::Type::DocstringParsingError) cerr << "Documentation parsing error: " << *boost::get_error_info(_error) << endl; else - SourceReferenceFormatter::printExceptionInformation(cerr, _error, _error.typeName(), scannerFromSourceName); + formatter.printExceptionInformation(_error, _error.typeName()); return false; } @@ -1086,15 +1087,17 @@ bool CommandLineInterface::assemble( return false; } } + for (auto const& sourceAndStack: assemblyStacks) { auto const& stack = sourceAndStack.second; + auto scannerFromSourceName = [&](string const&) -> Scanner const& { return stack.scanner(); }; + SourceReferenceFormatter formatter(cerr, scannerFromSourceName); + for (auto const& error: stack.errors()) - SourceReferenceFormatter::printExceptionInformation( - cerr, + formatter.printExceptionInformation( *error, - (error->type() == Error::Type::Warning) ? "Warning" : "Error", - [&](string const&) -> Scanner const& { return stack.scanner(); } + (error->type() == Error::Type::Warning) ? "Warning" : "Error" ); if (!Error::containsOnlyWarnings(stack.errors())) successful = false; diff --git a/test/libjulia/Common.cpp b/test/libjulia/Common.cpp index e1ab8215..00336977 100644 --- a/test/libjulia/Common.cpp +++ b/test/libjulia/Common.cpp @@ -40,12 +40,12 @@ using namespace dev::solidity; void dev::julia::test::printErrors(ErrorList const& _errors, Scanner const& _scanner) { + SourceReferenceFormatter formatter(cout, [&](std::string const&) -> Scanner const& { return _scanner; }); + for (auto const& error: _errors) - SourceReferenceFormatter::printExceptionInformation( - cout, + formatter.printExceptionInformation( *error, - (error->type() == Error::Type::Warning) ? "Warning" : "Error", - [&](std::string const&) -> Scanner const& { return _scanner; } + (error->type() == Error::Type::Warning) ? "Warning" : "Error" ); } diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index 86e8201b..9d3409dd 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -136,8 +136,10 @@ BOOST_AUTO_TEST_CASE(non_overlapping_filtered_costs) { BOOST_CHECK_MESSAGE(false, "Source locations should not overlap!"); auto scannerFromSource = [&](string const& _sourceName) -> Scanner const& { return m_compiler.scanner(_sourceName); }; - SourceReferenceFormatter::printSourceLocation(cout, &first->first->location(), scannerFromSource); - SourceReferenceFormatter::printSourceLocation(cout, &second->first->location(), scannerFromSource); + SourceReferenceFormatter formatter(cout, scannerFromSource); + + formatter.printSourceLocation(&first->first->location()); + formatter.printSourceLocation(&second->first->location()); } } } diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h index b0daaba9..b853d558 100644 --- a/test/libsolidity/SolidityExecutionFramework.h +++ b/test/libsolidity/SolidityExecutionFramework.h @@ -60,12 +60,13 @@ public: m_compiler.setOptimiserSettings(m_optimize, m_optimizeRuns); if (!m_compiler.compile()) { + auto scannerFromSourceName = [&](std::string const& _sourceName) -> solidity::Scanner const& { return m_compiler.scanner(_sourceName); }; + SourceReferenceFormatter formatter(std::cerr, scannerFromSourceName); + for (auto const& error: m_compiler.errors()) - SourceReferenceFormatter::printExceptionInformation( - std::cerr, + formatter.printExceptionInformation( *error, - (error->type() == Error::Type::Warning) ? "Warning" : "Error", - [&](std::string const& _sourceName) -> solidity::Scanner const& { return m_compiler.scanner(_sourceName); } + (error->type() == Error::Type::Warning) ? "Warning" : "Error" ); BOOST_ERROR("Compiling contract failed"); } -- cgit v1.2.3 From 305d5f70c25d63d602477c6b576b2f62d388c032 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 19 Feb 2018 15:13:49 +0100 Subject: Fix scannerFromSourceName use-after-free. --- libsolidity/interface/SourceReferenceFormatter.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libsolidity/interface/SourceReferenceFormatter.h b/libsolidity/interface/SourceReferenceFormatter.h index c212f822..a32babdc 100644 --- a/libsolidity/interface/SourceReferenceFormatter.h +++ b/libsolidity/interface/SourceReferenceFormatter.h @@ -45,10 +45,10 @@ public: explicit SourceReferenceFormatter( std::ostream& _stream, - ScannerFromSourceNameFun const& _scannerFromSourceName + ScannerFromSourceNameFun _scannerFromSourceName ): m_stream(_stream), - m_scannerFromSourceName(_scannerFromSourceName) + m_scannerFromSourceName(std::move(_scannerFromSourceName)) {} /// Prints source location if it is given. @@ -72,7 +72,7 @@ private: void printSourceName(SourceLocation const* _location); std::ostream& m_stream; - ScannerFromSourceNameFun const& m_scannerFromSourceName; + ScannerFromSourceNameFun m_scannerFromSourceName; }; } -- cgit v1.2.3 From ca6957da37454ddd474b1feeaf02f7d06cba06b0 Mon Sep 17 00:00:00 2001 From: Emilio Almansi Date: Fri, 16 Feb 2018 14:37:11 -0300 Subject: Added property _documentation_ to Function, Event, and Modifier definition classes. --- libsolidity/ast/ASTJsonConverter.cpp | 3 +++ test/libsolidity/ASTJSON.cpp | 23 ++++++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 51249f20..cd9f7eca 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -324,6 +324,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node) { std::vector> attributes = { make_pair("name", _node.name()), + make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue), // FIXME: remove with next breaking release make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() <= StateMutability::View), make_pair("payable", _node.isPayable()), @@ -365,6 +366,7 @@ bool ASTJsonConverter::visit(ModifierDefinition const& _node) { setJsonNode(_node, "ModifierDefinition", { make_pair("name", _node.name()), + make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue), make_pair("visibility", Declaration::visibilityToString(_node.visibility())), make_pair("parameters", toJson(_node.parameterList())), make_pair("body", toJson(_node.body())) @@ -386,6 +388,7 @@ bool ASTJsonConverter::visit(EventDefinition const& _node) m_inEvent = true; setJsonNode(_node, "EventDefinition", { make_pair("name", _node.name()), + make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue), make_pair("parameters", toJson(_node.parameterList())), make_pair("anonymous", _node.isAnonymous()) }); diff --git a/test/libsolidity/ASTJSON.cpp b/test/libsolidity/ASTJSON.cpp index 31165922..808a747a 100644 --- a/test/libsolidity/ASTJSON.cpp +++ b/test/libsolidity/ASTJSON.cpp @@ -237,16 +237,31 @@ BOOST_AUTO_TEST_CASE(documentation) " and has a line-breaking comment.*/" "contract C {}" ); + c.addSource("c", + "contract C {" + " /** Some comment on Evt.*/ event Evt();" + " /** Some comment on mod.*/ modifier mod() { _; }" + " /** Some comment on fn.*/ function fn() public {}" + "}" + ); c.parseAndAnalyze(); map sourceIndices; sourceIndices["a"] = 0; sourceIndices["b"] = 1; + sourceIndices["c"] = 2; Json::Value astJsonA = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); Json::Value documentationA = astJsonA["children"][0]["attributes"]["documentation"]; BOOST_CHECK_EQUAL(documentationA, "This contract is empty"); Json::Value astJsonB = ASTJsonConverter(true, sourceIndices).toJson(c.ast("b")); Json::Value documentationB = astJsonB["children"][0]["attributes"]["documentation"]; BOOST_CHECK_EQUAL(documentationB, "This contract is empty and has a line-breaking comment."); + Json::Value astJsonC = ASTJsonConverter(true, sourceIndices).toJson(c.ast("c")); + Json::Value documentationC0 = astJsonC["children"][0]["children"][0]["attributes"]["documentation"]; + Json::Value documentationC1 = astJsonC["children"][0]["children"][1]["attributes"]["documentation"]; + Json::Value documentationC2 = astJsonC["children"][0]["children"][2]["attributes"]["documentation"]; + BOOST_CHECK_EQUAL(documentationC0, "Some comment on Evt."); + BOOST_CHECK_EQUAL(documentationC1, "Some comment on mod."); + BOOST_CHECK_EQUAL(documentationC2, "Some comment on fn."); //same tests for non-legacy mode astJsonA = ASTJsonConverter(false, sourceIndices).toJson(c.ast("a")); documentationA = astJsonA["nodes"][0]["documentation"]; @@ -254,7 +269,13 @@ BOOST_AUTO_TEST_CASE(documentation) astJsonB = ASTJsonConverter(false, sourceIndices).toJson(c.ast("b")); documentationB = astJsonB["nodes"][0]["documentation"]; BOOST_CHECK_EQUAL(documentationB, "This contract is empty and has a line-breaking comment."); - + astJsonC = ASTJsonConverter(false, sourceIndices).toJson(c.ast("c")); + documentationC0 = astJsonC["nodes"][0]["nodes"][0]["documentation"]; + documentationC1 = astJsonC["nodes"][0]["nodes"][1]["documentation"]; + documentationC2 = astJsonC["nodes"][0]["nodes"][2]["documentation"]; + BOOST_CHECK_EQUAL(documentationC0, "Some comment on Evt."); + BOOST_CHECK_EQUAL(documentationC1, "Some comment on mod."); + BOOST_CHECK_EQUAL(documentationC2, "Some comment on fn."); } -- cgit v1.2.3 From 47a0e374e215c66324b291a5e6bede4f275c5f0f Mon Sep 17 00:00:00 2001 From: Emilio Almansi Date: Fri, 16 Feb 2018 14:40:17 -0300 Subject: Fix indentation. --- test/libsolidity/ASTJSON.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/libsolidity/ASTJSON.cpp b/test/libsolidity/ASTJSON.cpp index 808a747a..a165f7a9 100644 --- a/test/libsolidity/ASTJSON.cpp +++ b/test/libsolidity/ASTJSON.cpp @@ -239,9 +239,9 @@ BOOST_AUTO_TEST_CASE(documentation) ); c.addSource("c", "contract C {" - " /** Some comment on Evt.*/ event Evt();" - " /** Some comment on mod.*/ modifier mod() { _; }" - " /** Some comment on fn.*/ function fn() public {}" + " /** Some comment on Evt.*/ event Evt();" + " /** Some comment on mod.*/ modifier mod() { _; }" + " /** Some comment on fn.*/ function fn() public {}" "}" ); c.parseAndAnalyze(); -- cgit v1.2.3 From 7530592a583dcb1a02bc5c8a01bde89a399a448d Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 19 Feb 2018 16:44:49 +0100 Subject: Changelog entry. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index c856839e..de0b742e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Features: Bugfixes: + * JSON-AST: Add "documentation" property to function, event and modifier definition. * Standard JSON: catch errors properly when invalid "sources" are passed ### 0.4.20 (2018-02-14) -- cgit v1.2.3 From 2b23d02c8365051fd1d87fe89cebee3a1ccf27be Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 19 Feb 2018 16:52:37 +0100 Subject: Update introduction-to-smart-contracts.rst --- docs/introduction-to-smart-contracts.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 1af2014c..d022004e 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -118,9 +118,11 @@ that is publicly accessible. The ``address`` type is a 160-bit value that does not allow any arithmetic operations. It is suitable for storing addresses of contracts or keypairs belonging to external persons. The keyword ``public`` automatically generates a function that -allows you to access the current value of the state variable. -Without this keyword, other contracts have no way to access the variable, - but raw implementation would look something like this:: +allows you to access the current value of the state variable +from outside of the contract. +Without this keyword, other contracts have no way to access the variable. +The code of the function generated by the compiler is roughly equivalent +to the following:: function minter() returns (address) { return minter; } -- cgit v1.2.3 From 09887de266665c1233cd2aa853e8271e6af282db Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 19 Feb 2018 19:02:49 +0100 Subject: Fix link. --- docs/contracts.rst | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index a9b42f15..d3978672 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -1053,15 +1053,13 @@ but they can be used as base contracts:: If a contract inherits from an abstract contract and does not implement all non-implemented functions by overriding, it will itself be abstract. -Note that a function without implementation is different from a :ref:`Function Type ` even though their syntax looks very similar. +Note that a function without implementation is different from a :ref:`Function Type ` even though their syntax looks very similar. -Example of function without implementation (a function declaration) -:: +Example of function without implementation (a function declaration):: function foo(address) external returns (address); -Example of a Function Type (a variable declaration, where the variable is of type ``function``) -:: +Example of a Function Type (a variable declaration, where the variable is of type ``function``):: function(address) external returns (address) foo; -- cgit v1.2.3 From a680cbd47411c20b8bda59b535fc138974e72f0d Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 19 Feb 2018 19:21:16 +0100 Subject: Test ``_offset`` for constants in inline assembly. --- test/libsolidity/SolidityNameAndTypeResolution.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 2b102312..7c03d7cb 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -5776,6 +5776,21 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage_variable_access_out_of_functions) CHECK_SUCCESS_NO_WARNINGS(text); } +BOOST_AUTO_TEST_CASE(inline_assembly_constant_variable_via_offset) +{ + char const* text = R"( + contract test { + uint constant x = 2; + function f() pure public { + assembly { + let r := x_offset + } + } + } + )"; + CHECK_ERROR(text, TypeError, "Constant variables not supported by inline assembly."); +} + BOOST_AUTO_TEST_CASE(inline_assembly_calldata_variables) { char const* text = R"( -- cgit v1.2.3 From 930485cd244cc32fd499dc3d59f1220b972c9da1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 19 Feb 2018 19:21:33 +0100 Subject: Properly warn about the use of constants in inline assembly in connection with ``_offset`` and ``_slot``. --- libsolidity/analysis/TypeChecker.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index f62ddeb9..0ee16c89 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -804,7 +804,12 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) solAssert(!!declaration, ""); if (auto var = dynamic_cast(declaration)) { - if (ref->second.isSlot || ref->second.isOffset) + if (var->isConstant()) + { + m_errorReporter.typeError(_identifier.location, "Constant variables not supported by inline assembly."); + return size_t(-1); + } + else if (ref->second.isSlot || ref->second.isOffset) { if (!var->isStateVariable() && !var->type()->dataStoredIn(DataLocation::Storage)) { @@ -817,11 +822,6 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) return size_t(-1); } } - else if (var->isConstant()) - { - m_errorReporter.typeError(_identifier.location, "Constant variables not supported by inline assembly."); - return size_t(-1); - } else if (!var->isLocalVariable()) { m_errorReporter.typeError(_identifier.location, "Only local variables are supported. To access storage variables, use the _slot and _offset suffixes."); -- cgit v1.2.3 From 83692360b1a3213fbe8a65b08b892980312b7ac1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 19 Feb 2018 19:21:58 +0100 Subject: Changelog entry. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index de0b742e..d6b83d6e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,6 +8,7 @@ Features: Bugfixes: * JSON-AST: Add "documentation" property to function, event and modifier definition. * Standard JSON: catch errors properly when invalid "sources" are passed + * Type Checker: Properly warn when using ``_offset`` and ``_slot`` for constants in inline assembly. ### 0.4.20 (2018-02-14) -- cgit v1.2.3 From bce545c990996830d5afb7322651f1cf16f3fbe0 Mon Sep 17 00:00:00 2001 From: Evgeny Medvedev Date: Tue, 20 Feb 2018 02:06:42 +0700 Subject: Add adjustable/no adjustable gas details to address related functions in units-and-global-variables.rst --- docs/units-and-global-variables.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 66a4b4a9..dd16ccc6 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -154,15 +154,15 @@ Address Related ``
.balance`` (``uint256``): balance of the :ref:`address` in Wei ``
.transfer(uint256 amount)``: - send given amount of Wei to :ref:`address`, throws on failure, forwards 2300 gas stipend + send given amount of Wei to :ref:`address`, throws on failure, forwards 2300 gas stipend, not adjustable ``
.send(uint256 amount) returns (bool)``: - send given amount of Wei to :ref:`address`, returns ``false`` on failure, forwards 2300 gas stipend + send given amount of Wei to :ref:`address`, returns ``false`` on failure, forwards 2300 gas stipend, not adjustable ``
.call(...) returns (bool)``: - issue low-level ``CALL``, returns ``false`` on failure, forwards all available gas + issue low-level ``CALL``, returns ``false`` on failure, forwards all available gas, adjustable ``
.callcode(...) returns (bool)``: - issue low-level ``CALLCODE``, returns ``false`` on failure, forwards all available gas + issue low-level ``CALLCODE``, returns ``false`` on failure, forwards all available gas, adjustable ``
.delegatecall(...) returns (bool)``: - issue low-level ``DELEGATECALL``, returns ``false`` on failure, forwards all available gas + issue low-level ``DELEGATECALL``, returns ``false`` on failure, forwards all available gas, adjustable For more information, see the section on :ref:`address`. -- cgit v1.2.3 From 360009c37667183a7aeab48b5827b4d3e6055b17 Mon Sep 17 00:00:00 2001 From: William Entriken Date: Mon, 19 Feb 2018 15:38:55 -0500 Subject: Typo --- docs/abi-spec.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 8095a3b7..f249bbcd 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -157,7 +157,7 @@ on the type of ``X`` being - ``uint``: ``enc(X)`` is the big-endian encoding of ``X``, padded on the higher-order (left) side with zero-bytes such that the length is a multiple of 32 bytes. - ``address``: as in the ``uint160`` case -- ``int``: ``enc(X)`` is the big-endian two's complement encoding of ``X``, padded on the higher-oder (left) side with ``0xff`` for negative ``X`` and with zero bytes for positive ``X`` such that the length is a multiple of 32 bytes. +- ``int``: ``enc(X)`` is the big-endian two's complement encoding of ``X``, padded on the higher-order (left) side with ``0xff`` for negative ``X`` and with zero bytes for positive ``X`` such that the length is a multiple of 32 bytes. - ``bool``: as in the ``uint8`` case, where ``1`` is used for ``true`` and ``0`` for ``false`` - ``fixedx``: ``enc(X)`` is ``enc(X * 10**N)`` where ``X * 10**N`` is interpreted as a ``int256``. - ``fixed``: as in the ``fixed128x19`` case -- cgit v1.2.3 From 29d40ad42af1e7168ec3ea077a2bcfb71c68dc60 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 20 Feb 2018 12:19:19 +0100 Subject: Clarify padding direction. --- docs/abi-spec.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index f249bbcd..58b88a07 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -155,15 +155,15 @@ on the type of ``X`` being ``enc(X) = enc(enc_utf8(X))``, i.e. ``X`` is utf-8 encoded and this value is interpreted as of ``bytes`` type and encoded further. Note that the length used in this subsequent encoding is the number of bytes of the utf-8 encoded string, not its number of characters. -- ``uint``: ``enc(X)`` is the big-endian encoding of ``X``, padded on the higher-order (left) side with zero-bytes such that the length is a multiple of 32 bytes. +- ``uint``: ``enc(X)`` is the big-endian encoding of ``X``, padded on the higher-order (left) side with zero-bytes such that the length is 32 bytes. - ``address``: as in the ``uint160`` case -- ``int``: ``enc(X)`` is the big-endian two's complement encoding of ``X``, padded on the higher-order (left) side with ``0xff`` for negative ``X`` and with zero bytes for positive ``X`` such that the length is a multiple of 32 bytes. +- ``int``: ``enc(X)`` is the big-endian two's complement encoding of ``X``, padded on the higher-order (left) side with ``0xff`` for negative ``X`` and with zero bytes for positive ``X`` such that the length is 32 bytes. - ``bool``: as in the ``uint8`` case, where ``1`` is used for ``true`` and ``0`` for ``false`` - ``fixedx``: ``enc(X)`` is ``enc(X * 10**N)`` where ``X * 10**N`` is interpreted as a ``int256``. - ``fixed``: as in the ``fixed128x19`` case - ``ufixedx``: ``enc(X)`` is ``enc(X * 10**N)`` where ``X * 10**N`` is interpreted as a ``uint256``. - ``ufixed``: as in the ``ufixed128x19`` case -- ``bytes``: ``enc(X)`` is the sequence of bytes in ``X`` padded with zero-bytes to a length of 32. +- ``bytes``: ``enc(X)`` is the sequence of bytes in ``X`` padded with zero-bytes on the _right_ to a length of 32. Note that for any ``X``, ``len(enc(X))`` is a multiple of 32. -- cgit v1.2.3 From decfa0eed0512ec20aa15c3b7656f8369e9fdcba Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Feb 2018 10:58:50 +0100 Subject: Test aliasing in shadowing. --- test/libsolidity/Imports.cpp | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/test/libsolidity/Imports.cpp b/test/libsolidity/Imports.cpp index 03287b28..dc1174f4 100644 --- a/test/libsolidity/Imports.cpp +++ b/test/libsolidity/Imports.cpp @@ -266,7 +266,28 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_multiple_imports) } } - +BOOST_AUTO_TEST_CASE(shadowing_builtins_with_alias) +{ + CompilerStack c; + c.addSource("B.sol", "contract C {} pragma solidity >=0.0;"); + c.addSource("b", R"( + pragma solidity >=0.0; + import {C as msg} from "B.sol"; + )"); + BOOST_CHECK(c.compile()); + auto numErrors = c.errors().size(); + // Sometimes we get the prerelease warning, sometimes not. + BOOST_CHECK(1 <= numErrors && numErrors <= 2); + for (auto const& e: c.errors()) + { + string const* msg = e->comment(); + BOOST_REQUIRE(msg); + BOOST_CHECK( + msg->find("pre-release") != string::npos || + msg->find("shadows a builtin symbol") != string::npos + ); + } +} BOOST_AUTO_TEST_SUITE_END() -- cgit v1.2.3 From 4272d16f30e78d252e96b80f1202a61a06518a9f Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Feb 2018 11:03:56 +0100 Subject: Fix shadowing for imports with aliases. --- libsolidity/analysis/NameAndTypeResolver.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 5e4d414b..662792a3 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -457,9 +457,10 @@ bool DeclarationRegistrationHelper::registerDeclaration( if (!_errorLocation) _errorLocation = &_declaration.location(); + string name = _name ? *_name : _declaration.name(); Declaration const* shadowedDeclaration = nullptr; - if (_warnOnShadow && !_declaration.name().empty() && _container.enclosingContainer()) - for (auto const* decl: _container.enclosingContainer()->resolveName(_declaration.name(), true)) + if (_warnOnShadow && !name.empty() && _container.enclosingContainer()) + for (auto const* decl: _container.enclosingContainer()->resolveName(name, true)) shadowedDeclaration = decl; if (!_container.registerDeclaration(_declaration, _name, !_declaration.isVisibleInContract())) -- cgit v1.2.3 From 58bbb04276f27d3ecfa96dda54fbad80f8bbdbea Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Feb 2018 11:04:44 +0100 Subject: Changelog entry. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index d6b83d6e..4e6e1b92 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ Features: Bugfixes: * JSON-AST: Add "documentation" property to function, event and modifier definition. + * Resolver: Properly determine shadowing for imports with aliases. * Standard JSON: catch errors properly when invalid "sources" are passed * Type Checker: Properly warn when using ``_offset`` and ``_slot`` for constants in inline assembly. -- cgit v1.2.3 From 3ba8af4fd55e2573c6a153c3f36e9132f411db06 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 20 Feb 2018 18:03:39 +0100 Subject: Provide input for two optimizer tests. --- test/libsolidity/SolidityOptimizer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index bd635c33..33039ca9 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -175,7 +175,7 @@ BOOST_AUTO_TEST_CASE(constant_folding_both_sides) } })"; compileBothVersions(sourceCode); - compareVersions("f(uint256)"); + compareVersions("f(uint256)", 7); } BOOST_AUTO_TEST_CASE(storage_access) @@ -190,7 +190,7 @@ BOOST_AUTO_TEST_CASE(storage_access) } )"; compileBothVersions(sourceCode); - compareVersions("f(uint256)"); + compareVersions("f(uint256)", 7); } BOOST_AUTO_TEST_CASE(array_copy) -- cgit v1.2.3 From 89a8f4096c6f1cc1025d4026ebfc7b2674330458 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 20 Feb 2018 19:30:30 +0100 Subject: Provide proper input for test call. --- test/libsolidity/SolidityEndToEndTest.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 4d20f4f5..d2528a67 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -3565,8 +3565,8 @@ BOOST_AUTO_TEST_CASE(library_call_protection) } )"; compileAndRun(sourceCode, 0, "Lib"); - ABI_CHECK(callContractFunction("np(Lib.S storage)"), encodeArgs()); - ABI_CHECK(callContractFunction("v(Lib.S storage)"), encodeArgs(u160(m_sender))); + ABI_CHECK(callContractFunction("np(Lib.S storage)", 0), encodeArgs()); + ABI_CHECK(callContractFunction("v(Lib.S storage)", 0), encodeArgs(u160(m_sender))); ABI_CHECK(callContractFunction("pu()"), encodeArgs(2)); compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); ABI_CHECK(callContractFunction("s()"), encodeArgs(0)); -- cgit v1.2.3 From 0f29ac4e563f60be781b31ed9ef2693e1a19dcc8 Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Wed, 7 Feb 2018 02:05:20 +0100 Subject: Add new JSON API for better abstraction and for supporting strict JSON parsing --- Changelog.md | 2 +- libdevcore/CMakeLists.txt | 4 +- libdevcore/JSON.cpp | 109 +++++++++++++++++++++ libdevcore/JSON.h | 31 +++--- libsolc/libsolc.cpp | 16 +-- libsolidity/interface/StandardCompiler.cpp | 7 +- test/Metadata.cpp | 2 +- test/RPCSession.cpp | 14 ++- test/fuzzer.cpp | 6 +- test/libdevcore/JSON.cpp | 151 +++++++++++++++++++++++++++++ test/libsolidity/JSONCompiler.cpp | 6 +- test/libsolidity/Metadata.cpp | 5 +- test/libsolidity/SolidityABIJSON.cpp | 5 +- test/libsolidity/SolidityNatspecJSON.cpp | 5 +- test/libsolidity/StandardCompiler.cpp | 27 +++++- 15 files changed, 337 insertions(+), 53 deletions(-) create mode 100644 libdevcore/JSON.cpp create mode 100644 test/libdevcore/JSON.cpp diff --git a/Changelog.md b/Changelog.md index 4e6e1b92..8fa9d93d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,9 +2,9 @@ Features: * Code Generator: Assert that ``k != 0`` for ``molmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature. + * Standard JSON: Reject badly formatted invalid JSON inputs. * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. - Bugfixes: * JSON-AST: Add "documentation" property to function, event and modifier definition. * Resolver: Properly determine shadowing for imports with aliases. diff --git a/libdevcore/CMakeLists.txt b/libdevcore/CMakeLists.txt index a1c4c2d3..d107f701 100644 --- a/libdevcore/CMakeLists.txt +++ b/libdevcore/CMakeLists.txt @@ -2,7 +2,9 @@ file(GLOB sources "*.cpp") file(GLOB headers "*.h") add_library(devcore ${sources} ${headers}) -target_link_libraries(devcore PRIVATE ${Boost_FILESYSTEM_LIBRARIES} ${Boost_REGEX_LIBRARIES} ${Boost_SYSTEM_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(devcore PRIVATE ${JSONCPP_LIBRARY} ${Boost_FILESYSTEM_LIBRARIES} ${Boost_REGEX_LIBRARIES} ${Boost_SYSTEM_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) target_include_directories(devcore SYSTEM PUBLIC ${Boost_INCLUDE_DIRS}) target_include_directories(devcore PUBLIC "${CMAKE_SOURCE_DIR}") +target_include_directories(devcore PUBLIC "${JSONCPP_INCLUDE_DIR}") +add_dependencies(devcore jsoncpp) add_dependencies(devcore solidity_BuildInfo.h) diff --git a/libdevcore/JSON.cpp b/libdevcore/JSON.cpp new file mode 100644 index 00000000..079d4d51 --- /dev/null +++ b/libdevcore/JSON.cpp @@ -0,0 +1,109 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** @file JSON.cpp + * @author Alexander Arlt + * @date 2018 + */ + +#include "JSON.h" + +#include +#include +#include + +using namespace std; + +namespace dev +{ + +namespace +{ + +/// StreamWriterBuilder that can be constructed with specific settings +class StreamWriterBuilder: public Json::StreamWriterBuilder +{ +public: + explicit StreamWriterBuilder(map const& _settings) + { + for (auto const& iter :_settings) + this->settings_[iter.first] = iter.second; + } +}; + +/// CharReaderBuilder with strict-mode settings +class StrictModeCharReaderBuilder: public Json::CharReaderBuilder +{ +public: + StrictModeCharReaderBuilder() + { + Json::CharReaderBuilder::strictMode(&this->settings_); + } +}; + +/// Serialise the JSON object (@a _input) with specific builder (@a _builder) +/// \param _input JSON input string +/// \param _builder StreamWriterBuilder that is used to create new Json::StreamWriter +/// \return serialized json object +string print(Json::Value const& _input, Json::StreamWriterBuilder const& _builder) +{ + stringstream stream; + unique_ptr writer(_builder.newStreamWriter()); + writer->write(_input, &stream); + return stream.str(); +} + +/// Parse a JSON string (@a _input) with specified builder (@ _builder) and writes resulting JSON object to (@a _json) +/// \param _builder CharReaderBuilder that is used to create new Json::CharReaders +/// \param _input JSON input string +/// \param _json [out] resulting JSON object +/// \param _errs [out] Formatted error messages +/// \return \c true if the document was successfully parsed, \c false if an error occurred. +bool parse(Json::CharReaderBuilder& _builder, string const& _input, Json::Value& _json, string* _errs) +{ + unique_ptr reader(_builder.newCharReader()); + return reader->parse(_input.c_str(), _input.c_str() + _input.length(), &_json, _errs); +} + +} // end anonymous namespace + +string jsonPrettyPrint(Json::Value const& _input) +{ + static map settings{{"indentation", " "}}; + static StreamWriterBuilder writerBuilder(settings); + return print(_input, writerBuilder); +} + +string jsonCompactPrint(Json::Value const& _input) +{ + static map settings{{"indentation", ""}}; + static StreamWriterBuilder writerBuilder(settings); + return print(_input, writerBuilder); +} + +bool jsonParseStrict(string const& _input, Json::Value& _json, string* _errs /* = nullptr */) +{ + static StrictModeCharReaderBuilder readerBuilder; + return parse(readerBuilder, _input, _json, _errs); +} + +bool jsonParse(string const& _input, Json::Value& _json, string *_errs /* = nullptr */) +{ + static Json::CharReaderBuilder readerBuilder; + return parse(readerBuilder, _input, _json, _errs); +} + +} // namespace dev diff --git a/libdevcore/JSON.h b/libdevcore/JSON.h index 8499d623..1ce822cd 100644 --- a/libdevcore/JSON.h +++ b/libdevcore/JSON.h @@ -24,21 +24,28 @@ #include -namespace dev -{ +#include + +namespace dev { /// Serialise the JSON object (@a _input) with indentation -inline std::string jsonPrettyPrint(Json::Value const& _input) -{ - return Json::StyledWriter().write(_input); -} +std::string jsonPrettyPrint(Json::Value const& _input); /// Serialise the JSON object (@a _input) without indentation -inline std::string jsonCompactPrint(Json::Value const& _input) -{ - Json::FastWriter writer; - writer.omitEndingLineFeed(); - return writer.write(_input); -} +std::string jsonCompactPrint(Json::Value const& _input); + +/// Parse a JSON string (@a _input) with enabled strict-mode and writes resulting JSON object to (@a _json) +/// \param _input JSON input string +/// \param _json [out] resulting JSON object +/// \param _errs [out] Formatted error messages +/// \return \c true if the document was successfully parsed, \c false if an error occurred. +bool jsonParseStrict(std::string const& _input, Json::Value& _json, std::string* _errs = nullptr); + +/// Parse a JSON string (@a _input) and writes resulting JSON object to (@a _json) +/// \param _input JSON input string +/// \param _json [out] resulting JSON object +/// \param _errs [out] Formatted error messages +/// \return \c true if the document was successfully parsed, \c false if an error occurred. +bool jsonParse(std::string const& _input, Json::Value& _json, std::string* _errs = nullptr); } diff --git a/libsolc/libsolc.cpp b/libsolc/libsolc.cpp index 3a6e1521..6c587e23 100644 --- a/libsolc/libsolc.cpp +++ b/libsolc/libsolc.cpp @@ -203,7 +203,7 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback { Json::Value contractInput = ret["contracts"][sourceName][contractName]; Json::Value contractOutput = Json::objectValue; - contractOutput["interface"] = dev::jsonCompactPrint(contractInput["abi"]); + contractOutput["interface"] = jsonCompactPrint(contractInput["abi"]); contractOutput["metadata"] = contractInput["metadata"]; contractOutput["functionHashes"] = contractInput["evm"]["methodIdentifiers"]; contractOutput["gasEstimates"] = translateGasEstimates(contractInput["evm"]["gasEstimates"]); @@ -219,7 +219,7 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback try { - return dev::jsonCompactPrint(output); + return jsonCompactPrint(output); } catch (...) { @@ -229,15 +229,15 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback string compileMulti(string const& _input, bool _optimize, CStyleReadFileCallback _readCallback = nullptr) { - Json::Reader reader; + string errors; Json::Value input; - if (!reader.parse(_input, input, false)) + if (!jsonParseStrict(_input, input, &errors)) { - Json::Value errors(Json::arrayValue); - errors.append("Error parsing input JSON: " + reader.getFormattedErrorMessages()); + Json::Value jsonErrors(Json::arrayValue); + jsonErrors.append("Error parsing input JSON: " + errors); Json::Value output(Json::objectValue); - output["errors"] = errors; - return dev::jsonCompactPrint(output); + output["errors"] = jsonErrors; + return jsonCompactPrint(output); } else { diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 84dedfb8..fb973d51 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -554,12 +554,11 @@ Json::Value StandardCompiler::compile(Json::Value const& _input) string StandardCompiler::compile(string const& _input) { Json::Value input; - Json::Reader reader; - + string errors; try { - if (!reader.parse(_input, input, false)) - return jsonCompactPrint(formatFatalError("JSONError", reader.getFormattedErrorMessages())); + if (!jsonParseStrict(_input, input, &errors)) + return jsonCompactPrint(formatFatalError("JSONError", errors)); } catch(...) { diff --git a/test/Metadata.cpp b/test/Metadata.cpp index e4de0a6b..1ebfd468 100644 --- a/test/Metadata.cpp +++ b/test/Metadata.cpp @@ -50,7 +50,7 @@ string bytecodeSansMetadata(string const& _bytecode) bool isValidMetadata(string const& _metadata) { Json::Value metadata; - if (!Json::Reader().parse(_metadata, metadata, false)) + if (!jsonParseStrict(_metadata, metadata)) return false; if ( diff --git a/test/RPCSession.cpp b/test/RPCSession.cpp index 60aafc85..69c75cee 100644 --- a/test/RPCSession.cpp +++ b/test/RPCSession.cpp @@ -23,8 +23,7 @@ #include -#include -#include +#include #include #include @@ -216,7 +215,7 @@ string RPCSession::personal_newAccount(string const& _password) void RPCSession::test_setChainParams(vector const& _accounts) { - static std::string const c_configString = R"( + static string const c_configString = R"( { "sealEngine": "NoProof", "params": { @@ -249,10 +248,10 @@ void RPCSession::test_setChainParams(vector const& _accounts) )"; Json::Value config; - BOOST_REQUIRE(Json::Reader().parse(c_configString, config)); + BOOST_REQUIRE(jsonParseStrict(c_configString, config)); for (auto const& account: _accounts) config["accounts"][account]["wei"] = "0x100000000000000000000000000000000000000000"; - test_setChainParams(Json::FastWriter().write(config)); + test_setChainParams(jsonCompactPrint(config)); } void RPCSession::test_setChainParams(string const& _config) @@ -328,7 +327,7 @@ Json::Value RPCSession::rpcCall(string const& _methodName, vector const& BOOST_TEST_MESSAGE("Reply: " + reply); Json::Value result; - BOOST_REQUIRE(Json::Reader().parse(reply, result, false)); + BOOST_REQUIRE(jsonParseStrict(reply, result)); if (result.isMember("error")) { @@ -371,6 +370,5 @@ string RPCSession::TransactionData::toJson() const json["gasprice"] = gasPrice; json["value"] = value; json["data"] = data; - return Json::FastWriter().write(json); - + return jsonCompactPrint(json); } diff --git a/test/fuzzer.cpp b/test/fuzzer.cpp index 578e63a4..c61410b6 100644 --- a/test/fuzzer.cpp +++ b/test/fuzzer.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include @@ -101,7 +101,7 @@ void testStandardCompiler() string input = readInput(); string outputString(compileStandard(input.c_str(), NULL)); Json::Value output; - if (!Json::Reader().parse(outputString, output)) + if (!jsonParseStrict(outputString, output)) { cout << "Compiler produced invalid JSON output." << endl; abort(); @@ -129,7 +129,7 @@ void testCompiler(bool optimize) string outputString(compileJSON(input.c_str(), optimize)); Json::Value outputJson; - if (!Json::Reader().parse(outputString, outputJson)) + if (!jsonParseStrict(outputString, outputJson)) { cout << "Compiler produced invalid JSON output." << endl; abort(); diff --git a/test/libdevcore/JSON.cpp b/test/libdevcore/JSON.cpp new file mode 100644 index 00000000..39d71b42 --- /dev/null +++ b/test/libdevcore/JSON.cpp @@ -0,0 +1,151 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * @date 2018 + * Unit tests for JSON.h. + */ + +#include + +#include "../TestHelper.h" + +using namespace std; + +namespace dev +{ +namespace test +{ + +BOOST_AUTO_TEST_SUITE(JsonTest) + +BOOST_AUTO_TEST_CASE(json_pretty_print) +{ + Json::Value json; + Json::Value jsonChild; + + jsonChild["3.1"] = "3.1"; + jsonChild["3.2"] = 2; + json["1"] = 1; + json["2"] = "2"; + json["3"] = jsonChild; + + BOOST_CHECK( + "{\n" + " \"1\" : 1,\n" + " \"2\" : \"2\",\n" + " \"3\" : \n" + " {\n" + " \"3.1\" : \"3.1\",\n" + " \"3.2\" : 2\n" + " }\n" + "}" == jsonPrettyPrint(json)); +} + +BOOST_AUTO_TEST_CASE(json_compact_print) +{ + Json::Value json; + Json::Value jsonChild; + + jsonChild["3.1"] = "3.1"; + jsonChild["3.2"] = 2; + json["1"] = 1; + json["2"] = "2"; + json["3"] = jsonChild; + + BOOST_CHECK("{\"1\":1,\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":2}}" == jsonCompactPrint(json)); +} + +BOOST_AUTO_TEST_CASE(parse_json_not_strict) +{ + Json::Value json; + std::string errors; + + // just parse a valid json input + BOOST_CHECK(jsonParse("{\"1\":1,\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":2}}", json, &errors)); + BOOST_CHECK(json["1"] == 1); + BOOST_CHECK(json["2"] == "2"); + BOOST_CHECK(json["3"]["3.1"] == "3.1"); + BOOST_CHECK(json["3"]["3.2"] == 2); + + // trailing garbage is allowed here + BOOST_CHECK(jsonParse("{\"1\":2,\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":3}}}}}}}}}}", json, &errors)); + BOOST_CHECK(json["1"] == 2); + BOOST_CHECK(json["2"] == "2"); + BOOST_CHECK(json["3"]["3.1"] == "3.1"); + BOOST_CHECK(json["3"]["3.2"] == 3); + + // comments are allowed + BOOST_CHECK(jsonParse( + "{\"1\":3, // awesome comment\n\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":4}}", json, &errors + )); + BOOST_CHECK(json["1"] == 3); + BOOST_CHECK(json["2"] == "2"); + BOOST_CHECK(json["3"]["3.1"] == "3.1"); + BOOST_CHECK(json["3"]["3.2"] == 4); + + // root element other than object or array is allowed + BOOST_CHECK(jsonParse("[]", json, &errors)); + BOOST_CHECK(jsonParse("{}", json, &errors)); + BOOST_CHECK(jsonParse("1", json, &errors)); + BOOST_CHECK(json == 1); + BOOST_CHECK(jsonParse("\"hello\"", json, &errors)); + BOOST_CHECK(json == "hello"); + + // non-UTF-8 escapes allowed + BOOST_CHECK(jsonParse("[ \"\x80\xec\x80\" ]", json, &errors)); + BOOST_CHECK(json[0] == "\x80\xec\x80"); +} + +BOOST_AUTO_TEST_CASE(parse_json_strict) +{ + Json::Value json; + std::string errors; + + // just parse a valid json input + BOOST_CHECK(jsonParseStrict("{\"1\":1,\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":2}}", json, &errors)); + BOOST_CHECK(json["1"] == 1); + BOOST_CHECK(json["2"] == "2"); + BOOST_CHECK(json["3"]["3.1"] == "3.1"); + BOOST_CHECK(json["3"]["3.2"] == 2); + + // trailing garbage is not allowed in strict-mode + BOOST_CHECK(!jsonParseStrict("{\"1\":2,\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":3}}}}}}}}}}", json, &errors)); + + // comments are allowed in strict-mode? - that's strange... + BOOST_CHECK(jsonParseStrict( + "{\"1\":3, // awesome comment\n\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":5}}", json, &errors + )); + BOOST_CHECK(json["1"] == 3); + BOOST_CHECK(json["2"] == "2"); + BOOST_CHECK(json["3"]["3.1"] == "3.1"); + BOOST_CHECK(json["3"]["3.2"] == 5); + + // root element can only be object or array + BOOST_CHECK(jsonParseStrict("[]", json, &errors)); + BOOST_CHECK(jsonParseStrict("{}", json, &errors)); + BOOST_CHECK(!jsonParseStrict("1", json, &errors)); + BOOST_CHECK(!jsonParseStrict("\"hello\"", json, &errors)); + + // non-UTF-8 escapes allowed?? + BOOST_CHECK(jsonParseStrict("[ \"\x80\xec\x80\" ]", json, &errors)); + BOOST_CHECK(json[0] == "\x80\xec\x80"); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} diff --git a/test/libsolidity/JSONCompiler.cpp b/test/libsolidity/JSONCompiler.cpp index 0c904c77..285c5604 100644 --- a/test/libsolidity/JSONCompiler.cpp +++ b/test/libsolidity/JSONCompiler.cpp @@ -44,7 +44,7 @@ Json::Value compileSingle(string const& _input) { string output(compileJSON(_input.c_str(), dev::test::Options::get().optimize)); Json::Value ret; - BOOST_REQUIRE(Json::Reader().parse(output, ret, false)); + BOOST_REQUIRE(jsonParseStrict(output, ret)); return ret; } @@ -56,7 +56,7 @@ Json::Value compileMulti(string const& _input, bool _callback) compileJSONMulti(_input.c_str(), dev::test::Options::get().optimize) ); Json::Value ret; - BOOST_REQUIRE(Json::Reader().parse(output, ret, false)); + BOOST_REQUIRE(jsonParseStrict(output, ret)); return ret; } @@ -64,7 +64,7 @@ Json::Value compile(string const& _input) { string output(compileStandard(_input.c_str(), NULL)); Json::Value ret; - BOOST_REQUIRE(Json::Reader().parse(output, ret, false)); + BOOST_REQUIRE(jsonParseStrict(output, ret)); return ret; } diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index efe8faff..47cf1d3d 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -23,6 +23,7 @@ #include "../TestHelper.h" #include #include +#include namespace dev { @@ -111,7 +112,7 @@ BOOST_AUTO_TEST_CASE(metadata_relevant_sources) std::string const& serialisedMetadata = compilerStack.metadata("A"); BOOST_CHECK(dev::test::isValidMetadata(serialisedMetadata)); Json::Value metadata; - BOOST_REQUIRE(Json::Reader().parse(serialisedMetadata, metadata, false)); + BOOST_REQUIRE(jsonParseStrict(serialisedMetadata, metadata)); BOOST_CHECK_EQUAL(metadata["sources"].size(), 1); BOOST_CHECK(metadata["sources"].isMember("A")); @@ -149,7 +150,7 @@ BOOST_AUTO_TEST_CASE(metadata_relevant_sources_imports) std::string const& serialisedMetadata = compilerStack.metadata("C"); BOOST_CHECK(dev::test::isValidMetadata(serialisedMetadata)); Json::Value metadata; - BOOST_REQUIRE(Json::Reader().parse(serialisedMetadata, metadata, false)); + BOOST_REQUIRE(jsonParseStrict(serialisedMetadata, metadata)); BOOST_CHECK_EQUAL(metadata["sources"].size(), 3); BOOST_CHECK(metadata["sources"].isMember("A")); diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index 26bfb6d0..e242508a 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -26,7 +26,7 @@ #include #include -#include +#include namespace dev { @@ -48,7 +48,7 @@ public: Json::Value generatedInterface = m_compilerStack.contractABI(m_compilerStack.lastContractName()); Json::Value expectedInterface; - BOOST_REQUIRE(m_reader.parse(_expectedInterfaceString, expectedInterface)); + BOOST_REQUIRE(jsonParseStrict(_expectedInterfaceString, expectedInterface)); BOOST_CHECK_MESSAGE( expectedInterface == generatedInterface, "Expected:\n" << expectedInterface.toStyledString() << @@ -58,7 +58,6 @@ public: protected: CompilerStack m_compilerStack; - Json::Reader m_reader; }; BOOST_FIXTURE_TEST_SUITE(SolidityABIJSON, JSONInterfaceChecker) diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index fb09451f..e8906bb9 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -22,7 +22,7 @@ #include "../TestHelper.h" #include -#include +#include #include #include #include @@ -55,7 +55,7 @@ public: else generatedDocumentation = m_compilerStack.natspecDev(m_compilerStack.lastContractName()); Json::Value expectedDocumentation; - m_reader.parse(_expectedDocumentationString, expectedDocumentation); + jsonParseStrict(_expectedDocumentationString, expectedDocumentation); BOOST_CHECK_MESSAGE( expectedDocumentation == generatedDocumentation, "Expected:\n" << expectedDocumentation.toStyledString() << @@ -73,7 +73,6 @@ public: private: CompilerStack m_compilerStack; - Json::Reader m_reader; }; BOOST_FIXTURE_TEST_SUITE(SolidityNatspecJSON, DocumentationChecker) diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 8da536d8..404f709d 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -89,7 +89,7 @@ Json::Value compile(string const& _input) StandardCompiler compiler; string output = compiler.compile(_input); Json::Value ret; - BOOST_REQUIRE(Json::Reader().parse(output, ret, false)); + BOOST_REQUIRE(jsonParseStrict(output, ret)); return ret; } @@ -110,11 +110,11 @@ BOOST_AUTO_TEST_CASE(assume_object_input) /// Use the string interface of StandardCompiler to trigger these result = compile(""); - BOOST_CHECK(containsError(result, "JSONError", "* Line 1, Column 1\n Syntax error: value, object or array expected.\n")); + BOOST_CHECK(containsError(result, "JSONError", "* Line 1, Column 1\n Syntax error: value, object or array expected.\n* Line 1, Column 1\n A valid JSON document must be either an array or an object value.\n")); result = compile("invalid"); - BOOST_CHECK(containsError(result, "JSONError", "* Line 1, Column 1\n Syntax error: value, object or array expected.\n")); + BOOST_CHECK(containsError(result, "JSONError", "* Line 1, Column 1\n Syntax error: value, object or array expected.\n* Line 1, Column 2\n Extra non-whitespace after JSON value.\n")); result = compile("\"invalid\""); - BOOST_CHECK(containsError(result, "JSONError", "Input is not a JSON object.")); + BOOST_CHECK(containsError(result, "JSONError", "* Line 1, Column 1\n A valid JSON document must be either an array or an object value.\n")); BOOST_CHECK(!containsError(result, "JSONError", "* Line 1, Column 1\n Syntax error: value, object or array expected.\n")); result = compile("{}"); BOOST_CHECK(!containsError(result, "JSONError", "* Line 1, Column 1\n Syntax error: value, object or array expected.\n")); @@ -190,6 +190,25 @@ BOOST_AUTO_TEST_CASE(sources_is_array) BOOST_CHECK(containsError(result, "JSONError", "\"sources\" is not a JSON object.")); } +BOOST_AUTO_TEST_CASE(unexpected_trailing_test) +{ + char const* input = R"( + { + "language": "Solidity", + "sources": { + "A": { + "content": "contract A { function f() {} }" + } + } + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "* Line 10, Column 2\n Extra non-whitespace after JSON value.\n")); +} + + BOOST_AUTO_TEST_CASE(smoke_test) { char const* input = R"( -- cgit v1.2.3 From c43953f9fe25066881938944c6a33817be8f424f Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 20 Feb 2018 23:23:30 +0100 Subject: CMake: abort if linking results in undefined symbols with emscripten --- cmake/EthCompilerSettings.cmake | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index 7199f537..a9ed0a74 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -140,6 +140,9 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NO_DYNAMIC_EXECUTION=1") # Disable greedy exception catcher set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NODEJS_CATCH_EXIT=0") + # Abort if linking results in any undefined symbols + # Note: this is on by default in the CMake Emscripten module which we aren't using + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ERROR_ON_UNDEFINED_SYMBOLS=1") add_definitions(-DETH_EMSCRIPTEN=1) endif() endif() -- cgit v1.2.3 From 0936a44bea60874c36db6113de78b1570bd4c01b Mon Sep 17 00:00:00 2001 From: Li Xuanji Date: Tue, 20 Feb 2018 18:41:02 -0500 Subject: Create .gitattributes --- .gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..52031de5 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.sol linguist-language=Solidity -- cgit v1.2.3 From e2bf5de8a278e04623c7bb34a925372039095ba7 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 20 Feb 2018 18:39:00 +0100 Subject: Expect end of string at end of top-level block for assembly parser. --- Changelog.md | 1 + libsolidity/codegen/CompilerContext.cpp | 2 +- libsolidity/inlineasm/AsmParser.cpp | 7 +++++-- libsolidity/inlineasm/AsmParser.h | 3 ++- libsolidity/interface/AssemblyStack.cpp | 2 +- libsolidity/parsing/Parser.cpp | 2 +- test/libjulia/Common.cpp | 2 +- test/libjulia/Parser.cpp | 2 +- test/libsolidity/InlineAssembly.cpp | 5 +++++ 9 files changed, 18 insertions(+), 8 deletions(-) diff --git a/Changelog.md b/Changelog.md index 4e6e1b92..a404b2ae 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,6 +8,7 @@ Features: Bugfixes: * JSON-AST: Add "documentation" property to function, event and modifier definition. * Resolver: Properly determine shadowing for imports with aliases. + * Standalone Assembly: Do not ignore input after closing brace of top level block. * Standard JSON: catch errors properly when invalid "sources" are passed * Type Checker: Properly warn when using ``_offset`` and ``_slot`` for constants in inline assembly. diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 7a88475a..0198a107 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -319,7 +319,7 @@ void CompilerContext::appendInlineAssembly( ErrorList errors; ErrorReporter errorReporter(errors); auto scanner = make_shared(CharStream(_assembly), "--CODEGEN--"); - auto parserResult = assembly::Parser(errorReporter, assembly::AsmFlavour::Strict).parse(scanner); + auto parserResult = assembly::Parser(errorReporter, assembly::AsmFlavour::Strict).parse(scanner, false); #ifdef SOL_OUTPUT_ASM cout << assembly::AsmPrinter()(*parserResult) << endl; #endif diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 306b07e6..7f618e07 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -34,13 +34,16 @@ using namespace dev; using namespace dev::solidity; using namespace dev::solidity::assembly; -shared_ptr Parser::parse(std::shared_ptr const& _scanner) +shared_ptr Parser::parse(std::shared_ptr const& _scanner, bool _reuseScanner) { m_recursionDepth = 0; try { m_scanner = _scanner; - return make_shared(parseBlock()); + auto block = make_shared(parseBlock()); + if (!_reuseScanner) + expectToken(Token::EOS); + return block; } catch (FatalError const&) { diff --git a/libsolidity/inlineasm/AsmParser.h b/libsolidity/inlineasm/AsmParser.h index 015aeef3..41117228 100644 --- a/libsolidity/inlineasm/AsmParser.h +++ b/libsolidity/inlineasm/AsmParser.h @@ -41,8 +41,9 @@ public: ParserBase(_errorReporter), m_flavour(_flavour) {} /// Parses an inline assembly block starting with `{` and ending with `}`. + /// @param _reuseScanner if true, do check for end of input after the `}`. /// @returns an empty shared pointer on error. - std::shared_ptr parse(std::shared_ptr const& _scanner); + std::shared_ptr parse(std::shared_ptr const& _scanner, bool _reuseScanner); protected: using ElementaryOperation = boost::variant; diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp index 1b4bd270..c9e534c7 100644 --- a/libsolidity/interface/AssemblyStack.cpp +++ b/libsolidity/interface/AssemblyStack.cpp @@ -69,7 +69,7 @@ bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string m_errors.clear(); m_analysisSuccessful = false; m_scanner = make_shared(CharStream(_source), _sourceName); - m_parserResult = assembly::Parser(m_errorReporter, languageToAsmFlavour(m_language)).parse(m_scanner); + m_parserResult = assembly::Parser(m_errorReporter, languageToAsmFlavour(m_language)).parse(m_scanner, false); if (!m_errorReporter.errors().empty()) return false; solAssert(m_parserResult, ""); diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 05b877b5..e306e21b 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -926,7 +926,7 @@ ASTPointer Parser::parseInlineAssembly(ASTPointer con } assembly::Parser asmParser(m_errorReporter); - shared_ptr block = asmParser.parse(m_scanner); + shared_ptr block = asmParser.parse(m_scanner, true); nodeFactory.markEndPosition(); return nodeFactory.createNode(_docString, block); } diff --git a/test/libjulia/Common.cpp b/test/libjulia/Common.cpp index 00336977..7053a68d 100644 --- a/test/libjulia/Common.cpp +++ b/test/libjulia/Common.cpp @@ -56,7 +56,7 @@ pair, shared_ptr> dev::julia::test: ErrorList errors; ErrorReporter errorReporter(errors); auto scanner = make_shared(CharStream(_source), ""); - auto parserResult = assembly::Parser(errorReporter, flavour).parse(scanner); + auto parserResult = assembly::Parser(errorReporter, flavour).parse(scanner, false); if (parserResult) { BOOST_REQUIRE(errorReporter.errors().empty()); diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp index a8a41b3c..0a2dd815 100644 --- a/test/libjulia/Parser.cpp +++ b/test/libjulia/Parser.cpp @@ -52,7 +52,7 @@ bool parse(string const& _source, ErrorReporter& errorReporter) try { auto scanner = make_shared(CharStream(_source)); - auto parserResult = assembly::Parser(errorReporter, assembly::AsmFlavour::IULIA).parse(scanner); + auto parserResult = assembly::Parser(errorReporter, assembly::AsmFlavour::IULIA).parse(scanner, false); if (parserResult) { assembly::AsmAnalysisInfo analysisInfo; diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index b09eb261..70620f78 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -168,6 +168,11 @@ BOOST_AUTO_TEST_CASE(smoke_test) BOOST_CHECK(successParse("{ }")); } +BOOST_AUTO_TEST_CASE(surplus_input) +{ + CHECK_PARSE_ERROR("{ } { }", ParserError, "Expected token EOS"); +} + BOOST_AUTO_TEST_CASE(simple_instructions) { BOOST_CHECK(successParse("{ dup1 dup1 mul dup1 sub pop }")); -- cgit v1.2.3 From a048d69c03061a2b1429a02c06a48e4d6e6e6563 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 19 Feb 2018 17:20:30 +0100 Subject: Build documentation on CircleCI. --- .travis.yml | 14 -------------- circle.yml | 25 ++++++++++++++++++++++++- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/.travis.yml b/.travis.yml index ebe91939..0b05f661 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,7 +39,6 @@ env: global: - ENCRYPTION_LABEL="6d4541b72666" - SOLC_BUILD_TYPE=RelWithDebInfo - - SOLC_DOCS=Off - SOLC_EMSCRIPTEN=Off - SOLC_INSTALL_DEPS_TRAVIS=On - SOLC_RELEASE=On @@ -65,18 +64,6 @@ matrix: - ZIP_SUFFIX=ubuntu-trusty-clang - SOLC_STOREBYTECODE=On - # Documentation target, which generates documentation using Phoenix / ReadTheDocs. - - os: linux - dist: trusty - sudo: required - compiler: gcc - before_install: - - sudo apt-get -y install python-sphinx - env: - - SOLC_DOCS=On - - SOLC_RELEASE=Off - - SOLC_TESTS=Off - # Docker target, which generates a statically linked alpine image - os: linux dist: trusty @@ -184,7 +171,6 @@ before_script: script: - test $SOLC_EMSCRIPTEN != On || (scripts/test_emscripten.sh) - - test $SOLC_DOCS != On || (scripts/docs.sh) - test $SOLC_TESTS != On || (cd $TRAVIS_BUILD_DIR && scripts/tests.sh) - test $SOLC_STOREBYTECODE != On || (cd $TRAVIS_BUILD_DIR && scripts/bytecodecompare/storebytecode.sh) diff --git a/circle.yml b/circle.yml index 4edc9383..bc72623c 100644 --- a/circle.yml +++ b/circle.yml @@ -101,7 +101,7 @@ jobs: - run: name: Store commit hash and prerelease command: | - date -u +"nightly.%Y.%-m.%-d" > prerelease.txt + if [ "$CIRCLE_BRANCH" = release -o -n "$CIRCLE_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi echo -n "$CIRCLE_SHA1" > commit_hash.txt - restore_cache: key: ccache-{{ arch }}-{{ .Branch }} @@ -130,6 +130,28 @@ jobs: path: build/solc/solc destination: solc + docs: + docker: + - image: buildpack-deps:artful + steps: + - checkout + - run: + name: Install build dependencies + command: | + apt-get -qq update + apt-get -qy install python-sphinx + - run: + name: Store commit hash and prerelease + command: | + if [ "$CIRCLE_BRANCH" = release -o -n "$CIRCLE_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi + echo -n "$CIRCLE_SHA1" > commit_hash.txt + - run: + name: Build documentation + command: ./scripts/docs.sh + - store_artifacts: + path: docs/_build/html/ + destination: docs-html + workflows: version: 2 build_all: @@ -142,3 +164,4 @@ workflows: requires: - build_emscripten - build_x86 + - docs -- cgit v1.2.3 From d01786f0aa4e128f70aea2bc53a8d3c61294a587 Mon Sep 17 00:00:00 2001 From: mirgj Date: Thu, 22 Feb 2018 01:42:57 +0800 Subject: Update solidity-by-example.rst highestBidder is the address. it's highestBid that should be != 0 to be considered as pending return. It's that right? --- docs/solidity-by-example.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index b663083c..b8e158ac 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -272,7 +272,7 @@ activate themselves. // money back. require(msg.value > highestBid); - if (highestBidder != 0) { + if (highestBid != 0) { // Sending back the money by simply using // highestBidder.send(highestBid) is a security risk // because it could execute an untrusted contract. -- cgit v1.2.3 From ae02bb5aadfa49636f9931524dbc026c855201f1 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 4 Jan 2018 23:25:41 +0000 Subject: Raise error on oversized number literals in assembly --- Changelog.md | 1 + libsolidity/inlineasm/AsmAnalysis.cpp | 8 ++++++++ test/libjulia/Parser.cpp | 1 + test/libsolidity/InlineAssembly.cpp | 1 + 4 files changed, 11 insertions(+) diff --git a/Changelog.md b/Changelog.md index f25b138b..ad92c94a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Features: * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. Bugfixes: + * Assembly: Raise error on oversized number literals in assembly. * JSON-AST: Add "documentation" property to function, event and modifier definition. * Resolver: Properly determine shadowing for imports with aliases. * Standalone Assembly: Do not ignore input after closing brace of top level block. diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 2d6e58de..e672d5ef 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -82,6 +82,14 @@ bool AsmAnalyzer::operator()(assembly::Literal const& _literal) ); return false; } + else if (_literal.kind == assembly::LiteralKind::Number && bigint(_literal.value) > u256(-1)) + { + m_errorReporter.typeError( + _literal.location, + "Number literal too large (> 256 bits)" + ); + return false; + } m_info.stackHeightInfo[&_literal] = m_stackHeight; return true; } diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp index 0a2dd815..ff9474c1 100644 --- a/test/libjulia/Parser.cpp +++ b/test/libjulia/Parser.cpp @@ -228,6 +228,7 @@ BOOST_AUTO_TEST_CASE(number_literals) CHECK_ERROR("{ let x:u256 := .1:u256 }", ParserError, "Invalid number literal."); CHECK_ERROR("{ let x:u256 := 1e5:u256 }", ParserError, "Invalid number literal."); CHECK_ERROR("{ let x:u256 := 67.235:u256 }", ParserError, "Invalid number literal."); + CHECK_ERROR("{ let x:u256 := 0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff:u256 }", TypeError, "Number literal too large (> 256 bits)"); } BOOST_AUTO_TEST_CASE(builtin_types) diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 70620f78..45fb54f8 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -390,6 +390,7 @@ BOOST_AUTO_TEST_CASE(number_literals) CHECK_PARSE_ERROR("{ let x := .1 }", ParserError, "Invalid number literal."); CHECK_PARSE_ERROR("{ let x := 1e5 }", ParserError, "Invalid number literal."); CHECK_PARSE_ERROR("{ let x := 67.235 }", ParserError, "Invalid number literal."); + CHECK_STRICT_ERROR("{ let x := 0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff }", TypeError, "Number literal too large (> 256 bits)"); } BOOST_AUTO_TEST_CASE(function_definitions) -- cgit v1.2.3 From 42856e0f53c1cb9be61e9f24b9f09a7b0179cd55 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 22 Feb 2018 01:00:26 +0100 Subject: Add assertion for boolean literals in Julia-mode --- libsolidity/inlineasm/AsmAnalysis.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index e672d5ef..a05ac57d 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -90,6 +90,11 @@ bool AsmAnalyzer::operator()(assembly::Literal const& _literal) ); return false; } + else if (_literal.kind == assembly::LiteralKind::Boolean) + { + solAssert(m_flavour == AsmFlavour::IULIA, ""); + solAssert(_literal.value == "true" || _literal.value == "false", ""); + } m_info.stackHeightInfo[&_literal] = m_stackHeight; return true; } -- cgit v1.2.3 From 68a8e67cc37bf6f923f3b51781f2613168fa4d38 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 20 Sep 2017 08:52:10 +0100 Subject: Mark functions const --- libjulia/backends/evm/EVMCodeTransform.cpp | 6 +++--- libjulia/backends/evm/EVMCodeTransform.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libjulia/backends/evm/EVMCodeTransform.cpp b/libjulia/backends/evm/EVMCodeTransform.cpp index 0c7365fb..2a97429b 100644 --- a/libjulia/backends/evm/EVMCodeTransform.cpp +++ b/libjulia/backends/evm/EVMCodeTransform.cpp @@ -522,7 +522,7 @@ void CodeTransform::generateAssignment(Identifier const& _variableName) } } -int CodeTransform::variableHeightDiff(solidity::assembly::Scope::Variable const& _var, bool _forSwap) +int CodeTransform::variableHeightDiff(solidity::assembly::Scope::Variable const& _var, bool _forSwap) const { solAssert(m_context->variableStackHeights.count(&_var), ""); int heightDiff = m_assembly.stackHeight() - m_context->variableStackHeights[&_var]; @@ -537,12 +537,12 @@ int CodeTransform::variableHeightDiff(solidity::assembly::Scope::Variable const& return heightDiff; } -void CodeTransform::expectDeposit(int _deposit, int _oldHeight) +void CodeTransform::expectDeposit(int _deposit, int _oldHeight) const { solAssert(m_assembly.stackHeight() == _oldHeight + _deposit, "Invalid stack deposit."); } -void CodeTransform::checkStackHeight(void const* _astElement) +void CodeTransform::checkStackHeight(void const* _astElement) const { solAssert(m_info.stackHeightInfo.count(_astElement), "Stack height for AST element not found."); solAssert( diff --git a/libjulia/backends/evm/EVMCodeTransform.h b/libjulia/backends/evm/EVMCodeTransform.h index 0f2aaf95..f8eec0b7 100644 --- a/libjulia/backends/evm/EVMCodeTransform.h +++ b/libjulia/backends/evm/EVMCodeTransform.h @@ -133,11 +133,11 @@ private: /// Determines the stack height difference to the given variables. Throws /// if it is not yet in scope or the height difference is too large. Returns /// the (positive) stack height difference otherwise. - int variableHeightDiff(solidity::assembly::Scope::Variable const& _var, bool _forSwap); + int variableHeightDiff(solidity::assembly::Scope::Variable const& _var, bool _forSwap) const; - void expectDeposit(int _deposit, int _oldHeight); + void expectDeposit(int _deposit, int _oldHeight) const; - void checkStackHeight(void const* _astElement); + void checkStackHeight(void const* _astElement) const; julia::AbstractAssembly& m_assembly; solidity::assembly::AsmAnalysisInfo& m_info; -- cgit v1.2.3 From c182284d28faa45e668fe20293e9e08734187ee3 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 21 Feb 2018 16:33:59 +0100 Subject: Do not warn about analysis-only experimental features. --- Changelog.md | 1 + libsolidity/analysis/SyntaxChecker.cpp | 6 ++++-- test/libsolidity/SMTChecker.cpp | 7 +------ 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Changelog.md b/Changelog.md index ad92c94a..f0d00416 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Features: * Code Generator: Assert that ``k != 0`` for ``molmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature. * Standard JSON: Reject badly formatted invalid JSON inputs. * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. + * Syntax Analyser: Do not warn about experimental features if they do not concern code generation. Bugfixes: * Assembly: Raise error on oversized number literals in assembly. diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 5a3745b0..74834ba4 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -93,8 +93,10 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) m_errorReporter.syntaxError(_pragma.location(), "Duplicate experimental feature name."); else { - m_sourceUnit->annotation().experimentalFeatures.insert(ExperimentalFeatureNames.at(literal)); - m_errorReporter.warning(_pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments."); + auto feature = ExperimentalFeatureNames.at(literal); + m_sourceUnit->annotation().experimentalFeatures.insert(feature); + if (!ExperimentalFeatureOnlyAnalysis.count(feature)) + m_errorReporter.warning(_pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments."); } } } diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp index 8c955292..5088ab94 100644 --- a/test/libsolidity/SMTChecker.cpp +++ b/test/libsolidity/SMTChecker.cpp @@ -35,12 +35,6 @@ namespace test class SMTCheckerFramework: public AnalysisFramework { -public: - SMTCheckerFramework() - { - m_warningsToFilter.push_back("Experimental features are turned on."); - } - protected: virtual std::pair parseAnalyseAndReturnError( @@ -103,6 +97,7 @@ BOOST_AUTO_TEST_CASE(warn_on_struct) } )"; CHECK_WARNING_ALLOW_MULTI(text, (vector{ + "Experimental feature", "Assertion checker does not yet implement this expression.", "Assertion checker does not yet support the type of this variable." })); -- cgit v1.2.3 From 388718b59f604c944be6816ba50068014e563fb0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Feb 2018 16:55:21 +0100 Subject: Introduce emit statement. --- docs/grammar.txt | 3 +- libsolidity/analysis/TypeChecker.cpp | 9 ++ libsolidity/analysis/TypeChecker.h | 1 + libsolidity/ast/AST.h | 21 +++++ libsolidity/ast/ASTForward.h | 1 + libsolidity/ast/ASTJsonConverter.cpp | 10 ++- libsolidity/ast/ASTJsonConverter.h | 1 + libsolidity/ast/ASTPrinter.cpp | 12 +++ libsolidity/ast/ASTPrinter.h | 2 + libsolidity/ast/ASTVisitor.h | 4 + libsolidity/ast/AST_accept.h | 14 ++++ libsolidity/codegen/ContractCompiler.cpp | 9 ++ libsolidity/codegen/ContractCompiler.h | 1 + libsolidity/parsing/Parser.cpp | 36 +++++++- libsolidity/parsing/Parser.h | 1 + test/libsolidity/SolidityEndToEndTest.cpp | 97 ++++++++++++++++++++++ test/libsolidity/SolidityNameAndTypeResolution.cpp | 33 ++++++++ 17 files changed, 252 insertions(+), 3 deletions(-) diff --git a/docs/grammar.txt b/docs/grammar.txt index e700c946..a5c2acf3 100644 --- a/docs/grammar.txt +++ b/docs/grammar.txt @@ -63,7 +63,7 @@ StateMutability = 'pure' | 'constant' | 'view' | 'payable' Block = '{' Statement* '}' Statement = IfStatement | WhileStatement | ForStatement | Block | InlineAssemblyStatement | ( DoWhileStatement | PlaceholderStatement | Continue | Break | Return | - Throw | SimpleStatement ) ';' + Throw | EmitStatement | SimpleStatement ) ';' ExpressionStatement = Expression IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )? @@ -77,6 +77,7 @@ Continue = 'continue' Break = 'break' Return = 'return' Expression? Throw = 'throw' +EmitStatement = 'emit' FunctionCall VariableDefinition = ('var' IdentifierList | VariableDeclaration) ( '=' Expression )? IdentifierList = '(' ( Identifier? ',' )* Identifier? ')' diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 0ee16c89..d93ebbd1 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -955,6 +955,15 @@ void TypeChecker::endVisit(Return const& _return) } } +void TypeChecker::endVisit(EmitStatement const& _emit) +{ + if ( + _emit.eventCall().annotation().kind != FunctionCallKind::FunctionCall || + dynamic_cast(*type(_emit.eventCall().expression())).kind() != FunctionType::Kind::Event + ) + m_errorReporter.typeError(_emit.eventCall().expression().location(), "Expression has to be an event."); +} + bool TypeChecker::visit(VariableDeclarationStatement const& _statement) { if (!_statement.initialValue()) diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 344b019d..fc4ec6f0 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -94,6 +94,7 @@ private: virtual bool visit(WhileStatement const& _whileStatement) override; virtual bool visit(ForStatement const& _forStatement) override; virtual void endVisit(Return const& _return) override; + virtual void endVisit(EmitStatement const& _emit) override; virtual bool visit(VariableDeclarationStatement const& _variable) override; virtual void endVisit(ExpressionStatement const& _statement) override; virtual bool visit(Conditional const& _conditional) override; diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index feffde64..c0d55aec 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -1196,6 +1196,27 @@ public: virtual void accept(ASTConstVisitor& _visitor) const override; }; +/** + * The emit statement is used to emit events: emit EventName(arg1, ..., argn) + */ +class EmitStatement: public Statement +{ +public: + explicit EmitStatement( + SourceLocation const& _location, + ASTPointer const& _docString, + ASTPointer const& _functionCall + ): + Statement(_location, _docString), m_eventCall(_functionCall) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + FunctionCall const& eventCall() const { return *m_eventCall; } + +private: + ASTPointer m_eventCall; +}; + /** * Definition of a variable as a statement inside a function. It requires a type name (which can * also be "var") but the actual assignment can be missing. diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h index 46675e51..992fe4cd 100644 --- a/libsolidity/ast/ASTForward.h +++ b/libsolidity/ast/ASTForward.h @@ -69,6 +69,7 @@ class Continue; class Break; class Return; class Throw; +class EmitStatement; class VariableDeclarationStatement; class ExpressionStatement; class Expression; diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index cd9f7eca..4fef67c3 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -540,7 +540,15 @@ bool ASTJsonConverter::visit(Return const& _node) bool ASTJsonConverter::visit(Throw const& _node) { - setJsonNode(_node, "Throw", {});; + setJsonNode(_node, "Throw", {}); + return false; +} + +bool ASTJsonConverter::visit(EmitStatement const& _node) +{ + setJsonNode(_node, "EmitStatement", { + make_pair("eventCall", toJson(_node.eventCall())) + }); return false; } diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index 9a886220..88b93699 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -91,6 +91,7 @@ public: bool visit(Break const& _node) override; bool visit(Return const& _node) override; bool visit(Throw const& _node) override; + bool visit(EmitStatement const& _node) override; bool visit(VariableDeclarationStatement const& _node) override; bool visit(ExpressionStatement const& _node) override; bool visit(Conditional const& _node) override; diff --git a/libsolidity/ast/ASTPrinter.cpp b/libsolidity/ast/ASTPrinter.cpp index 23c3cbe1..4273f225 100644 --- a/libsolidity/ast/ASTPrinter.cpp +++ b/libsolidity/ast/ASTPrinter.cpp @@ -258,6 +258,13 @@ bool ASTPrinter::visit(Throw const& _node) return goDeeper(); } +bool ASTPrinter::visit(EmitStatement const& _node) +{ + writeLine("EmitStatement"); + printSourcePart(_node); + return goDeeper(); +} + bool ASTPrinter::visit(VariableDeclarationStatement const& _node) { writeLine("VariableDeclarationStatement"); @@ -517,6 +524,11 @@ void ASTPrinter::endVisit(Throw const&) m_indentation--; } +void ASTPrinter::endVisit(EmitStatement const&) +{ + m_indentation--; +} + void ASTPrinter::endVisit(VariableDeclarationStatement const&) { m_indentation--; diff --git a/libsolidity/ast/ASTPrinter.h b/libsolidity/ast/ASTPrinter.h index 01e4f7fc..de3bf8a2 100644 --- a/libsolidity/ast/ASTPrinter.h +++ b/libsolidity/ast/ASTPrinter.h @@ -76,6 +76,7 @@ public: bool visit(Break const& _node) override; bool visit(Return const& _node) override; bool visit(Throw const& _node) override; + bool visit(EmitStatement const& _node) override; bool visit(VariableDeclarationStatement const& _node) override; bool visit(ExpressionStatement const& _node) override; bool visit(Conditional const& _node) override; @@ -120,6 +121,7 @@ public: void endVisit(Break const&) override; void endVisit(Return const&) override; void endVisit(Throw const&) override; + void endVisit(EmitStatement const&) override; void endVisit(VariableDeclarationStatement const&) override; void endVisit(ExpressionStatement const&) override; void endVisit(Conditional const&) override; diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h index b726d592..b1389f0f 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -73,6 +73,7 @@ public: virtual bool visit(Break& _node) { return visitNode(_node); } virtual bool visit(Return& _node) { return visitNode(_node); } virtual bool visit(Throw& _node) { return visitNode(_node); } + virtual bool visit(EmitStatement& _node) { return visitNode(_node); } virtual bool visit(VariableDeclarationStatement& _node) { return visitNode(_node); } virtual bool visit(ExpressionStatement& _node) { return visitNode(_node); } virtual bool visit(Conditional& _node) { return visitNode(_node); } @@ -118,6 +119,7 @@ public: virtual void endVisit(Break& _node) { endVisitNode(_node); } virtual void endVisit(Return& _node) { endVisitNode(_node); } virtual void endVisit(Throw& _node) { endVisitNode(_node); } + virtual void endVisit(EmitStatement& _node) { endVisitNode(_node); } virtual void endVisit(VariableDeclarationStatement& _node) { endVisitNode(_node); } virtual void endVisit(ExpressionStatement& _node) { endVisitNode(_node); } virtual void endVisit(Conditional& _node) { endVisitNode(_node); } @@ -175,6 +177,7 @@ public: virtual bool visit(Break const& _node) { return visitNode(_node); } virtual bool visit(Return const& _node) { return visitNode(_node); } virtual bool visit(Throw const& _node) { return visitNode(_node); } + virtual bool visit(EmitStatement const& _node) { return visitNode(_node); } virtual bool visit(VariableDeclarationStatement const& _node) { return visitNode(_node); } virtual bool visit(ExpressionStatement const& _node) { return visitNode(_node); } virtual bool visit(Conditional const& _node) { return visitNode(_node); } @@ -220,6 +223,7 @@ public: virtual void endVisit(Break const& _node) { endVisitNode(_node); } virtual void endVisit(Return const& _node) { endVisitNode(_node); } virtual void endVisit(Throw const& _node) { endVisitNode(_node); } + virtual void endVisit(EmitStatement const& _node) { endVisitNode(_node); } virtual void endVisit(VariableDeclarationStatement const& _node) { endVisitNode(_node); } virtual void endVisit(ExpressionStatement const& _node) { endVisitNode(_node); } virtual void endVisit(Conditional const& _node) { endVisitNode(_node); } diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index 904d9ff6..70ee997e 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -541,6 +541,20 @@ void Throw::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } +void EmitStatement::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + m_eventCall->accept(_visitor); + _visitor.endVisit(*this); +} + +void EmitStatement::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + m_eventCall->accept(_visitor); + _visitor.endVisit(*this); +} + void ExpressionStatement::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index f463db94..ebb718a5 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -903,6 +903,15 @@ bool ContractCompiler::visit(Throw const& _throw) return false; } +bool ContractCompiler::visit(EmitStatement const& _emit) +{ + CompilerContext::LocationSetter locationSetter(m_context, _emit); + StackHeightChecker checker(m_context); + compileExpression(_emit.eventCall()); + checker.check(); + return false; +} + bool ContractCompiler::visit(VariableDeclarationStatement const& _variableDeclarationStatement) { StackHeightChecker checker(m_context); diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index 1fd80d05..d698dc71 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -109,6 +109,7 @@ private: virtual bool visit(Break const& _breakStatement) override; virtual bool visit(Return const& _return) override; virtual bool visit(Throw const& _throw) override; + virtual bool visit(EmitStatement const& _emit) override; virtual bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override; virtual bool visit(ExpressionStatement const& _expressionStatement) override; virtual bool visit(PlaceholderStatement const&) override; diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index e306e21b..8c97f55f 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -897,7 +897,9 @@ ASTPointer Parser::parseStatement() case Token::Assembly: return parseInlineAssembly(docString); case Token::Identifier: - if (m_insideModifier && m_scanner->currentLiteral() == "_") + if (m_scanner->currentLiteral() == "emit") + statement = parseEmitStatement(docString); + else if (m_insideModifier && m_scanner->currentLiteral() == "_") { statement = ASTNodeFactory(*this).createNode(docString); m_scanner->next(); @@ -1015,6 +1017,38 @@ ASTPointer Parser::parseForStatement(ASTPointer const& ); } +ASTPointer Parser::parseEmitStatement(ASTPointer const& _docString) +{ + ASTNodeFactory nodeFactory(*this); + m_scanner->next(); + ASTNodeFactory eventCallNodeFactory(*this); + + if (m_scanner->currentToken() != Token::Identifier) + fatalParserError("Expected event name or path."); + + vector> path; + while (true) + { + path.push_back(parseIdentifier()); + if (m_scanner->currentToken() != Token::Period) + break; + m_scanner->next(); + }; + + auto eventName = expressionFromIndexAccessStructure(path, {}); + expectToken(Token::LParen); + + vector> arguments; + vector> names; + std::tie(arguments, names) = parseFunctionCallArguments(); + eventCallNodeFactory.markEndPosition(); + nodeFactory.markEndPosition(); + expectToken(Token::RParen); + auto eventCall = eventCallNodeFactory.createNode(eventName, arguments, names); + auto statement = nodeFactory.createNode(_docString, eventCall); + return statement; +} + ASTPointer Parser::parseSimpleStatement(ASTPointer const& _docString) { RecursionGuard recursionGuard(*this); diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index cfdfea7e..3f780af9 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -104,6 +104,7 @@ private: ASTPointer parseWhileStatement(ASTPointer const& _docString); ASTPointer parseDoWhileStatement(ASTPointer const& _docString); ASTPointer parseForStatement(ASTPointer const& _docString); + ASTPointer parseEmitStatement(ASTPointer const& docString); /// A "simple statement" can be a variable declaration statement or an expression statement. ASTPointer parseSimpleStatement(ASTPointer const& _docString); ASTPointer parseVariableDeclarationStatement( diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index d2528a67..3882e4ea 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2967,6 +2967,29 @@ BOOST_AUTO_TEST_CASE(event) } } +BOOST_AUTO_TEST_CASE(event_emit) +{ + char const* sourceCode = R"( + contract ClientReceipt { + event Deposit(address indexed _from, bytes32 indexed _id, uint _value); + function deposit(bytes32 _id) payable { + emit Deposit(msg.sender, _id, msg.value); + } + } + )"; + compileAndRun(sourceCode); + u256 value(18); + u256 id(0x1234); + callContractFunctionWithValue("deposit(bytes32)", value, id); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK_EQUAL(h256(m_logs[0].data), h256(u256(value))); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 3); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address,bytes32,uint256)"))); + BOOST_CHECK_EQUAL(m_logs[0].topics[1], h256(m_sender, h256::AlignRight)); + BOOST_CHECK_EQUAL(m_logs[0].topics[2], h256(id)); +} + BOOST_AUTO_TEST_CASE(event_no_arguments) { char const* sourceCode = R"( @@ -3009,6 +3032,28 @@ BOOST_AUTO_TEST_CASE(event_access_through_base_name) BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("x()"))); } +BOOST_AUTO_TEST_CASE(event_access_through_base_name_emit) +{ + char const* sourceCode = R"( + contract A { + event x(); + } + contract B is A { + function f() returns (uint) { + emit A.x(); + return 1; + } + } + )"; + compileAndRun(sourceCode); + callContractFunction("f()"); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data.empty()); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("x()"))); +} + BOOST_AUTO_TEST_CASE(events_with_same_name) { char const* sourceCode = R"( @@ -3107,6 +3152,58 @@ BOOST_AUTO_TEST_CASE(events_with_same_name_inherited) BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address,uint256)"))); } +BOOST_AUTO_TEST_CASE(events_with_same_name_inherited_emit) +{ + char const* sourceCode = R"( + contract A { + event Deposit(); + } + + contract B { + event Deposit(address _addr); + } + + contract ClientReceipt is A, B { + event Deposit(address _addr, uint _amount); + function deposit() returns (uint) { + emit Deposit(); + return 1; + } + function deposit(address _addr) returns (uint) { + emit Deposit(_addr); + return 1; + } + function deposit(address _addr, uint _amount) returns (uint) { + emit Deposit(_addr, _amount); + return 1; + } + } + )"; + u160 const c_loggedAddress = m_contractAddress; + + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("deposit()"), encodeArgs(u256(1))); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data.empty()); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit()"))); + + ABI_CHECK(callContractFunction("deposit(address)", c_loggedAddress), encodeArgs(u256(1))); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data == encodeArgs(c_loggedAddress)); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address)"))); + + ABI_CHECK(callContractFunction("deposit(address,uint256)", c_loggedAddress, u256(100)), encodeArgs(u256(1))); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data == encodeArgs(c_loggedAddress, 100)); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address,uint256)"))); +} + BOOST_AUTO_TEST_CASE(event_anonymous) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 7c03d7cb..ce80ffda 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7785,6 +7785,39 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) CHECK_ERROR(text, TypeError, "Member \"delegatecall\" not found or not visible after argument-dependent lookup in contract"); } +BOOST_AUTO_TEST_CASE(emit_events) +{ + char const* text = R"( + contract C { + event e(); + function f() public { + emit e(); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + event e(uint a, string b); + function f() public { + emit e(2, "abc"); + emit e({b: "abc", a: 8}); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract A { event e(uint a, string b); } + contract C is A { + function f() public { + emit A.e(2, "abc"); + emit A.e({b: "abc", a: 8}); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + BOOST_AUTO_TEST_SUITE_END() } -- cgit v1.2.3 From 04c922e5ed038fd5f6d43a364e11b8c459898a93 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Feb 2018 16:57:02 +0100 Subject: Changelog entry. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index f0d00416..3af3a99d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Features: * Code Generator: Assert that ``k != 0`` for ``molmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature. * Standard JSON: Reject badly formatted invalid JSON inputs. * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. + * Support and recommend using ``emit EventName();`` to call events explicitly. * Syntax Analyser: Do not warn about experimental features if they do not concern code generation. Bugfixes: -- cgit v1.2.3 From f58024b9744f557dbc77d5f7bfbc4319bde2e0c7 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Feb 2018 17:32:30 +0100 Subject: Documentation about emitting events. --- docs/contracts.rst | 10 ++++++---- docs/introduction-to-smart-contracts.rst | 10 +++++----- docs/solidity-by-example.rst | 18 +++++++++--------- docs/structure-of-a-contract.rst | 4 ++-- docs/types.rst | 4 ++-- std/StandardToken.sol | 4 ++-- 6 files changed, 26 insertions(+), 24 deletions(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 368b7819..ee203263 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -724,10 +724,12 @@ All non-indexed arguments will be stored in the data part of the log. ); function deposit(bytes32 _id) public payable { - // Any call to this function (even deeply nested) can - // be detected from the JavaScript API by filtering - // for `Deposit` to be called. - Deposit(msg.sender, _id, msg.value); + // Events are emitted using `emit`, followed by + // the name of the event and the arguments + // (if any) in parentheses. Any such invocation + // (even deeply nested) can be detected from + // the JavaScript API by filtering for `Deposit`. + emit Deposit(msg.sender, _id, msg.value); } } diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index c297a8ad..11e07292 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -80,7 +80,7 @@ registering with username and password - all you need is an Ethereum keypair. :: - pragma solidity ^0.4.0; + pragma solidity ^0.4.20; // should actually be 0.4.21 contract Coin { // The keyword "public" makes those variables @@ -107,7 +107,7 @@ registering with username and password - all you need is an Ethereum keypair. if (balances[msg.sender] < amount) return; balances[msg.sender] -= amount; balances[receiver] += amount; - Sent(msg.sender, receiver, amount); + emit Sent(msg.sender, receiver, amount); } } @@ -157,10 +157,10 @@ single account. .. index:: event The line ``event Sent(address from, address to, uint amount);`` declares -a so-called "event" which is fired in the last line of the function +a so-called "event" which is emitted in the last line of the function ``send``. User interfaces (as well as server applications of course) can -listen for those events being fired on the blockchain without much -cost. As soon as it is fired, the listener will also receive the +listen for those events being emitted on the blockchain without much +cost. As soon as it is emitted, the listener will also receive the arguments ``from``, ``to`` and ``amount``, which makes it easy to track transactions. In order to listen for this event, you would use :: diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index b8e158ac..e5b44c98 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -214,7 +214,7 @@ activate themselves. :: - pragma solidity ^0.4.11; + pragma solidity ^0.4.20; // should actually be 0.4.21 contract SimpleAuction { // Parameters of the auction. Times are either @@ -282,7 +282,7 @@ activate themselves. } highestBidder = msg.sender; highestBid = msg.value; - HighestBidIncreased(msg.sender, msg.value); + emit HighestBidIncreased(msg.sender, msg.value); } /// Withdraw a bid that was overbid. @@ -325,7 +325,7 @@ activate themselves. // 2. Effects ended = true; - AuctionEnded(highestBidder, highestBid); + emit AuctionEnded(highestBidder, highestBid); // 3. Interaction beneficiary.transfer(highestBid); @@ -371,7 +371,7 @@ high or low invalid bids. :: - pragma solidity ^0.4.11; + pragma solidity ^0.4.20; // should actually be 0.4.21 contract BlindAuction { struct Bid { @@ -509,7 +509,7 @@ high or low invalid bids. onlyAfter(revealEnd) { require(!ended); - AuctionEnded(highestBidder, highestBid); + emit AuctionEnded(highestBidder, highestBid); ended = true; beneficiary.transfer(highestBid); } @@ -524,7 +524,7 @@ Safe Remote Purchase :: - pragma solidity ^0.4.11; + pragma solidity ^0.4.20; // should actually be 0.4.21 contract Purchase { uint public value; @@ -574,7 +574,7 @@ Safe Remote Purchase onlySeller inState(State.Created) { - Aborted(); + emit Aborted(); state = State.Inactive; seller.transfer(this.balance); } @@ -589,7 +589,7 @@ Safe Remote Purchase condition(msg.value == (2 * value)) payable { - PurchaseConfirmed(); + emit PurchaseConfirmed(); buyer = msg.sender; state = State.Locked; } @@ -601,7 +601,7 @@ Safe Remote Purchase onlyBuyer inState(State.Locked) { - ItemReceived(); + emit ItemReceived(); // It is important to change the state first because // otherwise, the contracts called using `send` below // can call in again here. diff --git a/docs/structure-of-a-contract.rst b/docs/structure-of-a-contract.rst index a9a7ed52..4a0873df 100644 --- a/docs/structure-of-a-contract.rst +++ b/docs/structure-of-a-contract.rst @@ -86,14 +86,14 @@ Events are convenience interfaces with the EVM logging facilities. :: - pragma solidity ^0.4.0; + pragma solidity ^0.4.20; // should actually be 0.4.21 contract SimpleAuction { event HighestBidIncreased(address bidder, uint amount); // Event function bid() public payable { // ... - HighestBidIncreased(msg.sender, msg.value); // Triggering event + emit HighestBidIncreased(msg.sender, msg.value); // Triggering event } } diff --git a/docs/types.rst b/docs/types.rst index 55eaa69a..3611bc3e 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -470,7 +470,7 @@ Example that shows how to use internal function types:: Another example that uses external function types:: - pragma solidity ^0.4.11; + pragma solidity ^0.4.20; // should actually be 0.4.21 contract Oracle { struct Request { @@ -481,7 +481,7 @@ Another example that uses external function types:: event NewRequest(uint); function query(bytes data, function(bytes memory) external callback) public { requests.push(Request(data, callback)); - NewRequest(requests.length - 1); + emit NewRequest(requests.length - 1); } function reply(uint requestID, bytes response) public { // Here goes the check that the reply comes from a trusted source diff --git a/std/StandardToken.sol b/std/StandardToken.sol index 2986cb56..1b218d67 100644 --- a/std/StandardToken.sol +++ b/std/StandardToken.sol @@ -40,7 +40,7 @@ contract StandardToken is Token { if (balance[_from] >= _value && balance[_to] + _value >= balance[_to]) { balance[_from] -= _value; balance[_to] += _value; - Transfer(_from, _to, _value); + emit Transfer(_from, _to, _value); return true; } else { return false; @@ -49,7 +49,7 @@ contract StandardToken is Token { function approve(address _spender, uint256 _value) public returns (bool success) { m_allowance[msg.sender][_spender] = _value; - Approval(msg.sender, _spender, _value); + emit Approval(msg.sender, _spender, _value); return true; } -- cgit v1.2.3 From 8c1a8ecc2e6518dcfee73e6cd9ee8dd9a3eaaea0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Feb 2018 17:32:41 +0100 Subject: Warn about using events without emit. --- libsolidity/analysis/TypeChecker.cpp | 10 +++++++++- libsolidity/analysis/TypeChecker.h | 4 ++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index d93ebbd1..2914472a 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -961,7 +961,8 @@ void TypeChecker::endVisit(EmitStatement const& _emit) _emit.eventCall().annotation().kind != FunctionCallKind::FunctionCall || dynamic_cast(*type(_emit.eventCall().expression())).kind() != FunctionType::Kind::Event ) - m_errorReporter.typeError(_emit.eventCall().expression().location(), "Expression has to be an event."); + m_errorReporter.typeError(_emit.eventCall().expression().location(), "Expression has to be an event invocation."); + m_insideEmitStatement = false; } bool TypeChecker::visit(VariableDeclarationStatement const& _statement) @@ -1540,6 +1541,13 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) else if (functionName->name() == "suicide" && functionType->kind() == FunctionType::Kind::Selfdestruct) m_errorReporter.warning(_functionCall.location(), "\"suicide\" has been deprecated in favour of \"selfdestruct\""); } + if (!m_insideEmitStatement && functionType->kind() == FunctionType::Kind::Event) + { + if (m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + m_errorReporter.typeError(_functionCall.location(), "Event invocations have to be prefixed by \"emit\"."); + else + m_errorReporter.warning(_functionCall.location(), "Invoking events without \"emit\" prefix is deprecated."); + } TypePointers parameterTypes = functionType->parameterTypes(); diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index fc4ec6f0..16796b63 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -94,6 +94,7 @@ private: virtual bool visit(WhileStatement const& _whileStatement) override; virtual bool visit(ForStatement const& _forStatement) override; virtual void endVisit(Return const& _return) override; + virtual bool visit(EmitStatement const&) override { m_insideEmitStatement = true; return true; } virtual void endVisit(EmitStatement const& _emit) override; virtual bool visit(VariableDeclarationStatement const& _variable) override; virtual void endVisit(ExpressionStatement const& _statement) override; @@ -131,6 +132,9 @@ private: ContractDefinition const* m_scope = nullptr; + /// Flag indicating whether we are currently inside an EmitStatement. + bool m_insideEmitStatement = false; + ErrorReporter& m_errorReporter; }; -- cgit v1.2.3 From ad010f557bc4af683688aef423e7ae68d0fd4d08 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Feb 2018 17:32:54 +0100 Subject: Tests for warnings. --- test/libsolidity/SolidityNameAndTypeResolution.cpp | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index ce80ffda..27761066 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7818,6 +7818,29 @@ BOOST_AUTO_TEST_CASE(emit_events) CHECK_SUCCESS_NO_WARNINGS(text); } +BOOST_AUTO_TEST_CASE(old_style_events_050) +{ + char const* text = R"( + contract C { + event e(); + function f() public { + e(); + } + } + )"; + CHECK_WARNING(text, "without \"emit\" prefix"); + text = R"( + pragma experimental "v0.5.0"; + contract C { + event e(); + function f() public { + e(); + } + } + )"; + CHECK_ERROR(text, TypeError, "have to be prefixed"); +} + BOOST_AUTO_TEST_SUITE_END() } -- cgit v1.2.3 From b5a3b6a4293537c843e8387ffd0b03253160dc0a Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 22 Feb 2018 15:06:14 +0100 Subject: Parser test. --- test/libsolidity/SolidityParser.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 861e6408..b7097d0f 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -1708,6 +1708,19 @@ BOOST_AUTO_TEST_CASE(newInvalidTypeName) CHECK_PARSE_ERROR(text, "Expected explicit type name"); } +BOOST_AUTO_TEST_CASE(emitWithoutEvent) +{ + char const* text = R"( + contract C { + event A(); + function f() { + emit A; + } + } + )"; + CHECK_PARSE_ERROR(text, "Expected token LParen got 'Semicolon'"); +} + BOOST_AUTO_TEST_SUITE_END() } -- cgit v1.2.3 From 2ada9683dd54a10f61816814e79f15860f8feb2e Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 22 Feb 2018 15:24:14 +0100 Subject: Use the term "trailing zero-bytes". --- docs/abi-spec.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 58b88a07..07c8e0ce 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -163,7 +163,7 @@ on the type of ``X`` being - ``fixed``: as in the ``fixed128x19`` case - ``ufixedx``: ``enc(X)`` is ``enc(X * 10**N)`` where ``X * 10**N`` is interpreted as a ``uint256``. - ``ufixed``: as in the ``ufixed128x19`` case -- ``bytes``: ``enc(X)`` is the sequence of bytes in ``X`` padded with zero-bytes on the _right_ to a length of 32. +- ``bytes``: ``enc(X)`` is the sequence of bytes in ``X`` padded with trailing zero-bytes to a length of 32 bytes. Note that for any ``X``, ``len(enc(X))`` is a multiple of 32. -- cgit v1.2.3 From b1acb7d226cd2819a47809e50b9591d5e0069041 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 22 Feb 2018 15:35:44 +0100 Subject: Try external tests twice. --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index bc72623c..eb772d9b 100644 --- a/circle.yml +++ b/circle.yml @@ -83,7 +83,7 @@ jobs: name: External tests command: | . /usr/local/nvm/nvm.sh - test/externalTests.sh /tmp/workspace/soljson.js + test/externalTests.sh /tmp/workspace/soljson.js || test/externalTests.sh /tmp/workspace/soljson.js build_x86: docker: - image: buildpack-deps:artful -- cgit v1.2.3 From 187e50b14c063cdfc56e085d308f2cb8dacc13bd Mon Sep 17 00:00:00 2001 From: DYLAN BECKWITH Date: Wed, 14 Feb 2018 13:09:52 -0800 Subject: Recommend consistent style for wrapping long lines. --- docs/style-guide.rst | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 4c0d44f0..7d513d7a 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -112,6 +112,109 @@ No:: } } +Maximum Line Length +=================== + +Keeping lines under the `PEP 8 recommendation `_ of 79 (or 99) +characters helps readers easily parse the code. + +Wrapped lines should conform to the following guidelines. + +1. The first argument should not be attached to the opening parenthesis. +2. One, and only one, indent should be used. +3. Each argument should fall on its own line. +4. The terminating element, :code:`);`, should be placed on the final line by itself. + +Function Calls + +Yes:: + + thisFunctionCallIsReallyLong( + longArgument1, + longArgument2, + longArgument3 + ); + +No:: + + thisFunctionCallIsReallyLong(longArgument1, + longArgument2, + longArgument3 + ); + + thisFunctionCallIsReallyLong(longArgument1, + longArgument2, + longArgument3 + ); + + thisFunctionCallIsReallyLong( + longArgument1, longArgument2, + longArgument3 + ); + + thisFunctionCallIsReallyLong( + longArgument1, + longArgument2, + longArgument3 + ); + + thisFunctionCallIsReallyLong( + longArgument1, + longArgument2, + longArgument3); + +Assignment Statements + +Yes:: + + thisIsALongNestedMapping[being][set][to_some_value] = someFunction( + argument1, + argument2, + argument3, + argument4 + ); + +No:: + + thisIsALongNestedMapping[being][set][to_some_value] = someFunction(argument1, + argument2, + argument3, + argument4); + +Event Definitions and Event Emitters + +Yes:: + + event LongAndLotsOfArgs( + adress sender, + adress recipient, + uint256 publicKey, + uint256 amount, + bytes32[] options + ); + + LongAndLotsOfArgs( + sender, + recipient, + publicKey, + amount, + options + ); + +No:: + + event LongAndLotsOfArgs(adress sender, + adress recipient, + uint256 publicKey, + uint256 amount, + bytes32[] options); + + LongAndLotsOfArgs(sender, + recipient, + publicKey, + amount, + options); + Source File Encoding ==================== -- cgit v1.2.3 From 2ac49b3c2d10eb43deb716cdf75ae48cd64840f8 Mon Sep 17 00:00:00 2001 From: Dax Bondye Date: Thu, 22 Feb 2018 10:18:09 -0800 Subject: Reccomend to explicitly label the visibility of functions. --- docs/style-guide.rst | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 7d513d7a..9249f3e1 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -494,7 +494,7 @@ function body to be kept on the same line as the function declaration. The closing brace should be at the same indentation level as the function declaration. -The opening brace should be preceeded by a single space. +The opening brace should be preceded by a single space. Yes:: @@ -524,7 +524,21 @@ No:: function increment(uint x) public pure returns (uint) { return x + 1;} -The visibility modifiers for a function should come before any custom +You should explicitly label the visibility of all functions, including constructors. + +Yes:: + + function explicitlyPublic(uint val) public { + doSomething(); + } + +No:: + + function implicitlyPublic(uint val) { + doSomething(); + } + +The visibility modifier for a function should come before any custom modifiers. Yes:: -- cgit v1.2.3 From fc3473b282a0b0009c3d32660b75a3520f89b73c Mon Sep 17 00:00:00 2001 From: Hongbin Zuo Date: Fri, 23 Feb 2018 15:06:14 +0800 Subject: Why you made this change: Talked with Chris and Matt, in order to develop stronger ethereum community in China, we decided to initiate the activity to translate Solidity documentation into Simplified Chinese version. We have invited over 10 experienced translators as a team to contribute on this effort. What you did: I finished translation of index.rst into Simplified Chinese and wanted it to be added into main doc index. --- docs/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.rst b/docs/index.rst index 3df0af3c..6930afb8 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -34,6 +34,7 @@ Translations This documentation is translated into several languages by community volunteers, but the English version stands as a reference. +* `Simplified Chinese `_ * `Spanish `_ * `Russian `_ (rather outdated) -- cgit v1.2.3 From 4d1b969543c51be1188d792398e028fcbc3d85cf Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 24 Oct 2017 10:49:41 +0100 Subject: Use readStandardInput in the fuzzer --- test/fuzzer.cpp | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/test/fuzzer.cpp b/test/fuzzer.cpp index c61410b6..45738baa 100644 --- a/test/fuzzer.cpp +++ b/test/fuzzer.cpp @@ -18,6 +18,7 @@ * Executable for use with AFL . */ +#include #include #include #include @@ -82,23 +83,12 @@ void testConstantOptimizer() } } -string readInput() -{ - string input; - while (!cin.eof()) - { - string s; - getline(cin, s); - input += s + '\n'; - } - return input; -} - void testStandardCompiler() { if (!quiet) cout << "Testing compiler via JSON interface." << endl; - string input = readInput(); + string input = readStandardInput(); + string outputString(compileStandard(input.c_str(), NULL)); Json::Value output; if (!jsonParseStrict(outputString, output)) @@ -125,7 +115,7 @@ void testCompiler(bool optimize) { if (!quiet) cout << "Testing compiler " << (optimize ? "with" : "without") << " optimizer." << endl; - string input = readInput(); + string input = readStandardInput(); string outputString(compileJSON(input.c_str(), optimize)); Json::Value outputJson; -- cgit v1.2.3 From 02307235c5cacc19fd7b2bc02870eea5fae55a52 Mon Sep 17 00:00:00 2001 From: Hongbin Zuo Date: Fri, 23 Feb 2018 16:44:05 +0800 Subject: add in progress --- docs/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index 6930afb8..184d0e69 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -34,7 +34,7 @@ Translations This documentation is translated into several languages by community volunteers, but the English version stands as a reference. -* `Simplified Chinese `_ +* `Simplified Chinese `_ (in progress) * `Spanish `_ * `Russian `_ (rather outdated) -- cgit v1.2.3 From b80baa80021b993ba21f3afe041d5cd0fc9b015b Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 13 Feb 2018 11:54:22 +0100 Subject: Run EndToEnd tests on circle, too. --- circle.yml | 12 +++--------- scripts/tests.sh | 30 +++++++++++++++++++++++++----- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/circle.yml b/circle.yml index eb772d9b..94c711b0 100644 --- a/circle.yml +++ b/circle.yml @@ -93,7 +93,7 @@ jobs: name: Install build dependencies command: | apt-get -qq update - apt-get -qy install ccache cmake libboost-all-dev libz3-dev + apt-get -qy install ccache cmake libboost-all-dev libz3-dev libleveldb1v5 - run: name: Init submodules command: | @@ -114,16 +114,10 @@ jobs: key: ccache-{{ arch }}-{{ .Branch }} paths: - ~/.ccache - - run: - name: Commandline tests - command: test/cmdlineTests.sh - run: mkdir -p test_results - run: - name: Test without optimizer (exclude IPC tests) - command: build/test/soltest --logger=JUNIT,test_suite,test_results/no_opt.xml -- --no-ipc - - run: - name: Test with optimizer (exclude IPC tests) - command: build/test/soltest --logger=JUNIT,test_suite,test_results/opt.xml -- --optimize --no-ipc + name: Tests + command: scripts/tests.sh --junit_report test_results - store_test_results: path: test_results/ - store_artifacts: diff --git a/scripts/tests.sh b/scripts/tests.sh index 2b47c254..d414643b 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -30,6 +30,20 @@ set -e REPO_ROOT="$(dirname "$0")"/.. +if [ "$1" = --junit_report ] +then + if [ -z "$2" ] + then + echo "Usage: $0 [--junit_report ]" + exit 1 + fi + testargs_no_opt="--logger=JUNIT,test_suite,$2/no_opt.xml" + testargs_opt="--logger=JUNIT,test_suite,$2/opt.xml" +else + testargs_no_opt='' + testargs_opt='' +fi + echo "Running commandline tests..." "$REPO_ROOT/test/cmdlineTests.sh" @@ -42,9 +56,15 @@ elif [ -z $CI ]; then ETH_PATH="eth" else mkdir -p /tmp/test - # Update hash below if binary is changed. - wget -q -O /tmp/test/eth https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/eth_byzantium2 - test "$(shasum /tmp/test/eth)" = "4dc3f208475f622be7c8e53bee720e14cd254c6f /tmp/test/eth" + ETH_BINARY=eth_byzantium_artful + ETH_HASH="e527dd3e3dc17b983529dd7dcfb74a0d3a5aed4e" + if grep -i trusty /etc/lsb-release >/dev/null 2>&1 + then + ETH_BINARY=eth_byzantium2 + ETH_HASH="4dc3f208475f622be7c8e53bee720e14cd254c6f" + fi + wget -q -O /tmp/test/eth https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/$ETH_BINARY + test "$(shasum /tmp/test/eth)" = "$ETH_HASH /tmp/test/eth" sync chmod +x /tmp/test/eth sync # Otherwise we might get a "text file busy" error @@ -68,9 +88,9 @@ sleep 2 # And then run the Solidity unit-tests (once without optimization, once with), # pointing to that IPC endpoint. echo "--> Running tests without optimizer..." - "$REPO_ROOT"/build/test/soltest --show-progress -- --ipcpath /tmp/test/geth.ipc && \ + "$REPO_ROOT"/build/test/soltest --show-progress $testargs_no_opt -- --ipcpath /tmp/test/geth.ipc && \ echo "--> Running tests WITH optimizer..." && \ - "$REPO_ROOT"/build/test/soltest --show-progress -- --optimize --ipcpath /tmp/test/geth.ipc + "$REPO_ROOT"/build/test/soltest --show-progress $testargs_opt -- --optimize --ipcpath /tmp/test/geth.ipc ERROR_CODE=$? pkill "$ETH_PID" || true sleep 4 -- cgit v1.2.3 From ef7fbf8d0c7310142c39fb7a940e0732e245754a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aaron=20Cola=C3=A7o?= Date: Fri, 23 Feb 2018 23:17:48 +0530 Subject: Fix example; closes #3582 --- docs/frequently-asked-questions.rst | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index a6bead29..3c83cbc7 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -40,9 +40,9 @@ Is there a decompiler available? ================================ There is no exact decompiler to Solidity, but -`Porosity `_ is close. -Because some information like variable names, comments, and -source code formatting is lost in the compilation process, +`Porosity `_ is close. +Because some information like variable names, comments, and +source code formatting is lost in the compilation process, it is not possible to completely recover the original source code. Bytecode can be disassembled to opcodes, a service that is provided by @@ -542,12 +542,27 @@ contract level) with ``arrayname.length = ;``. If you get the :: - int8[] memory memArr; // Case 1 - memArr.length++; // illegal - int8[5] storageArr; // Case 2 - somearray.length++; // legal - int8[5] storage storageArr2; // Explicit case 2 - somearray2.length++; // legal + // This will not compile + + pragma solidity ^0.4.18; + + contract C { + int8[] dynamicStorageArray; + int8[5] fixedStorageArray; + + function f() { + int8[] memory memArr; // Case 1 + memArr.length++; // illegal + + int8[5] storage storageArr = fixedStorageArray; // Case 2 + storageArr.length++; // illegal + + int8[] storage storageArr2 = dynamicStorageArray; + storageArr2.length++; // legal + + + } + } **Important note:** In Solidity, array dimensions are declared backwards from the way you might be used to declaring them in C or Java, but they are access as in -- cgit v1.2.3 From 917dd28eef06dd62498073fc24f8a7beb6d9c553 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 20 Feb 2018 19:38:51 +0100 Subject: Simplify FunctionType constructors. --- libsolidity/ast/Types.cpp | 56 ++++++++++++----------------------------------- 1 file changed, 14 insertions(+), 42 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index e4b7e4fd..a7d3e256 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2153,32 +2153,19 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal m_stateMutability(_function.stateMutability()), m_declaration(&_function) { - TypePointers params; - vector paramNames; - TypePointers retParams; - vector retParamNames; - if (_isInternal && m_stateMutability == StateMutability::Payable) m_stateMutability = StateMutability::NonPayable; - params.reserve(_function.parameters().size()); - paramNames.reserve(_function.parameters().size()); for (ASTPointer const& var: _function.parameters()) { - paramNames.push_back(var->name()); - params.push_back(var->annotation().type); + m_parameterNames.push_back(var->name()); + m_parameterTypes.push_back(var->annotation().type); } - retParams.reserve(_function.returnParameters().size()); - retParamNames.reserve(_function.returnParameters().size()); for (ASTPointer const& var: _function.returnParameters()) { - retParamNames.push_back(var->name()); - retParams.push_back(var->annotation().type); + m_returnParameterNames.push_back(var->name()); + m_returnParameterTypes.push_back(var->annotation().type); } - swap(params, m_parameterTypes); - swap(paramNames, m_parameterNames); - swap(retParams, m_returnParameterTypes); - swap(retParamNames, m_returnParameterNames); } FunctionType::FunctionType(VariableDeclaration const& _varDecl): @@ -2186,16 +2173,14 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): m_stateMutability(StateMutability::View), m_declaration(&_varDecl) { - TypePointers paramTypes; - vector paramNames; auto returnType = _varDecl.annotation().type; while (true) { if (auto mappingType = dynamic_cast(returnType.get())) { - paramTypes.push_back(mappingType->keyType()); - paramNames.push_back(""); + m_parameterTypes.push_back(mappingType->keyType()); + m_parameterNames.push_back(""); returnType = mappingType->valueType(); } else if (auto arrayType = dynamic_cast(returnType.get())) @@ -2204,15 +2189,13 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): // Return byte arrays as as whole. break; returnType = arrayType->baseType(); - paramNames.push_back(""); - paramTypes.push_back(make_shared(256)); + m_parameterNames.push_back(""); + m_parameterTypes.push_back(make_shared(256)); } else break; } - TypePointers retParams; - vector retParamNames; if (auto structType = dynamic_cast(returnType.get())) { for (auto const& member: structType->members(nullptr)) @@ -2223,24 +2206,19 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): if (auto arrayType = dynamic_cast(member.type.get())) if (!arrayType->isByteArray()) continue; - retParams.push_back(member.type); - retParamNames.push_back(member.name); + m_returnParameterTypes.push_back(member.type); + m_returnParameterNames.push_back(member.name); } } } else { - retParams.push_back(ReferenceType::copyForLocationIfReference( + m_returnParameterTypes.push_back(ReferenceType::copyForLocationIfReference( DataLocation::Memory, returnType )); - retParamNames.push_back(""); + m_returnParameterNames.push_back(""); } - - swap(paramTypes, m_parameterTypes); - swap(paramNames, m_parameterNames); - swap(retParams, m_returnParameterTypes); - swap(retParamNames, m_returnParameterNames); } FunctionType::FunctionType(EventDefinition const& _event): @@ -2248,17 +2226,11 @@ FunctionType::FunctionType(EventDefinition const& _event): m_stateMutability(StateMutability::NonPayable), m_declaration(&_event) { - TypePointers params; - vector paramNames; - params.reserve(_event.parameters().size()); - paramNames.reserve(_event.parameters().size()); for (ASTPointer const& var: _event.parameters()) { - paramNames.push_back(var->name()); - params.push_back(var->annotation().type); + m_parameterNames.push_back(var->name()); + m_parameterTypes.push_back(var->annotation().type); } - swap(params, m_parameterTypes); - swap(paramNames, m_parameterNames); } FunctionType::FunctionType(FunctionTypeName const& _typeName): -- cgit v1.2.3 From 8ae6a76c58c7c35f429379fb67ccdfb457fc8e91 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 26 Feb 2018 11:13:21 +0100 Subject: Test data location of getter return type. --- test/libsolidity/SolidityNameAndTypeResolution.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 27761066..93abee0d 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7841,6 +7841,26 @@ BOOST_AUTO_TEST_CASE(old_style_events_050) CHECK_ERROR(text, TypeError, "have to be prefixed"); } +BOOST_AUTO_TEST_CASE(getter_is_memory_type) +{ + char const* text = R"( + contract C { + struct S { string m; } + string[] public x; + S[] public y; + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + // Check that the getters return a memory strings, not a storage strings. + ContractDefinition const& c = dynamic_cast(*m_compiler.ast("").nodes().at(1)); + BOOST_CHECK(c.interfaceFunctions().size() == 2); + for (auto const& f: c.interfaceFunctions()) + { + auto const& retType = f.second->returnParameterTypes().at(0); + BOOST_CHECK(retType->dataStoredIn(DataLocation::Memory)); + } +} + BOOST_AUTO_TEST_SUITE_END() } -- cgit v1.2.3 From 4da20bdf012d948a45cc99d137cf18ead65c0a30 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 21 Feb 2018 11:07:40 +0100 Subject: Fix: Function types for getters should not have storage pointers. --- libsolidity/ast/Types.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index a7d3e256..41f95f30 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2206,7 +2206,10 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): if (auto arrayType = dynamic_cast(member.type.get())) if (!arrayType->isByteArray()) continue; - m_returnParameterTypes.push_back(member.type); + m_returnParameterTypes.push_back(ReferenceType::copyForLocationIfReference( + DataLocation::Memory, + member.type + )); m_returnParameterNames.push_back(member.name); } } -- cgit v1.2.3 From 751705978e346b372e9aa1242c78e32892fae985 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 23 Feb 2018 18:27:27 +0100 Subject: Add helpers escapeIdentifier to Types --- libsolidity/ast/Types.cpp | 20 ++++++++++++++++++++ libsolidity/ast/Types.h | 8 +++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index e4b7e4fd..bf5745a6 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -170,6 +170,26 @@ string parenthesizeUserIdentifier(string const& _internal) } +string Type::escapeIdentifier(string const& _identifier) +{ + string ret = _identifier; + boost::algorithm::replace_all(ret, "$", "_$$$_"); + boost::algorithm::replace_all(ret, ",", "_$_"); + boost::algorithm::replace_all(ret, "(", "$_"); + boost::algorithm::replace_all(ret, ")", "_$"); + return ret; +} + +string Type::unescapeIdentifier(string const& _identifier) +{ + string ret = _identifier; + boost::algorithm::replace_all(ret, "_$_", ","); + boost::algorithm::replace_all(ret, "_$$$_", "$"); + boost::algorithm::replace_all(ret, "$_", "("); + boost::algorithm::replace_all(ret, "_$", ")"); + return ret; +} + TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type) { solAssert(Token::isElementaryTypeName(_type.token()), diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 2e7d05ba..ef898f37 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -163,10 +163,16 @@ public: /// @returns a valid solidity identifier such that two types should compare equal if and /// only if they have the same identifier. /// The identifier should start with "t_". + virtual std::string identifier() const = 0; + /// More complex identifier strings use "parentheses", where $_ is interpreted as as /// "opening parenthesis", _$ as "closing parenthesis", _$_ as "comma" and any $ that /// appears as part of a user-supplied identifier is escaped as _$$$_. - virtual std::string identifier() const = 0; + /// @returns an escaped identifier (will not contain any parenthesis or commas) + static std::string escapeIdentifier(std::string const& _identifier); + /// @returns an unescaped identifier + static std::string unescapeIdentifier(std::string const& _identifier); + virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; } virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const { -- cgit v1.2.3 From 2e7067fbe421872e0e7d0a4a4373199e56a9b7d0 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 23 Feb 2018 18:32:46 +0100 Subject: Rename Types::identifier to Types::richIdentifier --- libsolidity/ast/Types.cpp | 32 ++++++++++++++++---------------- libsolidity/ast/Types.h | 44 +++++++++++++++++++++++++------------------- 2 files changed, 41 insertions(+), 35 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index bf5745a6..97e7a922 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -354,7 +354,7 @@ IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier): ); } -string IntegerType::identifier() const +string IntegerType::richIdentifier() const { if (isAddress()) return "t_address"; @@ -524,7 +524,7 @@ FixedPointType::FixedPointType(int _totalBits, int _fractionalDigits, FixedPoint ); } -string FixedPointType::identifier() const +string FixedPointType::richIdentifier() const { return "t_" + string(isSigned() ? "" : "u") + "fixed" + std::to_string(m_totalBits) + "x" + std::to_string(m_fractionalDigits); } @@ -956,7 +956,7 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ } } -string RationalNumberType::identifier() const +string RationalNumberType::richIdentifier() const { return "t_rational_" + m_value.numerator().str() + "_by_" + m_value.denominator().str(); } @@ -1097,7 +1097,7 @@ bool StringLiteralType::isImplicitlyConvertibleTo(Type const& _convertTo) const return false; } -string StringLiteralType::identifier() const +string StringLiteralType::richIdentifier() const { // Since we have to return a valid identifier and the string itself may contain // anything, we hash it. @@ -1197,7 +1197,7 @@ MemberList::MemberMap FixedBytesType::nativeMembers(const ContractDefinition*) c return MemberList::MemberMap{MemberList::Member{"length", make_shared(8)}}; } -string FixedBytesType::identifier() const +string FixedBytesType::richIdentifier() const { return "t_bytes" + std::to_string(m_bytes); } @@ -1390,7 +1390,7 @@ bool ArrayType::isExplicitlyConvertibleTo(const Type& _convertTo) const return true; } -string ArrayType::identifier() const +string ArrayType::richIdentifier() const { string id; if (isString()) @@ -1624,7 +1624,7 @@ TypePointer ArrayType::copyForLocation(DataLocation _location, bool _isPointer) return copy; } -string ContractType::identifier() const +string ContractType::richIdentifier() const { return (m_super ? "t_super" : "t_contract") + parenthesizeUserIdentifier(m_contract.name()) + std::to_string(m_contract.id()); } @@ -1776,7 +1776,7 @@ bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const return this->m_struct == convertTo.m_struct; } -string StructType::identifier() const +string StructType::richIdentifier() const { return "t_struct" + parenthesizeUserIdentifier(m_struct.name()) + std::to_string(m_struct.id()) + identifierLocationSuffix(); } @@ -2008,7 +2008,7 @@ TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const return _operator == Token::Delete ? make_shared() : TypePointer(); } -string EnumType::identifier() const +string EnumType::richIdentifier() const { return "t_enum" + parenthesizeUserIdentifier(m_enum.name()) + std::to_string(m_enum.id()); } @@ -2094,7 +2094,7 @@ bool TupleType::isImplicitlyConvertibleTo(Type const& _other) const return false; } -string TupleType::identifier() const +string TupleType::richIdentifier() const { return "t_tuple" + identifierList(components()); } @@ -2354,7 +2354,7 @@ TypePointers FunctionType::parameterTypes() const return TypePointers(m_parameterTypes.cbegin() + 1, m_parameterTypes.cend()); } -string FunctionType::identifier() const +string FunctionType::richIdentifier() const { string id = "t_function_"; switch (m_kind) @@ -2847,7 +2847,7 @@ ASTPointer FunctionType::documentation() const return ASTPointer(); } -string MappingType::identifier() const +string MappingType::richIdentifier() const { return "t_mapping" + identifierList(m_keyType, m_valueType); } @@ -2870,7 +2870,7 @@ string MappingType::canonicalName() const return "mapping(" + keyType()->canonicalName() + " => " + valueType()->canonicalName() + ")"; } -string TypeType::identifier() const +string TypeType::richIdentifier() const { return "t_type" + identifierList(actualType()); } @@ -2955,7 +2955,7 @@ u256 ModifierType::storageSize() const solAssert(false, "Storage size of non-storable type type requested."); } -string ModifierType::identifier() const +string ModifierType::richIdentifier() const { return "t_modifier" + identifierList(m_parameterTypes); } @@ -2984,7 +2984,7 @@ string ModifierType::toString(bool _short) const return name + ")"; } -string ModuleType::identifier() const +string ModuleType::richIdentifier() const { return "t_module_" + std::to_string(m_sourceUnit.id()); } @@ -3010,7 +3010,7 @@ string ModuleType::toString(bool) const return string("module \"") + m_sourceUnit.annotation().path + string("\""); } -string MagicType::identifier() const +string MagicType::richIdentifier() const { switch (m_kind) { diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index ef898f37..a12b7063 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -163,7 +163,13 @@ public: /// @returns a valid solidity identifier such that two types should compare equal if and /// only if they have the same identifier. /// The identifier should start with "t_". - virtual std::string identifier() const = 0; + /// Can contain characters which are invalid in identifiers. + virtual std::string richIdentifier() const = 0; + /// @returns a valid solidity identifier such that two types should compare equal if and + /// only if they have the same identifier. + /// The identifier should start with "t_". + /// Will not contain any character which would be invalid as an identifier. + std::string identifier() const { return escapeIdentifier(richIdentifier()); } /// More complex identifier strings use "parentheses", where $_ is interpreted as as /// "opening parenthesis", _$ as "closing parenthesis", _$_ as "comma" and any $ that @@ -312,7 +318,7 @@ public: explicit IntegerType(int _bits, Modifier _modifier = Modifier::Unsigned); - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; @@ -359,7 +365,7 @@ public: explicit FixedPointType(int _totalBits, int _fractionalDigits, Modifier _modifier = Modifier::Unsigned); - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; @@ -416,7 +422,7 @@ public: virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } @@ -465,7 +471,7 @@ public: return TypePointer(); } - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } @@ -499,7 +505,7 @@ public: virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; @@ -527,7 +533,7 @@ class BoolType: public Type public: BoolType() {} virtual Category category() const override { return Category::Bool; } - virtual std::string identifier() const override { return "t_bool"; } + virtual std::string richIdentifier() const override { return "t_bool"; } virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; @@ -627,7 +633,7 @@ public: virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(const Type& _other) const override; virtual unsigned calldataEncodedSize(bool _padded) const override; virtual bool isDynamicallySized() const override { return m_hasDynamicLength; } @@ -684,7 +690,7 @@ public: /// Contracts can be converted to themselves and to integers. virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual unsigned calldataEncodedSize(bool _padded ) const override { @@ -742,7 +748,7 @@ public: explicit StructType(StructDefinition const& _struct, DataLocation _location = DataLocation::Storage): ReferenceType(_location), m_struct(_struct) {} virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override; - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual unsigned calldataEncodedSize(bool _padded) const override; virtual bool isDynamicallyEncoded() const override; @@ -797,7 +803,7 @@ public: virtual Category category() const override { return Category::Enum; } explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {} virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual unsigned calldataEncodedSize(bool _padded) const override { @@ -838,7 +844,7 @@ public: virtual Category category() const override { return Category::Tuple; } explicit TupleType(std::vector const& _types = std::vector()): m_components(_types) {} virtual bool isImplicitlyConvertibleTo(Type const& _other) const override; - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } virtual std::string toString(bool) const override; @@ -972,7 +978,7 @@ public: /// @returns the "self" parameter type for a bound function TypePointer const& selfType() const; - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; @@ -1076,7 +1082,7 @@ public: MappingType(TypePointer const& _keyType, TypePointer const& _valueType): m_keyType(_keyType), m_valueType(_valueType) {} - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual std::string toString(bool _short) const override; virtual std::string canonicalName() const override; @@ -1113,7 +1119,7 @@ public: TypePointer const& actualType() const { return m_actualType; } virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } virtual u256 storageSize() const override; @@ -1141,7 +1147,7 @@ public: virtual u256 storageSize() const override; virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned sizeOnStack() const override { return 0; } - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual std::string toString(bool _short) const override; @@ -1162,7 +1168,7 @@ public: explicit ModuleType(SourceUnit const& _source): m_sourceUnit(_source) {} virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } virtual bool canLiveOutsideStorage() const override { return true; } @@ -1192,7 +1198,7 @@ public: return TypePointer(); } - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } virtual bool canLiveOutsideStorage() const override { return true; } @@ -1216,7 +1222,7 @@ class InaccessibleDynamicType: public Type public: virtual Category category() const override { return Category::InaccessibleDynamic; } - virtual std::string identifier() const override { return "t_inaccessible"; } + virtual std::string richIdentifier() const override { return "t_inaccessible"; } virtual bool isImplicitlyConvertibleTo(Type const&) const override { return false; } virtual bool isExplicitlyConvertibleTo(Type const&) const override { return false; } virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } -- cgit v1.2.3 From b471983e3cacf4d92ecd987eac85620bd8030aff Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 24 Feb 2018 01:13:34 +0100 Subject: Use new escaping helpers for type identifiers --- libsolidity/ast/Types.cpp | 27 +++++++++------------------ libsolidity/ast/Types.h | 2 -- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 97e7a922..fadaf621 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -131,28 +131,28 @@ namespace string parenthesizeIdentifier(string const& _internal) { - return "$_" + _internal + "_$"; + return "(" + _internal + ")"; } template string identifierList(Range const&& _list) { - return parenthesizeIdentifier(boost::algorithm::join(_list, "_$_")); + return parenthesizeIdentifier(boost::algorithm::join(_list, ",")); } -string identifier(TypePointer const& _type) +string richIdentifier(TypePointer const& _type) { - return _type ? _type->identifier() : ""; + return _type ? _type->richIdentifier() : ""; } string identifierList(vector const& _list) { - return identifierList(_list | boost::adaptors::transformed(identifier)); + return identifierList(_list | boost::adaptors::transformed(richIdentifier)); } string identifierList(TypePointer const& _type) { - return parenthesizeIdentifier(identifier(_type)); + return parenthesizeIdentifier(richIdentifier(_type)); } string identifierList(TypePointer const& _type1, TypePointer const& _type2) @@ -165,7 +165,7 @@ string identifierList(TypePointer const& _type1, TypePointer const& _type2) string parenthesizeUserIdentifier(string const& _internal) { - return parenthesizeIdentifier(boost::algorithm::replace_all_copy(_internal, "$", "$$$")); + return parenthesizeIdentifier(_internal); } } @@ -173,23 +173,14 @@ string parenthesizeUserIdentifier(string const& _internal) string Type::escapeIdentifier(string const& _identifier) { string ret = _identifier; - boost::algorithm::replace_all(ret, "$", "_$$$_"); + // FIXME: should be _$$$_ + boost::algorithm::replace_all(ret, "$", "$$$"); boost::algorithm::replace_all(ret, ",", "_$_"); boost::algorithm::replace_all(ret, "(", "$_"); boost::algorithm::replace_all(ret, ")", "_$"); return ret; } -string Type::unescapeIdentifier(string const& _identifier) -{ - string ret = _identifier; - boost::algorithm::replace_all(ret, "_$_", ","); - boost::algorithm::replace_all(ret, "_$$$_", "$"); - boost::algorithm::replace_all(ret, "$_", "("); - boost::algorithm::replace_all(ret, "_$", ")"); - return ret; -} - TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type) { solAssert(Token::isElementaryTypeName(_type.token()), diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index a12b7063..7985521e 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -176,8 +176,6 @@ public: /// appears as part of a user-supplied identifier is escaped as _$$$_. /// @returns an escaped identifier (will not contain any parenthesis or commas) static std::string escapeIdentifier(std::string const& _identifier); - /// @returns an unescaped identifier - static std::string unescapeIdentifier(std::string const& _identifier); virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; } virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const -- cgit v1.2.3 From 0736d91eaa0807c21364376b455c0b72c1aeb9be Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 26 Feb 2018 16:25:23 +0100 Subject: Add test for escapeIdentifier --- test/libsolidity/SolidityTypes.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/libsolidity/SolidityTypes.cpp b/test/libsolidity/SolidityTypes.cpp index 9f385a04..bc9f2fe1 100644 --- a/test/libsolidity/SolidityTypes.cpp +++ b/test/libsolidity/SolidityTypes.cpp @@ -88,6 +88,21 @@ BOOST_AUTO_TEST_CASE(storage_layout_arrays) BOOST_CHECK(ArrayType(DataLocation::Storage, make_shared(32), 9).storageSize() == 9); } +BOOST_AUTO_TEST_CASE(type_escaping) +{ + BOOST_CHECK_EQUAL(Type::escapeIdentifier("("), "$_"); + BOOST_CHECK_EQUAL(Type::escapeIdentifier(")"), "_$"); + BOOST_CHECK_EQUAL(Type::escapeIdentifier(","), "_$_"); + BOOST_CHECK_EQUAL(Type::escapeIdentifier("$"), "$$$"); + BOOST_CHECK_EQUAL(Type::escapeIdentifier("()"), "$__$"); + BOOST_CHECK_EQUAL(Type::escapeIdentifier("(,)"), "$__$__$"); + BOOST_CHECK_EQUAL(Type::escapeIdentifier("(,$,)"), "$__$_$$$_$__$"); + BOOST_CHECK_EQUAL( + Type::escapeIdentifier("((__(_$_$$,__($$,,,$$),$,,,)))$$,$$"), + "$_$___$__$$$_$$$$$$_$___$_$$$$$$_$__$__$_$$$$$$_$_$_$$$_$__$__$__$_$_$$$$$$$_$_$$$$$$" + ); +} + BOOST_AUTO_TEST_CASE(type_identifiers) { ASTNode::resetID(); -- cgit v1.2.3 From b9dccf9f20d8f4348966e3c6cacd11fbda47c162 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 26 Feb 2018 19:33:03 +0100 Subject: Split circle into build and test for x86. --- circle.yml | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/circle.yml b/circle.yml index 94c711b0..add8a815 100644 --- a/circle.yml +++ b/circle.yml @@ -93,7 +93,7 @@ jobs: name: Install build dependencies command: | apt-get -qq update - apt-get -qy install ccache cmake libboost-all-dev libz3-dev libleveldb1v5 + apt-get -qy install ccache cmake libboost-all-dev libz3-dev - run: name: Init submodules command: | @@ -114,15 +114,34 @@ jobs: key: ccache-{{ arch }}-{{ .Branch }} paths: - ~/.ccache + - store_artifacts: + path: build/solc/solc + destination: solc + - persist_to_workspace: + root: build + paths: + - solc/solc + - test/soltest + - test/solfuzzer + + test_x86: + docker: + - image: buildpack-deps:artful + steps: + - checkout + - attach_workspace: + at: build + - run: + name: Install dependencies + command: | + apt-get -qq update + apt-get -qy install libz3-dev libleveldb1v5 - run: mkdir -p test_results - run: name: Tests command: scripts/tests.sh --junit_report test_results - store_test_results: path: test_results/ - - store_artifacts: - path: build/solc/solc - destination: solc docs: docker: @@ -158,4 +177,7 @@ workflows: requires: - build_emscripten - build_x86 + - test_x86: + requires: + - build_x86 - docs -- cgit v1.2.3 From 64eaff64200d166bdd48f81bceefec9bc83db72f Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 24 Nov 2017 03:06:42 +0000 Subject: Random documentation updates (assembly, faq) --- docs/assembly.rst | 14 ++++++++------ docs/contracts.rst | 6 +++--- docs/frequently-asked-questions.rst | 30 ------------------------------ 3 files changed, 11 insertions(+), 39 deletions(-) diff --git a/docs/assembly.rst b/docs/assembly.rst index 02522469..35484ad4 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -162,6 +162,8 @@ The opcodes ``pushi`` and ``jumpdest`` cannot be used directly. In the grammar, opcodes are represented as pre-defined identifiers. +-------------------------+------+-----------------------------------------------------------------+ +| Instruction | | Explanation | ++=========================+======+=================================================================+ | stop + `-` | stop execution, identical to return(0,0) | +-------------------------+------+-----------------------------------------------------------------+ | add(x, y) | | x + y | @@ -228,7 +230,7 @@ In the grammar, opcodes are represented as pre-defined identifiers. +-------------------------+------+-----------------------------------------------------------------+ | mstore(p, v) | `-` | mem[p..(p+32)) := v | +-------------------------+------+-----------------------------------------------------------------+ -| mstore8(p, v) | `-` | mem[p] := v & 0xff - only modifies a single byte | +| mstore8(p, v) | `-` | mem[p] := v & 0xff (only modifies a single byte) | +-------------------------+------+-----------------------------------------------------------------+ | sload(p) | | storage[p] | +-------------------------+------+-----------------------------------------------------------------+ @@ -242,7 +244,7 @@ In the grammar, opcodes are represented as pre-defined identifiers. +-------------------------+------+-----------------------------------------------------------------+ | balance(a) | | wei balance at address a | +-------------------------+------+-----------------------------------------------------------------+ -| caller | | call sender (excluding delegatecall) | +| caller | | call sender (excluding ``delegatecall``) | +-------------------------+------+-----------------------------------------------------------------+ | callvalue | | wei sent together with the current call | +-------------------------+------+-----------------------------------------------------------------+ @@ -276,13 +278,13 @@ In the grammar, opcodes are represented as pre-defined identifiers. | | | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) | | | | and 1 on success | +-------------------------+------+-----------------------------------------------------------------+ -| callcode(g, a, v, in, | | identical to `call` but only use the code from a and stay | +| callcode(g, a, v, in, | | identical to ``call`` but only use the code from a and stay | | insize, out, outsize) | | in the context of the current contract otherwise | +-------------------------+------+-----------------------------------------------------------------+ -| delegatecall(g, a, in, | | identical to `callcode` but also keep ``caller`` | +| delegatecall(g, a, in, | | identical to ``callcode`` but also keep ``caller`` | | insize, out, outsize) | | and ``callvalue`` | +-------------------------+------+-----------------------------------------------------------------+ -| staticcall(g, a, in, | | identical to `call(g, a, 0, in, insize, out, outsize)` but do | +| staticcall(g, a, in, | | identical to ``call(g, a, 0, in, insize, out, outsize)`` but do | | insize, out, outsize) | | not allow state modifications | +-------------------------+------+-----------------------------------------------------------------+ | return(p, s) | `-` | end execution, return data mem[p..(p+s)) | @@ -727,7 +729,7 @@ The following assembly will be generated:: // function dispatcher switch div(calldataload(0), exp(2, 226)) case 0xb3de648b { - let (r) = f(calldataload(4)) + let r := f(calldataload(4)) let ret := $allocate(0x20) mstore(ret, r) return(ret, 0x20) diff --git a/docs/contracts.rst b/docs/contracts.rst index ee203263..6dfcdf60 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -467,13 +467,13 @@ The following statements are considered modifying the state: } .. note:: - ``constant`` is an alias to ``view``. + ``constant`` on functions is an alias to ``view``. .. note:: Getter methods are marked ``view``. .. warning:: - The compiler does not enforce yet that a ``view`` method is not modifying state. + Before version 0.4.17 the compiler didn't enforce that ``view`` is not modifying the state. .. index:: ! pure function, function;pure @@ -503,7 +503,7 @@ In addition to the list of state modifying statements explained above, the follo } .. warning:: - The compiler does not enforce yet that a ``pure`` method is not reading from the state. + Before version 0.4.17 the compiler didn't enforce that ``view`` is not reading the state. .. index:: ! fallback function, function;fallback diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index 3c83cbc7..6a2fe685 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -9,17 +9,6 @@ This list was originally compiled by `fivedogit `_. Basic Questions *************** -Example contracts -================= - -There are some `contract examples `_ by fivedogit and -there should be a `test contract `_ for every single feature of Solidity. - -Create and publish the most basic contract possible -=================================================== - -A quite simple contract is the `greeter `_ - Is it possible to do something on a specific block number? (e.g. publish a contract or execute a transaction) ============================================================================================================= @@ -74,25 +63,6 @@ has it (which includes `Remix `_), then ``contractname.kill.sendTransaction({from:eth.coinbase})``, just the same as my examples. -Store Ether in a contract -========================= - -The trick is to create the contract with ``{from:someaddress, value: web3.toWei(3,"ether")...}`` - -See `endowment_retriever.sol `_. - -Use a non-constant function (req ``sendTransaction``) to increment a variable in a contract -=========================================================================================== - -See `value_incrementer.sol `_. - -Get a contract to return its funds to you (not using ``selfdestruct(...)``). -============================================================================ - -This example demonstrates how to send funds from a contract to an address. - -See `endowment_retriever `_. - Can you return an array or a ``string`` from a solidity function call? ====================================================================== -- cgit v1.2.3 From 098033c94a6bbedaee60b19f096dfee5bf2b4550 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 19 Feb 2018 16:58:56 +0100 Subject: Fix name shadowing in ballot example contract. --- docs/solidity-by-example.rst | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index e5b44c98..57556fa5 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -94,7 +94,11 @@ of votes. // called incorrectly. But watch out, this // will currently also consume all provided gas // (this is planned to change in the future). - require((msg.sender == chairperson) && !voters[voter].voted && (voters[voter].weight == 0)); + require( + (msg.sender == chairperson) && + !voters[voter].voted && + (voters[voter].weight == 0) + ); voters[voter].weight = 1; } @@ -126,15 +130,15 @@ of votes. // modifies `voters[msg.sender].voted` sender.voted = true; sender.delegate = to; - Voter storage delegate = voters[to]; - if (delegate.voted) { + Voter storage delegate_ = voters[to]; + if (delegate_.voted) { // If the delegate already voted, // directly add to the number of votes - proposals[delegate.vote].voteCount += sender.weight; + proposals[delegate_.vote].voteCount += sender.weight; } else { // If the delegate did not vote yet, // add to her weight. - delegate.weight += sender.weight; + delegate_.weight += sender.weight; } } @@ -155,13 +159,13 @@ of votes. /// @dev Computes the winning proposal taking all /// previous votes into account. function winningProposal() public view - returns (uint winningProposal) + returns (uint winningProposal_) { uint winningVoteCount = 0; for (uint p = 0; p < proposals.length; p++) { if (proposals[p].voteCount > winningVoteCount) { winningVoteCount = proposals[p].voteCount; - winningProposal = p; + winningProposal_ = p; } } } @@ -170,12 +174,13 @@ of votes. // of the winner contained in the proposals array and then // returns the name of the winner function winnerName() public view - returns (bytes32 winnerName) + returns (bytes32 winnerName_) { - winnerName = proposals[winningProposal()].name; + winnerName_ = proposals[winningProposal()].name; } } + Possible Improvements ===================== -- cgit v1.2.3 From 92fe9e621eace7527818ac1a136603d15058cb87 Mon Sep 17 00:00:00 2001 From: wbt Date: Tue, 23 Jan 2018 11:55:37 -0500 Subject: Note deprecation of constant keyword on functions --- docs/contracts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 6dfcdf60..416dc649 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -467,7 +467,7 @@ The following statements are considered modifying the state: } .. note:: - ``constant`` on functions is an alias to ``view``. + ``constant`` on functions is an alias to ``view``, but this is deprecated and is planned to be dropped in version 0.5.0. .. note:: Getter methods are marked ``view``. -- cgit v1.2.3 From 241134a8816fbd832ab60ccd7aecc70ba00e8ac3 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Feb 2018 00:36:01 +0100 Subject: Mention that solcjs is not compatible with solc in the using-the-compiler section --- docs/installing-solidity.rst | 2 ++ docs/using-the-compiler.rst | 3 +++ 2 files changed, 5 insertions(+) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 8f30f199..8c445e5e 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -29,6 +29,8 @@ Further options on this page detail installing commandline Solidity compiler sof on your computer. Choose a commandline compiler if you are working on a larger contract or if you require more compilation options. +.. _solcjs: + npm / Node.js ============= diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 42cc807a..66e3ac35 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -9,6 +9,9 @@ Using the compiler Using the Commandline Compiler ****************************** +.. note:: + This section doesn't apply to :ref:`solcjs `. + One of the build targets of the Solidity repository is ``solc``, the solidity commandline compiler. Using ``solc --help`` provides you with an explanation of all options. The compiler can produce various outputs, ranging from simple binaries and assembly over an abstract syntax tree (parse tree) to estimations of gas usage. If you only want to compile a single file, you run it as ``solc --bin sourceFile.sol`` and it will print the binary. Before you deploy your contract, activate the optimizer while compiling using ``solc --optimize --bin sourceFile.sol``. If you want to get some of the more advanced output variants of ``solc``, it is probably better to tell it to output everything to separate files using ``solc -o outputDirectory --bin --ast --asm sourceFile.sol``. -- cgit v1.2.3 From 3594f02d51aac4a99d3f1fefbd37fa27346b75ae Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Feb 2018 00:38:52 +0100 Subject: Fix link to solcjs in the installation section --- docs/installing-solidity.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 8c445e5e..e26870f0 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -36,13 +36,13 @@ npm / Node.js Use `npm` for a convenient and portable way to install `solcjs`, a Solidity compiler. The `solcjs` program has less features than all options further down this page. Our -`Using the compiler ` documentation assumes you are using +:ref:`commandline-compiler` documentation assumes you are using the full-featured compiler, `solc`. So if you install `solcjs` from `npm` then you will -stop reading the documentation here and then continue to , +stop reading the documentation here and then continue to `solc-js `_. -Note: The `solc-js ` project is derived from the C++ +Note: The solc-js project is derived from the C++ `solc` by using Emscripten. `solc-js` can be used in JavaScript projects directly (such as Remix). -Please refer to the `solc-js `_ repository for instructions. +Please refer to the solc-js repository for instructions. .. code:: bash -- cgit v1.2.3 From 5c0d82059f50cb6ac67171ca075f394acae27228 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Feb 2018 02:10:24 +0100 Subject: Turn throw into a syntax error for 0.5.0 --- Changelog.md | 1 + libsolidity/analysis/SyntaxChecker.cpp | 16 ++++++++++++---- test/libsolidity/SolidityNameAndTypeResolution.cpp | 11 ++++++++++- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index 3af3a99d..acd3aa54 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Features: * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. * Support and recommend using ``emit EventName();`` to call events explicitly. * Syntax Analyser: Do not warn about experimental features if they do not concern code generation. + * Syntax Checker: Mark ``throw`` as an error as experimental 0.5.0 feature. Bugfixes: * Assembly: Raise error on oversized number literals in assembly. diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 74834ba4..1dcfeb27 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -174,10 +174,18 @@ bool SyntaxChecker::visit(Break const& _breakStatement) bool SyntaxChecker::visit(Throw const& _throwStatement) { - m_errorReporter.warning( - _throwStatement.location(), - "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"." - ); + bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050); + + if (v050) + m_errorReporter.syntaxError( + _throwStatement.location(), + "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"." + ); + else + m_errorReporter.warning( + _throwStatement.location(), + "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"." + ); return true; } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 93abee0d..be147e48 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -6332,7 +6332,16 @@ BOOST_AUTO_TEST_CASE(warn_about_throw) } } )"; - CHECK_WARNING(text, "\"throw\" is deprecated"); + CHECK_WARNING(text, "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\""); + text = R"( + pragma experimental "v0.5.0"; + contract C { + function f() pure public { + throw; + } + } + )"; + CHECK_ERROR(text, SyntaxError, "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\""); } BOOST_AUTO_TEST_CASE(bare_revert) -- cgit v1.2.3 From e34d367593ccc1d5b7456dfc171473997a645eb6 Mon Sep 17 00:00:00 2001 From: Dax Bondye Date: Fri, 23 Feb 2018 10:10:01 -0800 Subject: Multiline output parameters and return statements --- docs/style-guide.rst | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 9249f3e1..533c4be5 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -112,7 +112,9 @@ No:: } } -Maximum Line Length +.. _maximum_line_length: + +Maximum Line Length =================== Keeping lines under the `PEP 8 recommendation `_ of 79 (or 99) @@ -650,6 +652,50 @@ No:: doSomething(); } +Multiline output parameters and return statements should follow the same style recommended for wrapping long lines found in the :ref:`Maximum Line Length ` section. + +Yes:: + + function thisFunctionNameIsReallyLong( + address a, + address b, + address c + ) + public + returns ( + address someAddressName, + uint256 LongArgument, + uint256 Argument + ) + { + doSomething() + + return ( + veryLongReturnArg1, + veryLongReturnArg2, + veryLongReturnArg3 + ); + } + +No:: + + function thisFunctionNameIsReallyLong( + address a, + address b, + address c + ) + public + returns (address someAddressName, + uint256 LongArgument, + uint256 Argument) + { + doSomething() + + return (veryLongReturnArg1, + veryLongReturnArg1, + veryLongReturnArg1); + } + For constructor functions on inherited contracts whose bases require arguments, it is recommended to drop the base constructors onto new lines in the same manner as modifiers if the function declaration is long or hard to read. -- cgit v1.2.3 From e424bd1007343d2e39b1949e6195fc1f0698441c Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 26 Feb 2018 23:59:44 +0100 Subject: Fix some keyword highlighting in docs --- docs/assembly.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/assembly.rst b/docs/assembly.rst index 35484ad4..d7e5eaf0 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -378,8 +378,8 @@ Functions external to inline assembly can also be accessed: The assembly will push their entry label (with virtual function resolution applied). The calling semantics in solidity are: - - the caller pushes return label, arg1, arg2, ..., argn - - the call returns with ret1, ret2, ..., retm + - the caller pushes ``return label``, ``arg1``, ``arg2``, ..., ``argn`` + - the call returns with ``ret1``, ``ret2``, ..., ``retm`` This feature is still a bit cumbersome to use, because the stack offset essentially changes during the call, and thus references to local variables will be wrong. -- cgit v1.2.3 From 8cebfb168bdd9c17769670f289d7721538fd1230 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Feb 2018 01:19:38 +0100 Subject: Introduce VM version ('hard fork') column for assembly opcodes --- docs/assembly.rst | 324 +++++++++++++++++++++++++++--------------------------- 1 file changed, 163 insertions(+), 161 deletions(-) diff --git a/docs/assembly.rst b/docs/assembly.rst index d7e5eaf0..46416142 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -153,6 +153,8 @@ If an opcode takes arguments (always from the top of the stack), they are given Note that the order of arguments can be seen to be reversed in non-functional style (explained below). Opcodes marked with ``-`` do not push an item onto the stack, those marked with ``*`` are special and all others push exactly one item onto the stack. +Opcodes marked with ``F``, ``H``, ``B`` or ``C`` are present since Frontier, Homestead, Byzantium or Constantinople, respectively. +Constantinople is still in planning and all instructions marked as such will result in an invalid instruction exception. In the following, ``mem[a...b)`` signifies the bytes of memory starting at position ``a`` up to (excluding) position ``b`` and ``storage[p]`` signifies the storage contents at position ``p``. @@ -161,167 +163,167 @@ The opcodes ``pushi`` and ``jumpdest`` cannot be used directly. In the grammar, opcodes are represented as pre-defined identifiers. -+-------------------------+------+-----------------------------------------------------------------+ -| Instruction | | Explanation | -+=========================+======+=================================================================+ -| stop + `-` | stop execution, identical to return(0,0) | -+-------------------------+------+-----------------------------------------------------------------+ -| add(x, y) | | x + y | -+-------------------------+------+-----------------------------------------------------------------+ -| sub(x, y) | | x - y | -+-------------------------+------+-----------------------------------------------------------------+ -| mul(x, y) | | x * y | -+-------------------------+------+-----------------------------------------------------------------+ -| div(x, y) | | x / y | -+-------------------------+------+-----------------------------------------------------------------+ -| sdiv(x, y) | | x / y, for signed numbers in two's complement | -+-------------------------+------+-----------------------------------------------------------------+ -| mod(x, y) | | x % y | -+-------------------------+------+-----------------------------------------------------------------+ -| smod(x, y) | | x % y, for signed numbers in two's complement | -+-------------------------+------+-----------------------------------------------------------------+ -| exp(x, y) | | x to the power of y | -+-------------------------+------+-----------------------------------------------------------------+ -| not(x) | | ~x, every bit of x is negated | -+-------------------------+------+-----------------------------------------------------------------+ -| lt(x, y) | | 1 if x < y, 0 otherwise | -+-------------------------+------+-----------------------------------------------------------------+ -| gt(x, y) | | 1 if x > y, 0 otherwise | -+-------------------------+------+-----------------------------------------------------------------+ -| slt(x, y) | | 1 if x < y, 0 otherwise, for signed numbers in two's complement | -+-------------------------+------+-----------------------------------------------------------------+ -| sgt(x, y) | | 1 if x > y, 0 otherwise, for signed numbers in two's complement | -+-------------------------+------+-----------------------------------------------------------------+ -| eq(x, y) | | 1 if x == y, 0 otherwise | -+-------------------------+------+-----------------------------------------------------------------+ -| iszero(x) | | 1 if x == 0, 0 otherwise | -+-------------------------+------+-----------------------------------------------------------------+ -| and(x, y) | | bitwise and of x and y | -+-------------------------+------+-----------------------------------------------------------------+ -| or(x, y) | | bitwise or of x and y | -+-------------------------+------+-----------------------------------------------------------------+ -| xor(x, y) | | bitwise xor of x and y | -+-------------------------+------+-----------------------------------------------------------------+ -| byte(n, x) | | nth byte of x, where the most significant byte is the 0th byte | -+-------------------------+------+-----------------------------------------------------------------+ -| addmod(x, y, m) | | (x + y) % m with arbitrary precision arithmetics | -+-------------------------+------+-----------------------------------------------------------------+ -| mulmod(x, y, m) | | (x * y) % m with arbitrary precision arithmetics | -+-------------------------+------+-----------------------------------------------------------------+ -| signextend(i, x) | | sign extend from (i*8+7)th bit counting from least significant | -+-------------------------+------+-----------------------------------------------------------------+ -| keccak256(p, n) | | keccak(mem[p...(p+n))) | -+-------------------------+------+-----------------------------------------------------------------+ -| sha3(p, n) | | keccak(mem[p...(p+n))) | -+-------------------------+------+-----------------------------------------------------------------+ -| jump(label) | `-` | jump to label / code position | -+-------------------------+------+-----------------------------------------------------------------+ -| jumpi(label, cond) | `-` | jump to label if cond is nonzero | -+-------------------------+------+-----------------------------------------------------------------+ -| pc | | current position in code | -+-------------------------+------+-----------------------------------------------------------------+ -| pop(x) | `-` | remove the element pushed by x | -+-------------------------+------+-----------------------------------------------------------------+ -| dup1 ... dup16 | | copy ith stack slot to the top (counting from top) | -+-------------------------+------+-----------------------------------------------------------------+ -| swap1 ... swap16 | `*` | swap topmost and ith stack slot below it | -+-------------------------+------+-----------------------------------------------------------------+ -| mload(p) | | mem[p..(p+32)) | -+-------------------------+------+-----------------------------------------------------------------+ -| mstore(p, v) | `-` | mem[p..(p+32)) := v | -+-------------------------+------+-----------------------------------------------------------------+ -| mstore8(p, v) | `-` | mem[p] := v & 0xff (only modifies a single byte) | -+-------------------------+------+-----------------------------------------------------------------+ -| sload(p) | | storage[p] | -+-------------------------+------+-----------------------------------------------------------------+ -| sstore(p, v) | `-` | storage[p] := v | -+-------------------------+------+-----------------------------------------------------------------+ -| msize | | size of memory, i.e. largest accessed memory index | -+-------------------------+------+-----------------------------------------------------------------+ -| gas | | gas still available to execution | -+-------------------------+------+-----------------------------------------------------------------+ -| address | | address of the current contract / execution context | -+-------------------------+------+-----------------------------------------------------------------+ -| balance(a) | | wei balance at address a | -+-------------------------+------+-----------------------------------------------------------------+ -| caller | | call sender (excluding ``delegatecall``) | -+-------------------------+------+-----------------------------------------------------------------+ -| callvalue | | wei sent together with the current call | -+-------------------------+------+-----------------------------------------------------------------+ -| calldataload(p) | | call data starting from position p (32 bytes) | -+-------------------------+------+-----------------------------------------------------------------+ -| calldatasize | | size of call data in bytes | -+-------------------------+------+-----------------------------------------------------------------+ -| calldatacopy(t, f, s) | `-` | copy s bytes from calldata at position f to mem at position t | -+-------------------------+------+-----------------------------------------------------------------+ -| codesize | | size of the code of the current contract / execution context | -+-------------------------+------+-----------------------------------------------------------------+ -| codecopy(t, f, s) | `-` | copy s bytes from code at position f to mem at position t | -+-------------------------+------+-----------------------------------------------------------------+ -| extcodesize(a) | | size of the code at address a | -+-------------------------+------+-----------------------------------------------------------------+ -| extcodecopy(a, t, f, s) | `-` | like codecopy(t, f, s) but take code at address a | -+-------------------------+------+-----------------------------------------------------------------+ -| returndatasize | | size of the last returndata | -+-------------------------+------+-----------------------------------------------------------------+ -| returndatacopy(t, f, s) | `-` | copy s bytes from returndata at position f to mem at position t | -+-------------------------+------+-----------------------------------------------------------------+ -| create(v, p, s) | | create new contract with code mem[p..(p+s)) and send v wei | -| | | and return the new address | -+-------------------------+------+-----------------------------------------------------------------+ -| create2(v, n, p, s) | | create new contract with code mem[p..(p+s)) at address | -| | | keccak256(
. n . keccak256(mem[p..(p+s))) and send v | -| | | wei and return the new address | -+-------------------------+------+-----------------------------------------------------------------+ -| call(g, a, v, in, | | call contract at address a with input mem[in..(in+insize)) | -| insize, out, outsize) | | providing g gas and v wei and output area | -| | | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) | -| | | and 1 on success | -+-------------------------+------+-----------------------------------------------------------------+ -| callcode(g, a, v, in, | | identical to ``call`` but only use the code from a and stay | -| insize, out, outsize) | | in the context of the current contract otherwise | -+-------------------------+------+-----------------------------------------------------------------+ -| delegatecall(g, a, in, | | identical to ``callcode`` but also keep ``caller`` | -| insize, out, outsize) | | and ``callvalue`` | -+-------------------------+------+-----------------------------------------------------------------+ -| staticcall(g, a, in, | | identical to ``call(g, a, 0, in, insize, out, outsize)`` but do | -| insize, out, outsize) | | not allow state modifications | -+-------------------------+------+-----------------------------------------------------------------+ -| return(p, s) | `-` | end execution, return data mem[p..(p+s)) | -+-------------------------+------+-----------------------------------------------------------------+ -| revert(p, s) | `-` | end execution, revert state changes, return data mem[p..(p+s)) | -+-------------------------+------+-----------------------------------------------------------------+ -| selfdestruct(a) | `-` | end execution, destroy current contract and send funds to a | -+-------------------------+------+-----------------------------------------------------------------+ -| invalid | `-` | end execution with invalid instruction | -+-------------------------+------+-----------------------------------------------------------------+ -| log0(p, s) | `-` | log without topics and data mem[p..(p+s)) | -+-------------------------+------+-----------------------------------------------------------------+ -| log1(p, s, t1) | `-` | log with topic t1 and data mem[p..(p+s)) | -+-------------------------+------+-----------------------------------------------------------------+ -| log2(p, s, t1, t2) | `-` | log with topics t1, t2 and data mem[p..(p+s)) | -+-------------------------+------+-----------------------------------------------------------------+ -| log3(p, s, t1, t2, t3) | `-` | log with topics t1, t2, t3 and data mem[p..(p+s)) | -+-------------------------+------+-----------------------------------------------------------------+ -| log4(p, s, t1, t2, t3, | `-` | log with topics t1, t2, t3, t4 and data mem[p..(p+s)) | -| t4) | | | -+-------------------------+------+-----------------------------------------------------------------+ -| origin | | transaction sender | -+-------------------------+------+-----------------------------------------------------------------+ -| gasprice | | gas price of the transaction | -+-------------------------+------+-----------------------------------------------------------------+ -| blockhash(b) | | hash of block nr b - only for last 256 blocks excluding current | -+-------------------------+------+-----------------------------------------------------------------+ -| coinbase | | current mining beneficiary | -+-------------------------+------+-----------------------------------------------------------------+ -| timestamp | | timestamp of the current block in seconds since the epoch | -+-------------------------+------+-----------------------------------------------------------------+ -| number | | current block number | -+-------------------------+------+-----------------------------------------------------------------+ -| difficulty | | difficulty of the current block | -+-------------------------+------+-----------------------------------------------------------------+ -| gaslimit | | block gas limit of the current block | -+-------------------------+------+-----------------------------------------------------------------+ ++-------------------------+-----+---+-----------------------------------------------------------------+ +| Instruction | | | Explanation | ++=========================+=====+===+=================================================================+ +| stop + `-` | F | stop execution, identical to return(0,0) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| add(x, y) | | F | x + y | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| sub(x, y) | | F | x - y | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| mul(x, y) | | F | x * y | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| div(x, y) | | F | x / y | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| sdiv(x, y) | | F | x / y, for signed numbers in two's complement | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| mod(x, y) | | F | x % y | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| smod(x, y) | | F | x % y, for signed numbers in two's complement | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| exp(x, y) | | F | x to the power of y | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| not(x) | | F | ~x, every bit of x is negated | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| lt(x, y) | | F | 1 if x < y, 0 otherwise | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| gt(x, y) | | F | 1 if x > y, 0 otherwise | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| slt(x, y) | | F | 1 if x < y, 0 otherwise, for signed numbers in two's complement | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| sgt(x, y) | | F | 1 if x > y, 0 otherwise, for signed numbers in two's complement | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| eq(x, y) | | F | 1 if x == y, 0 otherwise | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| iszero(x) | | F | 1 if x == 0, 0 otherwise | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| and(x, y) | | F | bitwise and of x and y | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| or(x, y) | | F | bitwise or of x and y | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| xor(x, y) | | F | bitwise xor of x and y | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| byte(n, x) | | F | nth byte of x, where the most significant byte is the 0th byte | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| addmod(x, y, m) | | F | (x + y) % m with arbitrary precision arithmetics | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| mulmod(x, y, m) | | F | (x * y) % m with arbitrary precision arithmetics | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| signextend(i, x) | | F | sign extend from (i*8+7)th bit counting from least significant | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| keccak256(p, n) | | F | keccak(mem[p...(p+n))) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| sha3(p, n) | | F | keccak(mem[p...(p+n))) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| jump(label) | `-` | F | jump to label / code position | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| jumpi(label, cond) | `-` | F | jump to label if cond is nonzero | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| pc | | F | current position in code | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| pop(x) | `-` | F | remove the element pushed by x | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| dup1 ... dup16 | | F | copy ith stack slot to the top (counting from top) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| swap1 ... swap16 | `*` | F | swap topmost and ith stack slot below it | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| mload(p) | | F | mem[p..(p+32)) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| mstore(p, v) | `-` | F | mem[p..(p+32)) := v | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| mstore8(p, v) | `-` | F | mem[p] := v & 0xff (only modifies a single byte) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| sload(p) | | F | storage[p] | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| sstore(p, v) | `-` | F | storage[p] := v | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| msize | | F | size of memory, i.e. largest accessed memory index | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| gas | | F | gas still available to execution | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| address | | F | address of the current contract / execution context | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| balance(a) | | F | wei balance at address a | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| caller | | F | call sender (excluding ``delegatecall``) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| callvalue | | F | wei sent together with the current call | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| calldataload(p) | | F | call data starting from position p (32 bytes) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| calldatasize | | F | size of call data in bytes | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| calldatacopy(t, f, s) | `-` | F | copy s bytes from calldata at position f to mem at position t | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| codesize | | F | size of the code of the current contract / execution context | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| codecopy(t, f, s) | `-` | F | copy s bytes from code at position f to mem at position t | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| extcodesize(a) | | F | size of the code at address a | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| extcodecopy(a, t, f, s) | `-` | F | like codecopy(t, f, s) but take code at address a | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| returndatasize | | B | size of the last returndata | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| returndatacopy(t, f, s) | `-` | B | copy s bytes from returndata at position f to mem at position t | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| create(v, p, s) | | F | create new contract with code mem[p..(p+s)) and send v wei | +| | | | and return the new address | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| create2(v, n, p, s) | | C | create new contract with code mem[p..(p+s)) at address | +| | | | keccak256(
. n . keccak256(mem[p..(p+s))) and send v | +| | | | wei and return the new address | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| call(g, a, v, in, | | F | call contract at address a with input mem[in..(in+insize)) | +| insize, out, outsize) | | | providing g gas and v wei and output area | +| | | | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) | +| | | | and 1 on success | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| callcode(g, a, v, in, | | F | identical to ``call`` but only use the code from a and stay | +| insize, out, outsize) | | | in the context of the current contract otherwise | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| delegatecall(g, a, in, | | H | identical to ``callcode`` but also keep ``caller`` | +| insize, out, outsize) | | | and ``callvalue`` | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| staticcall(g, a, in, | | B | identical to ``call(g, a, 0, in, insize, out, outsize)`` but do | +| insize, out, outsize) | | | not allow state modifications | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| return(p, s) | `-` | F | end execution, return data mem[p..(p+s)) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| revert(p, s) | `-` | B | end execution, revert state changes, return data mem[p..(p+s)) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| selfdestruct(a) | `-` | F | end execution, destroy current contract and send funds to a | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| invalid | `-` | F | end execution with invalid instruction | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| log0(p, s) | `-` | F | log without topics and data mem[p..(p+s)) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| log1(p, s, t1) | `-` | F | log with topic t1 and data mem[p..(p+s)) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| log2(p, s, t1, t2) | `-` | F | log with topics t1, t2 and data mem[p..(p+s)) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| log3(p, s, t1, t2, t3) | `-` | F | log with topics t1, t2, t3 and data mem[p..(p+s)) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| log4(p, s, t1, t2, t3, | `-` | F | log with topics t1, t2, t3, t4 and data mem[p..(p+s)) | +| t4) | | | | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| origin | | F | transaction sender | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| gasprice | | F | gas price of the transaction | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| blockhash(b) | | F | hash of block nr b - only for last 256 blocks excluding current | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| coinbase | | F | current mining beneficiary | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| timestamp | | F | timestamp of the current block in seconds since the epoch | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| number | | F | current block number | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| difficulty | | F | difficulty of the current block | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| gaslimit | | F | block gas limit of the current block | ++-------------------------+-----+---+-----------------------------------------------------------------+ Literals -------- -- cgit v1.2.3 From aeb75172d584c45fcf484b99ad3495d5774850dd Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Feb 2018 01:43:12 +0100 Subject: Fix builtin function formatting in julia --- docs/julia.rst | 153 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 77 insertions(+), 76 deletions(-) diff --git a/docs/julia.rst b/docs/julia.rst index 9e961a9d..078bc55b 100644 --- a/docs/julia.rst +++ b/docs/julia.rst @@ -320,168 +320,169 @@ The following functions must be available: +---------------------------------------------------------------------------------------------------------------+ | *Arithmetics* | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | addu256(x:u256, y:u256) -> z:u256 | x + y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | subu256(x:u256, y:u256) -> z:u256 | x - y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | mulu256(x:u256, y:u256) -> z:u256 | x * y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | divu256(x:u256, y:u256) -> z:u256 | x / y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | divs256(x:s256, y:s256) -> z:s256 | x / y, for signed numbers in two's complement | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | modu256(x:u256, y:u256) -> z:u256 | x % y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | mods256(x:s256, y:s256) -> z:s256 | x % y, for signed numbers in two's complement | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | signextendu256(i:u256, x:u256) -> z:u256 | sign extend from (i*8+7)th bit counting from least significant | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | expu256(x:u256, y:u256) -> z:u256 | x to the power of y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | addmodu256(x:u256, y:u256, m:u256) -> z:u256| (x + y) % m with arbitrary precision arithmetics | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | mulmodu256(x:u256, y:u256, m:u256) -> z:u256| (x * y) % m with arbitrary precision arithmetics | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | ltu256(x:u256, y:u256) -> z:bool | 1 if x < y, 0 otherwise | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | gtu256(x:u256, y:u256) -> z:bool | 1 if x > y, 0 otherwise | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | sltu256(x:s256, y:s256) -> z:bool | 1 if x < y, 0 otherwise, for signed numbers in two's complement | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | sgtu256(x:s256, y:s256) -> z:bool | 1 if x > y, 0 otherwise, for signed numbers in two's complement | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | equ256(x:u256, y:u256) -> z:bool | 1 if x == y, 0 otherwise | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | notu256(x:u256) -> z:u256 | ~x, every bit of x is negated | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | andu256(x:u256, y:u256) -> z:u256 | bitwise and of x and y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | oru256(x:u256, y:u256) -> z:u256 | bitwise or of x and y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | xoru256(x:u256, y:u256) -> z:u256 | bitwise xor of x and y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | shlu256(x:u256, y:u256) -> z:u256 | logical left shift of x by y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | shru256(x:u256, y:u256) -> z:u256 | logical right shift of x by y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | saru256(x:u256, y:u256) -> z:u256 | arithmetic right shift of x by y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | byte(n:u256, x:u256) -> v:u256 | nth byte of x, where the most significant byte is the 0th byte | -| Cannot this be just replaced by and256(shr256(n, x), 0xff) and let it be optimised out by the EVM backend? | -+---------------------------------------------------------------------------------------------------------------+ +| | Cannot this be just replaced by and256(shr256(n, x), 0xff) and | +| | let it be optimised out by the EVM backend? | ++---------------------------------------------+-----------------------------------------------------------------+ | *Memory and storage* | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | mload(p:u256) -> v:u256 | mem[p..(p+32)) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | mstore(p:u256, v:u256) | mem[p..(p+32)) := v | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | mstore8(p:u256, v:u256) | mem[p] := v & 0xff - only modifies a single byte | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | sload(p:u256) -> v:u256 | storage[p] | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | sstore(p:u256, v:u256) | storage[p] := v | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | msize() -> size:u256 | size of memory, i.e. largest accessed memory index, albeit due | | | due to the memory extension function, which extends by words, | | | this will always be a multiple of 32 bytes | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | *Execution control* | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | create(v:u256, p:u256, s:u256) | create new contract with code mem[p..(p+s)) and send v wei | | | and return the new address | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | call(g:u256, a:u256, v:u256, in:u256, | call contract at address a with input mem[in..(in+insize)) | | insize:u256, out:u256, | providing g gas and v wei and output area | | outsize:u256) | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) | | -> r:u256 | and 1 on success | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | callcode(g:u256, a:u256, v:u256, in:u256, | identical to ``call`` but only use the code from a | | insize:u256, out:u256, | and stay in the context of the | | outsize:u256) -> r:u256 | current contract otherwise | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | delegatecall(g:u256, a:u256, in:u256, | identical to ``callcode``, | | insize:u256, out:u256, | but also keep ``caller`` | | outsize:u256) -> r:u256 | and ``callvalue`` | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | stop() | stop execution, identical to return(0,0) | -| Perhaps it would make sense retiring this as it equals to return(0,0). It can be an optimisation by the EVM | -| backend. | -+---------------------------------------------------------------------------------------------------------------+ +| | Perhaps it would make sense retiring this as it equals to | +| | return(0,0). It can be an optimisation by the EVM backend. | ++---------------------------------------------+-----------------------------------------------------------------+ | abort() | abort (equals to invalid instruction on EVM) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | return(p:u256, s:u256) | end execution, return data mem[p..(p+s)) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | revert(p:u256, s:u256) | end execution, revert state changes, return data mem[p..(p+s)) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | selfdestruct(a:u256) | end execution, destroy current contract and send funds to a | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | log0(p:u256, s:u256) | log without topics and data mem[p..(p+s)) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | log1(p:u256, s:u256, t1:u256) | log with topic t1 and data mem[p..(p+s)) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | log2(p:u256, s:u256, t1:u256, t2:u256) | log with topics t1, t2 and data mem[p..(p+s)) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | log3(p:u256, s:u256, t1:u256, t2:u256, | log with topics t, t2, t3 and data mem[p..(p+s)) | | t3:u256) | | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | log4(p:u256, s:u256, t1:u256, t2:u256, | log with topics t1, t2, t3, t4 and data mem[p..(p+s)) | | t3:u256, t4:u256) | | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | *State queries* | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | blockcoinbase() -> address:u256 | current mining beneficiary | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | blockdifficulty() -> difficulty:u256 | difficulty of the current block | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | blockgaslimit() -> limit:u256 | block gas limit of the current block | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | blockhash(b:u256) -> hash:u256 | hash of block nr b - only for last 256 blocks excluding current | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | blocknumber() -> block:u256 | current block number | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | blocktimestamp() -> timestamp:u256 | timestamp of the current block in seconds since the epoch | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | txorigin() -> address:u256 | transaction sender | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | txgasprice() -> price:u256 | gas price of the transaction | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | gasleft() -> gas:u256 | gas still available to execution | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | balance(a:u256) -> v:u256 | wei balance at address a | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | this() -> address:u256 | address of the current contract / execution context | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | caller() -> address:u256 | call sender (excluding delegatecall) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | callvalue() -> v:u256 | wei sent together with the current call | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | calldataload(p:u256) -> v:u256 | call data starting from position p (32 bytes) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | calldatasize() -> v:u256 | size of call data in bytes | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | calldatacopy(t:u256, f:u256, s:u256) | copy s bytes from calldata at position f to mem at position t | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | codesize() -> size:u256 | size of the code of the current contract / execution context | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | codecopy(t:u256, f:u256, s:u256) | copy s bytes from code at position f to mem at position t | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | extcodesize(a:u256) -> size:u256 | size of the code at address a | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | extcodecopy(a:u256, t:u256, f:u256, s:u256) | like codecopy(t, f, s) but take code at address a | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | *Others* | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | discardu256(unused:u256) | discard value | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | splitu256tou64(x:u256) -> (x1:u64, x2:u64, | split u256 to four u64's | | x3:u64, x4:u64) | | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | combineu64tou256(x1:u64, x2:u64, x3:u64, | combine four u64's into a single u256 | | x4:u64) -> (x:u256) | | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | sha3(p:u256, s:u256) -> v:u256 | keccak(mem[p...(p+s))) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ Backends -------- -- cgit v1.2.3 From 59e6ea601b90577e39fe22a4c504f09e862dc40e Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 6 Feb 2017 22:14:17 +0000 Subject: Add bitwise shift operators to libevmasm (EIP145) --- libevmasm/Instruction.cpp | 10 ++++++++++ libevmasm/Instruction.h | 7 ++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index b38981d2..c25c9653 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -50,6 +50,11 @@ const std::map dev::solidity::c_instructions = { "OR", Instruction::OR }, { "XOR", Instruction::XOR }, { "BYTE", Instruction::BYTE }, + { "SHL", Instruction::SHL }, + { "SHR", Instruction::SHR }, + { "SAR", Instruction::SAR }, + { "ROL", Instruction::ROL }, + { "ROR", Instruction::ROR }, { "ADDMOD", Instruction::ADDMOD }, { "MULMOD", Instruction::MULMOD }, { "SIGNEXTEND", Instruction::SIGNEXTEND }, @@ -190,6 +195,11 @@ static const std::map c_instructionInfo = { Instruction::OR, { "OR", 0, 2, 1, false, Tier::VeryLow } }, { Instruction::XOR, { "XOR", 0, 2, 1, false, Tier::VeryLow } }, { Instruction::BYTE, { "BYTE", 0, 2, 1, false, Tier::VeryLow } }, + { Instruction::SHL, { "SHL", 0, 2, 1, false, Tier::VeryLow } }, + { Instruction::SHR, { "SHR", 0, 2, 1, false, Tier::VeryLow } }, + { Instruction::SAR, { "SAR", 0, 2, 1, false, Tier::VeryLow } }, + { Instruction::ROL, { "ROL", 0, 2, 1, false, Tier::VeryLow } }, + { Instruction::ROR, { "ROR", 0, 2, 1, false, Tier::VeryLow } }, { Instruction::ADDMOD, { "ADDMOD", 0, 3, 1, false, Tier::Mid } }, { Instruction::MULMOD, { "MULMOD", 0, 3, 1, false, Tier::Mid } }, { Instruction::SIGNEXTEND, { "SIGNEXTEND", 0, 2, 1, false, Tier::Low } }, diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index d9c53900..e56d4c9a 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -59,8 +59,13 @@ enum class Instruction: uint8_t AND, ///< bitwise AND operation OR, ///< bitwise OR operation XOR, ///< bitwise XOR operation - NOT, ///< bitwise NOT opertation + NOT, ///< bitwise NOT operation BYTE, ///< retrieve single byte from word + SHL, ///< bitwise SHL operation + SHR, ///< bitwise SHR operation + SAR, ///< bitwise SAR operation + ROL, ///< bitwise ROL operation + ROR, ///< bitwise ROR operation KECCAK256 = 0x20, ///< compute KECCAK-256 hash -- cgit v1.2.3 From 6a83beaab52e0601c1e7654d27a8e72f53b77182 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 26 Feb 2018 20:41:18 +0100 Subject: Run tests in parallel on circle. --- scripts/tests.sh | 100 ++++++++++++++++++++++++++++----------------------- test/cmdlineTests.sh | 2 +- 2 files changed, 57 insertions(+), 45 deletions(-) diff --git a/scripts/tests.sh b/scripts/tests.sh index d414643b..3c80adc5 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -45,54 +45,66 @@ else fi echo "Running commandline tests..." -"$REPO_ROOT/test/cmdlineTests.sh" +"$REPO_ROOT/test/cmdlineTests.sh" & +CMDLINE_PID=$! +# Only run in parallel if this is run on CI infrastructure +if [ -z "$CI" ] +then + wait $CMDLINE_PID +fi -# This conditional is only needed because we don't have a working Homebrew -# install for `eth` at the time of writing, so we unzip the ZIP file locally -# instead. This will go away soon. -if [[ "$OSTYPE" == "darwin"* ]]; then - ETH_PATH="$REPO_ROOT/eth" -elif [ -z $CI ]; then - ETH_PATH="eth" -else - mkdir -p /tmp/test - ETH_BINARY=eth_byzantium_artful - ETH_HASH="e527dd3e3dc17b983529dd7dcfb74a0d3a5aed4e" - if grep -i trusty /etc/lsb-release >/dev/null 2>&1 - then - ETH_BINARY=eth_byzantium2 - ETH_HASH="4dc3f208475f622be7c8e53bee720e14cd254c6f" +function download_eth() +{ + if [[ "$OSTYPE" == "darwin"* ]]; then + ETH_PATH="$REPO_ROOT/eth" + elif [ -z $CI ]; then + ETH_PATH="eth" + else + mkdir -p /tmp/test + ETH_BINARY=eth_byzantium_artful + ETH_HASH="e527dd3e3dc17b983529dd7dcfb74a0d3a5aed4e" + if grep -i trusty /etc/lsb-release >/dev/null 2>&1 + then + ETH_BINARY=eth_byzantium2 + ETH_HASH="4dc3f208475f622be7c8e53bee720e14cd254c6f" + fi + wget -q -O /tmp/test/eth https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/$ETH_BINARY + test "$(shasum /tmp/test/eth)" = "$ETH_HASH /tmp/test/eth" + sync + chmod +x /tmp/test/eth + sync # Otherwise we might get a "text file busy" error + ETH_PATH="/tmp/test/eth" fi - wget -q -O /tmp/test/eth https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/$ETH_BINARY - test "$(shasum /tmp/test/eth)" = "$ETH_HASH /tmp/test/eth" - sync - chmod +x /tmp/test/eth - sync # Otherwise we might get a "text file busy" error - ETH_PATH="/tmp/test/eth" -fi -# This trailing ampersand directs the shell to run the command in the background, -# that is, it is forked and run in a separate sub-shell, as a job, -# asynchronously. The shell will immediately return the return status of 0 for -# true and continue as normal, either processing further commands in a script -# or returning the cursor focus back to the user in a Linux terminal. -$ETH_PATH --test -d /tmp/test & -ETH_PID=$! +} + +# $1: data directory +# echos the PID +function run_eth() +{ + $ETH_PATH --test -d "$1" >/dev/null 2>&1 & + echo $! + # Wait until the IPC endpoint is available. + while [ ! -S "$1"/geth.ipc ] ; do sleep 1; done + sleep 2 +} + +download_eth +ETH_PID=$(run_eth /tmp/test) + +progress="--show-progress" +if [ "$CI" ] +then + progress="" +fi -# Wait until the IPC endpoint is available. That won't be available instantly. -# The node needs to get a little way into its startup sequence before the IPC -# is available and is ready for the unit-tests to start talking to it. -while [ ! -S /tmp/test/geth.ipc ]; do sleep 2; done -echo "--> IPC available." -sleep 2 -# And then run the Solidity unit-tests (once without optimization, once with), -# pointing to that IPC endpoint. echo "--> Running tests without optimizer..." - "$REPO_ROOT"/build/test/soltest --show-progress $testargs_no_opt -- --ipcpath /tmp/test/geth.ipc && \ - echo "--> Running tests WITH optimizer..." && \ - "$REPO_ROOT"/build/test/soltest --show-progress $testargs_opt -- --optimize --ipcpath /tmp/test/geth.ipc -ERROR_CODE=$? +"$REPO_ROOT"/build/test/soltest $testargs_no_opt $progress -- --ipcpath /tmp/test/geth.ipc +echo "--> Running tests WITH optimizer..." +"$REPO_ROOT"/build/test/soltest $testargs_opt $progress -- --optimize --ipcpath /tmp/test/geth.ipc + +wait $CMDLINE_PID + pkill "$ETH_PID" || true sleep 4 -pgrep "$ETH_PID" && pkill -9 "$ETH_PID" || true -exit $ERROR_CODE +pgrep "$ETH_PID" && pkill -9 "$ETH_PID" || true \ No newline at end of file diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index a249b601..32456fd0 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -172,4 +172,4 @@ TMPDIR=$(mktemp -d) done ) rm -rf "$TMPDIR" -echo "Done." +echo "Commandline tests successful." -- cgit v1.2.3 From 53289e15a2e1ea540a0c3abe28219c326a614fe5 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 9 Feb 2018 15:09:22 +0100 Subject: Make all lookups recursive by default. --- libsolidity/analysis/NameAndTypeResolver.cpp | 8 ++++---- libsolidity/analysis/NameAndTypeResolver.h | 13 +++++++------ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 662792a3..2693eb97 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -159,15 +159,15 @@ vector NameAndTypeResolver::resolveName(ASTString const& _na return iterator->second->resolveName(_name, false); } -vector NameAndTypeResolver::nameFromCurrentScope(ASTString const& _name, bool _recursive) const +vector NameAndTypeResolver::nameFromCurrentScope(ASTString const& _name) const { - return m_currentScope->resolveName(_name, _recursive); + return m_currentScope->resolveName(_name, true); } -Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector const& _path, bool _recursive) const +Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector const& _path) const { solAssert(!_path.empty(), ""); - vector candidates = m_currentScope->resolveName(_path.front(), _recursive); + vector candidates = m_currentScope->resolveName(_path.front(), true); for (size_t i = 1; i < _path.size() && candidates.size() == 1; i++) { if (!m_scopes.count(candidates.front())) diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index 9aea07ab..35f5d31e 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -73,16 +73,17 @@ public: /// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted, /// the global scope is used (i.e. the one containing only the pre-defined global variables). /// @returns a pointer to the declaration on success or nullptr on failure. + /// SHOULD only be used for testing. std::vector resolveName(ASTString const& _name, ASTNode const* _scope = nullptr) const; - /// Resolves a name in the "current" scope. Should only be called during the initial - /// resolving phase. - std::vector nameFromCurrentScope(ASTString const& _name, bool _recursive = true) const; + /// Resolves a name in the "current" scope, but also searches parent scopes. + /// Should only be called during the initial resolving phase. + std::vector nameFromCurrentScope(ASTString const& _name) const; - /// Resolves a path starting from the "current" scope. Should only be called during the initial - /// resolving phase. + /// Resolves a path starting from the "current" scope, but also searches parent scopes. + /// Should only be called during the initial resolving phase. /// @note Returns a null pointer if any component in the path was not unique or not found. - Declaration const* pathFromCurrentScope(std::vector const& _path, bool _recursive = true) const; + Declaration const* pathFromCurrentScope(std::vector const& _path) const; /// returns the vector of declarations without repetitions std::vector cleanedDeclarations( -- cgit v1.2.3 From 5f20129e65f5b8b714189145d177067152a21ac1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 9 Feb 2018 16:53:25 +0100 Subject: Scopes do not have to be declarations. --- libsolidity/analysis/NameAndTypeResolver.cpp | 29 ++++++++++++++++++---------- libsolidity/analysis/NameAndTypeResolver.h | 5 ++++- libsolidity/ast/AST.cpp | 11 ++++++----- libsolidity/ast/AST.h | 26 +++++++++++++++++-------- 4 files changed, 47 insertions(+), 24 deletions(-) diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 2693eb97..953b788d 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -244,19 +244,24 @@ void NameAndTypeResolver::warnVariablesNamedLikeInstructions() } } +void NameAndTypeResolver::setScope(ASTNode const* _node) +{ + m_currentScope = m_scopes[_node].get(); +} + bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode) { if (ContractDefinition* contract = dynamic_cast(&_node)) { bool success = true; - m_currentScope = m_scopes[contract->scope()].get(); + setScope(contract->scope()); solAssert(!!m_currentScope, ""); for (ASTPointer const& baseContract: contract->baseContracts()) if (!resolveNamesAndTypes(*baseContract, true)) success = false; - m_currentScope = m_scopes[contract].get(); + setScope(contract); if (success) { @@ -273,7 +278,7 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res // these can contain code, only resolve parameters for now for (ASTPointer const& node: contract->subNodes()) { - m_currentScope = m_scopes[contract].get(); + setScope(contract); if (!resolveNamesAndTypes(*node, false)) { success = false; @@ -287,12 +292,12 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res if (!_resolveInsideCode) return success; - m_currentScope = m_scopes[contract].get(); + setScope(contract); // now resolve references inside the code for (ASTPointer const& node: contract->subNodes()) { - m_currentScope = m_scopes[contract].get(); + setScope(contract); if (!resolveNamesAndTypes(*node, true)) success = false; } @@ -301,7 +306,7 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res else { if (m_scopes.count(&_node)) - m_currentScope = m_scopes[&_node].get(); + setScope(&_node); return ReferencesResolver(m_errorReporter, *this, _resolveInsideCode).resolve(_node); } } @@ -632,14 +637,17 @@ void DeclarationRegistrationHelper::endVisit(EventDefinition&) closeCurrentScope(); } -void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration) +void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _subScope) { + if (auto s = dynamic_cast(&_subScope)) + s->setScope(m_currentScope); + map>::iterator iter; bool newlyAdded; shared_ptr container(new DeclarationContainer(m_currentScope, m_scopes[m_currentScope].get())); - tie(iter, newlyAdded) = m_scopes.emplace(&_declaration, move(container)); + tie(iter, newlyAdded) = m_scopes.emplace(&_subScope, move(container)); solAssert(newlyAdded, "Unable to add new scope."); - m_currentScope = &_declaration; + m_currentScope = &_subScope; } void DeclarationRegistrationHelper::closeCurrentScope() @@ -669,9 +677,10 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, warnAboutShadowing, m_errorReporter); - _declaration.setScope(m_currentScope); if (_opensScope) enterNewSubScope(_declaration); + else + _declaration.setScope(m_currentScope); } string DeclarationRegistrationHelper::currentCanonicalName() const diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index 35f5d31e..d1fbe53c 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -97,6 +97,9 @@ public: /// @returns a list of similar identifiers in the current and enclosing scopes. May return empty string if no suggestions. std::string similarNameSuggestions(ASTString const& _name) const; + /// Sets the current scope. + void setScope(ASTNode const* _node); + private: /// Internal version of @a resolveNamesAndTypes (called from there) throws exceptions on fatal errors. bool resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode = true); @@ -169,7 +172,7 @@ private: bool visit(EventDefinition& _event) override; void endVisit(EventDefinition& _event) override; - void enterNewSubScope(Declaration const& _declaration); + void enterNewSubScope(ASTNode& _subScope); void closeCurrentScope(); void registerDeclaration(Declaration& _declaration, bool _opensScope); diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 8da6964e..af007908 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -98,11 +98,12 @@ set SourceUnit::referencedSourceUnits(bool _recurse, set(scope) && dynamic_cast(scope)->m_scope) - scope = dynamic_cast(scope)->m_scope; - return dynamic_cast(*scope); + ASTNode const* s = scope(); + solAssert(s, ""); + // will not always be a declaratoion + while (dynamic_cast(s) && dynamic_cast(s)->scope()) + s = dynamic_cast(s)->scope(); + return dynamic_cast(*s); } string Declaration::sourceUnitName() const diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index c0d55aec..a0089d64 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -139,10 +139,26 @@ private: std::vector> m_nodes; }; +/** + * Abstract class that is added to each AST node that is stored inside a scope + * (including scopes). + */ +class Scopable +{ +public: + /// @returns the scope this declaration resides in. Can be nullptr if it is the global scope. + /// Available only after name and type resolution step. + ASTNode const* scope() const { return m_scope; } + void setScope(ASTNode const* _scope) { m_scope = _scope; } + +protected: + ASTNode const* m_scope = nullptr; +}; + /** * Abstract AST class for a declaration (contract, function, struct, variable, import directive). */ -class Declaration: public ASTNode +class Declaration: public ASTNode, public Scopable { public: /// Visibility ordered from restricted to unrestricted. @@ -171,7 +187,7 @@ public: ASTPointer const& _name, Visibility _visibility = Visibility::Default ): - ASTNode(_location), m_name(_name), m_visibility(_visibility), m_scope(nullptr) {} + ASTNode(_location), m_name(_name), m_visibility(_visibility) {} /// @returns the declared name. ASTString const& name() const { return *m_name; } @@ -181,11 +197,6 @@ public: virtual bool isVisibleInContract() const { return visibility() != Visibility::External; } bool isVisibleInDerivedContracts() const { return isVisibleInContract() && visibility() >= Visibility::Internal; } - /// @returns the scope this declaration resides in. Can be nullptr if it is the global scope. - /// Available only after name and type resolution step. - ASTNode const* scope() const { return m_scope; } - void setScope(ASTNode const* _scope) { m_scope = _scope; } - /// @returns the source unit this declaration is present in. SourceUnit const& sourceUnit() const; @@ -213,7 +224,6 @@ protected: private: ASTPointer m_name; Visibility m_visibility; - ASTNode const* m_scope; }; /** -- cgit v1.2.3 From e6d48bb72a82e2b3140d5a98da2961e401430a25 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 9 Feb 2018 16:53:52 +0100 Subject: Blocks and for loops can be scopes. --- libsolidity/analysis/NameAndTypeResolver.cpp | 24 +++++++++++++++++++++ libsolidity/analysis/NameAndTypeResolver.h | 4 ++++ libsolidity/analysis/ReferencesResolver.cpp | 31 ++++++++++++++++++++++++++++ libsolidity/analysis/ReferencesResolver.h | 5 ++++- libsolidity/ast/AST.cpp | 12 +++++++++-- libsolidity/ast/AST.h | 8 ++++--- 6 files changed, 78 insertions(+), 6 deletions(-) diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 953b788d..1566df94 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -610,6 +610,30 @@ void DeclarationRegistrationHelper::endVisit(ModifierDefinition&) closeCurrentScope(); } +bool DeclarationRegistrationHelper::visit(Block& _block) +{ + enterNewSubScope(_block); + return true; +} + +void DeclarationRegistrationHelper::endVisit(Block&) +{ + closeCurrentScope(); +} + +bool DeclarationRegistrationHelper::visit(ForStatement& _for) +{ + // TODO special scoping rules for the init statement - if it is a block, then it should + // not open its own scope. + enterNewSubScope(_for); + return true; +} + +void DeclarationRegistrationHelper::endVisit(ForStatement&) +{ + closeCurrentScope(); +} + void DeclarationRegistrationHelper::endVisit(VariableDeclarationStatement& _variableDeclarationStatement) { // Register the local variables with the function diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index d1fbe53c..7a6e813e 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -167,6 +167,10 @@ private: void endVisit(FunctionDefinition& _function) override; bool visit(ModifierDefinition& _modifier) override; void endVisit(ModifierDefinition& _modifier) override; + bool visit(Block& _block) override; + void endVisit(Block& _block) override; + bool visit(ForStatement& _forLoop) override; + void endVisit(ForStatement& _forLoop) override; void endVisit(VariableDeclarationStatement& _variableDeclarationStatement) override; bool visit(VariableDeclaration& _declaration) override; bool visit(EventDefinition& _event) override; diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 0bb5e3fe..bee42e77 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -43,6 +43,37 @@ bool ReferencesResolver::resolve(ASTNode const& _root) return !m_errorOccurred; } +bool ReferencesResolver::visit(Block const& _block) +{ + if (!m_resolveInsideCode) + return false; + m_resolver.setScope(&_block); + return true; +} + +void ReferencesResolver::endVisit(Block const& _block) +{ + if (!m_resolveInsideCode) + return; + + m_resolver.setScope(_block.scope()); +} + +bool ReferencesResolver::visit(ForStatement const& _for) +{ + if (!m_resolveInsideCode) + return false; + m_resolver.setScope(&_for); + return true; +} + +void ReferencesResolver::endVisit(ForStatement const& _for) +{ + if (!m_resolveInsideCode) + return; + m_resolver.setScope(_for.scope()); +} + bool ReferencesResolver::visit(Identifier const& _identifier) { auto declarations = m_resolver.nameFromCurrentScope(_identifier.name()); diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h index fef2e73f..5bfd6c5a 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -57,7 +57,10 @@ public: bool resolve(ASTNode const& _root); private: - virtual bool visit(Block const&) override { return m_resolveInsideCode; } + virtual bool visit(Block const& _block) override; + virtual void endVisit(Block const& _block) override; + virtual bool visit(ForStatement const& _for) override; + virtual void endVisit(ForStatement const& _for) override; virtual bool visit(Identifier const& _identifier) override; virtual bool visit(ElementaryTypeName const& _typeName) override; virtual bool visit(FunctionDefinition const& _functionDefinition) override; diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index af007908..60a15aeb 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -415,6 +415,15 @@ bool VariableDeclaration::isLValue() const return !isExternalCallableParameter() && !m_isConstant; } +bool VariableDeclaration::isLocalVariable() const +{ + auto s = scope(); + return + dynamic_cast(s) || + dynamic_cast(s) || + dynamic_cast(s); +} + bool VariableDeclaration::isCallableParameter() const { auto const* callable = dynamic_cast(scope()); @@ -460,8 +469,7 @@ bool VariableDeclaration::isExternalCallableParameter() const bool VariableDeclaration::canHaveAutoType() const { - auto const* callable = dynamic_cast(scope()); - return (!!callable && !isCallableParameter()); + return isLocalVariable() && !isCallableParameter(); } TypePointer VariableDeclaration::type() const diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index a0089d64..1e4c6591 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -299,6 +299,8 @@ private: /** * Abstract class that is added to each AST node that can store local variables. + * Local variables in functions are always added to functions, even though they are not + * in scope for the whole function. */ class VariableScope { @@ -672,7 +674,7 @@ public: virtual bool isLValue() const override; virtual bool isPartOfExternalInterface() const override { return isPublic(); } - bool isLocalVariable() const { return !!dynamic_cast(scope()); } + bool isLocalVariable() const; /// @returns true if this variable is a parameter or return parameter of a function. bool isCallableParameter() const; /// @returns true if this variable is a return parameter of a function. @@ -1014,7 +1016,7 @@ private: /** * Brace-enclosed block containing zero or more statements. */ -class Block: public Statement +class Block: public Statement, public Scopable { public: Block( @@ -1121,7 +1123,7 @@ private: /** * For loop statement */ -class ForStatement: public BreakableStatement +class ForStatement: public BreakableStatement, public Scopable { public: ForStatement( -- cgit v1.2.3 From 69f5f2eb65969c8aa1ee0438601c0a0938f2d0a5 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 9 Feb 2018 16:54:08 +0100 Subject: Adjust tests. --- test/libsolidity/SMTChecker.cpp | 6 ++++-- test/libsolidity/SolidityExpressionCompiler.cpp | 19 +++++++++++-------- test/libsolidity/SolidityNameAndTypeResolution.cpp | 12 ++++++++---- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp index 5088ab94..12b5f439 100644 --- a/test/libsolidity/SMTChecker.cpp +++ b/test/libsolidity/SMTChecker.cpp @@ -466,7 +466,8 @@ BOOST_AUTO_TEST_CASE(for_loop) text = R"( contract C { function f(uint x) public pure { - for (uint y = 2; x < 10; ) { + uint y; + for (y = 2; x < 10; ) { y = 3; } assert(y == 3); @@ -477,7 +478,8 @@ BOOST_AUTO_TEST_CASE(for_loop) text = R"( contract C { function f(uint x) public pure { - for (uint y = 2; x < 10; ) { + uint y; + for (y = 2; x < 10; ) { y = 3; } assert(y == 2); diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 67747386..e2a0c3cd 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -322,10 +322,10 @@ BOOST_AUTO_TEST_CASE(arithmetics) { char const* sourceCode = R"( contract test { - function f(uint y) { var x = ((((((((y ^ 8) & 7) | 6) - 5) + 4) % 3) / 2) * 1); } + function f(uint y) { ((((((((y ^ 8) & 7) | 6) - 5) + 4) % 3) / 2) * 1); } } )"; - bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}, {"test", "f", "x"}}); + bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}}); bytes expectation({byte(Instruction::PUSH1), 0x1, byte(Instruction::PUSH1), 0x2, byte(Instruction::PUSH1), 0x3, @@ -334,7 +334,7 @@ BOOST_AUTO_TEST_CASE(arithmetics) byte(Instruction::PUSH1), 0x6, byte(Instruction::PUSH1), 0x7, byte(Instruction::PUSH1), 0x8, - byte(Instruction::DUP10), + byte(Instruction::DUP9), byte(Instruction::XOR), byte(Instruction::AND), byte(Instruction::OR), @@ -364,13 +364,13 @@ BOOST_AUTO_TEST_CASE(unary_operators) { char const* sourceCode = R"( contract test { - function f(int y) { var x = !(~+- y == 2); } + function f(int y) { !(~+- y == 2); } } )"; - bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}, {"test", "f", "x"}}); + bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}}); bytes expectation({byte(Instruction::PUSH1), 0x2, - byte(Instruction::DUP3), + byte(Instruction::DUP2), byte(Instruction::PUSH1), 0x0, byte(Instruction::SUB), byte(Instruction::NOT), @@ -383,7 +383,7 @@ BOOST_AUTO_TEST_CASE(unary_inc_dec) { char const* sourceCode = R"( contract test { - function f(uint a) { var x = --a ^ (a-- ^ (++a ^ a++)); } + function f(uint a) returns (uint x) { x = --a ^ (a-- ^ (++a ^ a++)); } } )"; bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "x"}}); @@ -426,7 +426,10 @@ BOOST_AUTO_TEST_CASE(unary_inc_dec) byte(Instruction::POP), // second ++ // Stack here: a x a^(a+2)^(a+2) byte(Instruction::DUP3), // will change - byte(Instruction::XOR)}); + byte(Instruction::XOR), + byte(Instruction::SWAP1), + byte(Instruction::POP), + byte(Instruction::DUP1)}); // Stack here: a x a^(a+2)^(a+2)^a BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index be147e48..a8adde1e 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -76,15 +76,19 @@ BOOST_AUTO_TEST_CASE(double_function_declaration) BOOST_AUTO_TEST_CASE(double_variable_declaration) { - char const* text = R"( + string text = R"( contract test { - function f() public { + function f() pure public { uint256 x; if (true) { uint256 x; } } } )"; - CHECK_ERROR(text, DeclarationError, "Identifier already declared."); + CHECK_WARNING_ALLOW_MULTI(text, (vector{ + "This declaration shadows an existing declaration.", + "Unused local variable", + "Unused local variable" + })); } BOOST_AUTO_TEST_CASE(name_shadowing) @@ -1043,7 +1047,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables) modifier mod(uint a) { if (a > 0) _; } } )"; - CHECK_SUCCESS(text); + CHECK_ERROR(text, DeclarationError, "Undeclared identifier."); } BOOST_AUTO_TEST_CASE(function_modifier_double_invocation) -- cgit v1.2.3 From 000599038202154afe62b144a44dd81178fc539c Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 9 Feb 2018 16:54:13 +0100 Subject: Tests for new scoping behaviour. --- test/libsolidity/SolidityNameAndTypeResolution.cpp | 44 ++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index a8adde1e..be042655 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -91,6 +91,50 @@ BOOST_AUTO_TEST_CASE(double_variable_declaration) })); } +BOOST_AUTO_TEST_CASE(scoping) +{ + char const* text = R"( + contract test { + function f() public { + { + uint256 x; + } + x = 2; + } + } + )"; + CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); +} + +BOOST_AUTO_TEST_CASE(scoping_for) +{ + char const* text = R"( + contract test { + function f() public { + for (uint x = 0; x < 10; x ++){ + x = 2; + } + } + } + )"; + CHECK_SUCCESS(text); +} + +BOOST_AUTO_TEST_CASE(scoping_for2) +{ + char const* text = R"( + contract test { + function f() public { + for (uint x = 0; x < 10; x ++){ + x = 2; + } + x = 4; + } + } + )"; + CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); +} + BOOST_AUTO_TEST_CASE(name_shadowing) { char const* text = R"( -- cgit v1.2.3 From e227bdbfa7157d2675c177c75d44e13f0bd82214 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Feb 2018 08:11:36 +0100 Subject: Changelog entry. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index acd3aa54..db4f9786 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.4.21 (unreleased) Features: + * C99/C++-style scoping rules (instead of JavaScript function scoping) take effect as experimental v0.5.0 feature. * Code Generator: Assert that ``k != 0`` for ``molmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature. * Standard JSON: Reject badly formatted invalid JSON inputs. * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. -- cgit v1.2.3 From 6b9dda06f3d85344a70efb2f868760a0dde9dc45 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 14 Feb 2018 01:48:40 +0100 Subject: Enable C99-scoping with the 0.5.0-experimental pragma. --- libsolidity/analysis/NameAndTypeResolver.cpp | 30 ++++++++------ libsolidity/analysis/ReferencesResolver.cpp | 17 ++++++-- libsolidity/analysis/ReferencesResolver.h | 1 + libsolidity/ast/AST.cpp | 30 +++++++------- libsolidity/ast/AST.h | 13 +++--- test/libsolidity/SolidityNameAndTypeResolution.cpp | 47 +++++++++++++++++++++- 6 files changed, 98 insertions(+), 40 deletions(-) diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 1566df94..40021771 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -612,26 +612,34 @@ void DeclarationRegistrationHelper::endVisit(ModifierDefinition&) bool DeclarationRegistrationHelper::visit(Block& _block) { - enterNewSubScope(_block); + _block.setScope(m_currentScope); + // Enable C99-scoped variables. + if (_block.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + enterNewSubScope(_block); return true; } -void DeclarationRegistrationHelper::endVisit(Block&) +void DeclarationRegistrationHelper::endVisit(Block& _block) { - closeCurrentScope(); + // Enable C99-scoped variables. + if (_block.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + closeCurrentScope(); } bool DeclarationRegistrationHelper::visit(ForStatement& _for) { - // TODO special scoping rules for the init statement - if it is a block, then it should - // not open its own scope. - enterNewSubScope(_for); + _for.setScope(m_currentScope); + if (_for.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + // TODO special scoping rules for the init statement - if it is a block, then it should + // not open its own scope. + enterNewSubScope(_for); return true; } -void DeclarationRegistrationHelper::endVisit(ForStatement&) +void DeclarationRegistrationHelper::endVisit(ForStatement& _for) { - closeCurrentScope(); + if (_for.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + closeCurrentScope(); } void DeclarationRegistrationHelper::endVisit(VariableDeclarationStatement& _variableDeclarationStatement) @@ -663,9 +671,6 @@ void DeclarationRegistrationHelper::endVisit(EventDefinition&) void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _subScope) { - if (auto s = dynamic_cast(&_subScope)) - s->setScope(m_currentScope); - map>::iterator iter; bool newlyAdded; shared_ptr container(new DeclarationContainer(m_currentScope, m_scopes[m_currentScope].get())); @@ -701,10 +706,9 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, warnAboutShadowing, m_errorReporter); + _declaration.setScope(m_currentScope); if (_opensScope) enterNewSubScope(_declaration); - else - _declaration.setScope(m_currentScope); } string DeclarationRegistrationHelper::currentCanonicalName() const diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index bee42e77..4d919f2b 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -47,7 +47,10 @@ bool ReferencesResolver::visit(Block const& _block) { if (!m_resolveInsideCode) return false; - m_resolver.setScope(&_block); + m_experimental050Mode = _block.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); + // C99-scoped variables + if (m_experimental050Mode) + m_resolver.setScope(&_block); return true; } @@ -56,14 +59,19 @@ void ReferencesResolver::endVisit(Block const& _block) if (!m_resolveInsideCode) return; - m_resolver.setScope(_block.scope()); + // C99-scoped variables + if (m_experimental050Mode) + m_resolver.setScope(_block.scope()); } bool ReferencesResolver::visit(ForStatement const& _for) { if (!m_resolveInsideCode) return false; - m_resolver.setScope(&_for); + m_experimental050Mode = _for.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); + // C99-scoped variables + if (m_experimental050Mode) + m_resolver.setScope(&_for); return true; } @@ -71,7 +79,8 @@ void ReferencesResolver::endVisit(ForStatement const& _for) { if (!m_resolveInsideCode) return; - m_resolver.setScope(_for.scope()); + if (m_experimental050Mode) + m_resolver.setScope(_for.scope()); } bool ReferencesResolver::visit(Identifier const& _identifier) diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h index 5bfd6c5a..ab7c987e 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -93,6 +93,7 @@ private: std::vector m_returnParameters; bool const m_resolveInsideCode; bool m_errorOccurred = false; + bool m_experimental050Mode = false; }; } diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 60a15aeb..27220b1f 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -96,21 +96,6 @@ set SourceUnit::referencedSourceUnits(bool _recurse, set(s) && dynamic_cast(s)->scope()) - s = dynamic_cast(s)->scope(); - return dynamic_cast(*s); -} - -string Declaration::sourceUnitName() const -{ - return sourceUnit().annotation().path; -} - ImportAnnotation& ImportDirective::annotation() const { if (!m_annotation) @@ -409,6 +394,21 @@ UserDefinedTypeNameAnnotation& UserDefinedTypeName::annotation() const return dynamic_cast(*m_annotation); } +SourceUnit const& Scopable::sourceUnit() const +{ + ASTNode const* s = scope(); + solAssert(s, ""); + // will not always be a declaratoion + while (dynamic_cast(s) && dynamic_cast(s)->scope()) + s = dynamic_cast(s)->scope(); + return dynamic_cast(*s); +} + +string Scopable::sourceUnitName() const +{ + return sourceUnit().annotation().path; +} + bool VariableDeclaration::isLValue() const { // External function parameters and constant declared variables are Read-Only diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 1e4c6591..863ad2fe 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -151,6 +151,13 @@ public: ASTNode const* scope() const { return m_scope; } void setScope(ASTNode const* _scope) { m_scope = _scope; } + /// @returns the source unit this scopable is present in. + SourceUnit const& sourceUnit() const; + + /// @returns the source name this scopable is present in. + /// Can be combined with annotation().canonicalName (if present) to form a globally unique name. + std::string sourceUnitName() const; + protected: ASTNode const* m_scope = nullptr; }; @@ -197,12 +204,6 @@ public: virtual bool isVisibleInContract() const { return visibility() != Visibility::External; } bool isVisibleInDerivedContracts() const { return isVisibleInContract() && visibility() >= Visibility::Internal; } - /// @returns the source unit this declaration is present in. - SourceUnit const& sourceUnit() const; - - /// @returns the source name this declaration is present in. - /// Can be combined with annotation().canonicalName to form a globally unique name. - std::string sourceUnitName() const; std::string fullyQualifiedName() const { return sourceUnitName() + ":" + name(); } virtual bool isLValue() const { return false; } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index be042655..8586ebf8 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -84,16 +84,45 @@ BOOST_AUTO_TEST_CASE(double_variable_declaration) } } )"; + CHECK_ERROR(text, DeclarationError, "Identifier already declared"); +} + +BOOST_AUTO_TEST_CASE(double_variable_declaration_050) +{ + string text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public { + uint256 x; + if (true) { uint256 x; } + } + } + )"; CHECK_WARNING_ALLOW_MULTI(text, (vector{ "This declaration shadows an existing declaration.", + "Experimental features", "Unused local variable", "Unused local variable" })); } +BOOST_AUTO_TEST_CASE(scoping_old) +{ + char const* text = R"( + contract test { + function f() pure public { + x = 4; + uint256 x = 2; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + BOOST_AUTO_TEST_CASE(scoping) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { function f() public { { @@ -109,6 +138,7 @@ BOOST_AUTO_TEST_CASE(scoping) BOOST_AUTO_TEST_CASE(scoping_for) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { function f() public { for (uint x = 0; x < 10; x ++){ @@ -123,6 +153,7 @@ BOOST_AUTO_TEST_CASE(scoping_for) BOOST_AUTO_TEST_CASE(scoping_for2) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { function f() public { for (uint x = 0; x < 10; x ++){ @@ -1052,7 +1083,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation) { char const* text = R"( contract B { - function f() mod1(2, true) mod2("0123456") public { } + function f() mod1(2, true) mod2("0123456") pure public { } modifier mod1(uint a, bool b) { if (b) _; } modifier mod2(bytes7 a) { while (a == "1234567") _; } } @@ -1087,7 +1118,19 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables) { char const* text = R"( contract B { - function f() mod(x) public { uint x = 7; } + function f() mod(x) pure public { uint x = 7; } + modifier mod(uint a) { if (a > 0) _; } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables050) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract B { + function f() mod(x) pure public { uint x = 7; } modifier mod(uint a) { if (a > 0) _; } } )"; -- cgit v1.2.3 From 88a5c66f4a7efae6d78ce2e04144219656bb9da0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Feb 2018 11:30:32 +0100 Subject: Only active variables at the point of their declaration. --- libsolidity/analysis/DeclarationContainer.cpp | 33 ++++++++-- libsolidity/analysis/DeclarationContainer.h | 6 +- libsolidity/analysis/NameAndTypeResolver.cpp | 54 ++++++++++------ libsolidity/analysis/NameAndTypeResolver.h | 10 ++- libsolidity/analysis/ReferencesResolver.cpp | 10 +++ libsolidity/analysis/ReferencesResolver.h | 1 + test/libsolidity/SolidityEndToEndTest.cpp | 20 ++++++ test/libsolidity/SolidityNameAndTypeResolution.cpp | 75 +++++++++++++++++++++- 8 files changed, 176 insertions(+), 33 deletions(-) diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp index 7508ad9e..c7ba78d6 100644 --- a/libsolidity/analysis/DeclarationContainer.cpp +++ b/libsolidity/analysis/DeclarationContainer.cpp @@ -79,6 +79,17 @@ Declaration const* DeclarationContainer::conflictingDeclaration( return nullptr; } +void DeclarationContainer::activateVariable(ASTString const& _name) +{ + solAssert( + m_invisibleDeclarations.count(_name) && m_invisibleDeclarations.at(_name).size() == 1, + "Tried to activate a non-inactive variable or multiple inactive variables with the same name." + ); + solAssert(m_declarations.count(_name) == 0 || m_declarations.at(_name).empty(), ""); + m_declarations[_name].emplace_back(m_invisibleDeclarations.at(_name).front()); + m_invisibleDeclarations.erase(_name); +} + bool DeclarationContainer::registerDeclaration( Declaration const& _declaration, ASTString const* _name, @@ -106,15 +117,17 @@ bool DeclarationContainer::registerDeclaration( return true; } -vector DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const +vector DeclarationContainer::resolveName(ASTString const& _name, bool _recursive, bool _alsoInvisible) const { solAssert(!_name.empty(), "Attempt to resolve empty name."); - auto result = m_declarations.find(_name); - if (result != m_declarations.end()) - return result->second; - if (_recursive && m_enclosingContainer) - return m_enclosingContainer->resolveName(_name, true); - return vector({}); + vector result; + if (m_declarations.count(_name)) + result = m_declarations.at(_name); + if (_alsoInvisible && m_invisibleDeclarations.count(_name)) + result += m_invisibleDeclarations.at(_name); + if (result.empty() && _recursive && m_enclosingContainer) + result = m_enclosingContainer->resolveName(_name, true, _alsoInvisible); + return result; } vector DeclarationContainer::similarNames(ASTString const& _name) const @@ -129,6 +142,12 @@ vector DeclarationContainer::similarNames(ASTString const& _name) con if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE)) similar.push_back(declarationName); } + for (auto const& declaration: m_invisibleDeclarations) + { + string const& declarationName = declaration.first; + if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE)) + similar.push_back(declarationName); + } if (m_enclosingContainer) similar += m_enclosingContainer->similarNames(_name); diff --git a/libsolidity/analysis/DeclarationContainer.h b/libsolidity/analysis/DeclarationContainer.h index f9b1bda4..e4b3320a 100644 --- a/libsolidity/analysis/DeclarationContainer.h +++ b/libsolidity/analysis/DeclarationContainer.h @@ -51,13 +51,17 @@ public: /// @param _update if true, replaces a potential declaration that is already present /// @returns false if the name was already declared. bool registerDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr, bool _invisible = false, bool _update = false); - std::vector resolveName(ASTString const& _name, bool _recursive = false) const; + std::vector resolveName(ASTString const& _name, bool _recursive = false, bool _alsoInvisible = false) const; ASTNode const* enclosingNode() const { return m_enclosingNode; } DeclarationContainer const* enclosingContainer() const { return m_enclosingContainer; } std::map> const& declarations() const { return m_declarations; } /// @returns whether declaration is valid, and if not also returns previous declaration. Declaration const* conflictingDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr) const; + /// Activates a previously inactive (invisible) variable. To be used in C99 scpoing for + /// VariableDeclarationStatements. + void activateVariable(ASTString const& _name); + /// @returns existing declaration names similar to @a _name. /// Searches this and all parent containers. std::vector similarNames(ASTString const& _name) const; diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 40021771..2f675135 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -50,12 +50,13 @@ NameAndTypeResolver::NameAndTypeResolver( m_scopes[nullptr]->registerDeclaration(*declaration); } -bool NameAndTypeResolver::registerDeclarations(ASTNode& _sourceUnit, ASTNode const* _currentScope) +bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit, ASTNode const* _currentScope) { + bool useC99Scoping = _sourceUnit.annotation().experimentalFeatures.count(ExperimentalFeature::V050); // The helper registers all declarations in m_scopes as a side-effect of its construction. try { - DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, m_errorReporter, _currentScope); + DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, useC99Scoping, m_errorReporter, _currentScope); } catch (FatalError const&) { @@ -106,7 +107,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, maplocation(), true, m_errorReporter + target, *declaration, alias.second.get(), &imp->location(), true, false, m_errorReporter )) error = true; } @@ -114,7 +115,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, mapsecond->declarations()) for (auto const& declaration: nameAndDeclaration.second) if (!DeclarationRegistrationHelper::registerDeclaration( - target, *declaration, &nameAndDeclaration.first, &imp->location(), true, m_errorReporter + target, *declaration, &nameAndDeclaration.first, &imp->location(), true, false, m_errorReporter )) error = true; } @@ -151,6 +152,12 @@ bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration) return true; } +void NameAndTypeResolver::activateVariable(string const& _name) +{ + solAssert(m_currentScope, ""); + m_currentScope->activateVariable(_name); +} + vector NameAndTypeResolver::resolveName(ASTString const& _name, ASTNode const* _scope) const { auto iterator = m_scopes.find(_scope); @@ -159,9 +166,9 @@ vector NameAndTypeResolver::resolveName(ASTString const& _na return iterator->second->resolveName(_name, false); } -vector NameAndTypeResolver::nameFromCurrentScope(ASTString const& _name) const +vector NameAndTypeResolver::nameFromCurrentScope(ASTString const& _name, bool _includeInvisibles) const { - return m_currentScope->resolveName(_name, true); + return m_currentScope->resolveName(_name, true, _includeInvisibles); } Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector const& _path) const @@ -229,7 +236,7 @@ void NameAndTypeResolver::warnVariablesNamedLikeInstructions() for (auto const& instruction: c_instructions) { string const instructionName{boost::algorithm::to_lower_copy(instruction.first)}; - auto declarations = nameFromCurrentScope(instructionName); + auto declarations = nameFromCurrentScope(instructionName, true); for (Declaration const* const declaration: declarations) { solAssert(!!declaration, ""); @@ -439,9 +446,11 @@ string NameAndTypeResolver::similarNameSuggestions(ASTString const& _name) const DeclarationRegistrationHelper::DeclarationRegistrationHelper( map>& _scopes, ASTNode& _astRoot, + bool _useC99Scoping, ErrorReporter& _errorReporter, ASTNode const* _currentScope ): + m_useC99Scoping(_useC99Scoping), m_scopes(_scopes), m_currentScope(_currentScope), m_errorReporter(_errorReporter) @@ -456,6 +465,7 @@ bool DeclarationRegistrationHelper::registerDeclaration( string const* _name, SourceLocation const* _errorLocation, bool _warnOnShadow, + bool _inactive, ErrorReporter& _errorReporter ) { @@ -465,10 +475,13 @@ bool DeclarationRegistrationHelper::registerDeclaration( string name = _name ? *_name : _declaration.name(); Declaration const* shadowedDeclaration = nullptr; if (_warnOnShadow && !name.empty() && _container.enclosingContainer()) - for (auto const* decl: _container.enclosingContainer()->resolveName(name, true)) + for (auto const* decl: _container.enclosingContainer()->resolveName(name, true, true)) shadowedDeclaration = decl; - if (!_container.registerDeclaration(_declaration, _name, !_declaration.isVisibleInContract())) + // We use "invisible" for both inactive variables in blocks and for members invisible in contracts. + // They cannot both be true at the same time. + solAssert(!(_inactive && !_declaration.isVisibleInContract()), ""); + if (!_container.registerDeclaration(_declaration, _name, !_declaration.isVisibleInContract() || _inactive)) { SourceLocation firstDeclarationLocation; SourceLocation secondDeclarationLocation; @@ -613,32 +626,28 @@ void DeclarationRegistrationHelper::endVisit(ModifierDefinition&) bool DeclarationRegistrationHelper::visit(Block& _block) { _block.setScope(m_currentScope); - // Enable C99-scoped variables. - if (_block.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + if (m_useC99Scoping) enterNewSubScope(_block); return true; } -void DeclarationRegistrationHelper::endVisit(Block& _block) +void DeclarationRegistrationHelper::endVisit(Block&) { - // Enable C99-scoped variables. - if (_block.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + if (m_useC99Scoping) closeCurrentScope(); } bool DeclarationRegistrationHelper::visit(ForStatement& _for) { _for.setScope(m_currentScope); - if (_for.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) - // TODO special scoping rules for the init statement - if it is a block, then it should - // not open its own scope. + if (m_useC99Scoping) enterNewSubScope(_for); return true; } -void DeclarationRegistrationHelper::endVisit(ForStatement& _for) +void DeclarationRegistrationHelper::endVisit(ForStatement&) { - if (_for.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + if (m_useC99Scoping) closeCurrentScope(); } @@ -704,7 +713,12 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio if (fun->isConstructor()) warnAboutShadowing = false; - registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, warnAboutShadowing, m_errorReporter); + // Register declaration as inactive if we are in block scope and C99 mode. + bool inactive = + m_useC99Scoping && + (dynamic_cast(m_currentScope) || dynamic_cast(m_currentScope)); + + registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, warnAboutShadowing, inactive, m_errorReporter); _declaration.setScope(m_currentScope); if (_opensScope) diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index 7a6e813e..3d10fbd8 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -56,7 +56,7 @@ public: /// @returns false in case of error. /// @param _currentScope should be nullptr but can be used to inject new declarations into /// existing scopes, used by the snippets feature. - bool registerDeclarations(ASTNode& _sourceUnit, ASTNode const* _currentScope = nullptr); + bool registerDeclarations(SourceUnit& _sourceUnit, ASTNode const* _currentScope = nullptr); /// Applies the effect of import directives. bool performImports(SourceUnit& _sourceUnit, std::map const& _sourceUnits); /// Resolves all names and types referenced from the given AST Node. @@ -69,6 +69,9 @@ public: /// that create their own scope. /// @returns false in case of error. bool updateDeclaration(Declaration const& _declaration); + /// Activates a previously inactive (invisible) variable. To be used in C99 scpoing for + /// VariableDeclarationStatements. + void activateVariable(std::string const& _name); /// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted, /// the global scope is used (i.e. the one containing only the pre-defined global variables). @@ -78,7 +81,7 @@ public: /// Resolves a name in the "current" scope, but also searches parent scopes. /// Should only be called during the initial resolving phase. - std::vector nameFromCurrentScope(ASTString const& _name) const; + std::vector nameFromCurrentScope(ASTString const& _name, bool _includeInvisibles = false) const; /// Resolves a path starting from the "current" scope, but also searches parent scopes. /// Should only be called during the initial resolving phase. @@ -139,6 +142,7 @@ public: DeclarationRegistrationHelper( std::map>& _scopes, ASTNode& _astRoot, + bool _useC99Scoping, ErrorReporter& _errorReporter, ASTNode const* _currentScope = nullptr ); @@ -149,6 +153,7 @@ public: std::string const* _name, SourceLocation const* _errorLocation, bool _warnOnShadow, + bool _inactive, ErrorReporter& _errorReporter ); @@ -185,6 +190,7 @@ private: /// @returns the canonical name of the current scope. std::string currentCanonicalName() const; + bool m_useC99Scoping = false; std::map>& m_scopes; ASTNode const* m_currentScope = nullptr; VariableScope* m_currentFunction = nullptr; diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 4d919f2b..985c44d0 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -83,6 +83,16 @@ void ReferencesResolver::endVisit(ForStatement const& _for) m_resolver.setScope(_for.scope()); } +void ReferencesResolver::endVisit(VariableDeclarationStatement const& _varDeclStatement) +{ + if (!m_resolveInsideCode) + return; + if (m_experimental050Mode) + for (auto const& var: _varDeclStatement.declarations()) + if (var) + m_resolver.activateVariable(var->name()); +} + bool ReferencesResolver::visit(Identifier const& _identifier) { auto declarations = m_resolver.nameFromCurrentScope(_identifier.name()); diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h index ab7c987e..4e8f54b5 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -61,6 +61,7 @@ private: virtual void endVisit(Block const& _block) override; virtual bool visit(ForStatement const& _for) override; virtual void endVisit(ForStatement const& _for) override; + virtual void endVisit(VariableDeclarationStatement const& _varDeclStatement) override; virtual bool visit(Identifier const& _identifier) override; virtual bool visit(ElementaryTypeName const& _typeName) override; virtual bool visit(FunctionDefinition const& _functionDefinition) override; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 3882e4ea..7bae3cd6 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -284,6 +284,26 @@ BOOST_AUTO_TEST_CASE(conditional_expression_functions) ABI_CHECK(callContractFunction("f(bool)", false), encodeArgs(u256(2))); } +BOOST_AUTO_TEST_CASE(C99_scoping_activation) +{ + char const* sourceCode = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public returns (uint) { + uint x = 7; + { + x = 3; // This should still assign to the outer variable + uint x; + x = 4; // This should assign to the new one + } + return x; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), encodeArgs(3)); +} + BOOST_AUTO_TEST_CASE(recursive_calls) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 8586ebf8..e5bd0103 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -135,19 +135,59 @@ BOOST_AUTO_TEST_CASE(scoping) CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); } -BOOST_AUTO_TEST_CASE(scoping_for) +BOOST_AUTO_TEST_CASE(scoping_activation_old) +{ + char const* text = R"( + contract test { + function f() pure public { + x = 3; + uint x; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(scoping_activation) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public { + x = 3; + uint x; + } + } + )"; + CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); +} + +BOOST_AUTO_TEST_CASE(scoping_self_use) { char const* text = R"( pragma experimental "v0.5.0"; contract test { function f() public { + uint a = a; + } + } + )"; + CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); +} + +BOOST_AUTO_TEST_CASE(scoping_for) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public { for (uint x = 0; x < 10; x ++){ x = 2; } } } )"; - CHECK_SUCCESS(text); + CHECK_WARNING(text, "Experimental features"); } BOOST_AUTO_TEST_CASE(scoping_for2) @@ -155,7 +195,21 @@ BOOST_AUTO_TEST_CASE(scoping_for2) char const* text = R"( pragma experimental "v0.5.0"; contract test { - function f() public { + function f() pure public { + for (uint x = 0; x < 10; x ++) + x = 2; + } + } + )"; + CHECK_WARNING(text, "Experimental features"); +} + +BOOST_AUTO_TEST_CASE(scoping_for3) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public { for (uint x = 0; x < 10; x ++){ x = 2; } @@ -166,6 +220,21 @@ BOOST_AUTO_TEST_CASE(scoping_for2) CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); } +BOOST_AUTO_TEST_CASE(scoping_for_decl_in_body) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public { + for (;; y++){ + uint y = 3; + } + } + } + )"; + CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); +} + BOOST_AUTO_TEST_CASE(name_shadowing) { char const* text = R"( -- cgit v1.2.3 From 6391a36a6c71c1e8177358bc3832e32504e73111 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Feb 2018 11:43:53 +0100 Subject: Documentation about new scoping rules. --- docs/control-structures.rst | 61 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 7be92cfa..46e076e5 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -324,7 +324,8 @@ is ``false``. The default value for the ``uint`` or ``int`` types is ``0``. For element will be initialized to the default value corresponding to its type. Finally, for dynamically-sized arrays, ``bytes`` and ``string``, the default value is an empty array or string. -A variable declared anywhere within a function will be in scope for the *entire function*, regardless of where it is declared. +A variable declared anywhere within a function will be in scope for the *entire function*, regardless of where it is declared +(this will change soon, see below). This happens because Solidity inherits its scoping rules from JavaScript. This is in contrast to many languages where variables are only scoped where they are declared until the end of the semantic block. As a result, the following code is illegal and cause the compiler to throw an error, ``Identifier already declared``:: @@ -366,7 +367,9 @@ As a result, the following code is illegal and cause the compiler to throw an er } In addition to this, if a variable is declared, it will be initialized at the beginning of the function to its default value. -As a result, the following code is legal, despite being poorly written:: +As a result, the following code is legal, despite being poorly written: + +:: pragma solidity ^0.4.0; @@ -383,6 +386,60 @@ As a result, the following code is legal, despite being poorly written:: } } +Scoping starting from Version 0.5.0 +----------------------------------- + +Starting from version 0.5.0, Solidity will change to the more widespread scoping rules of C99 +(and many other languages): Variables are visible from the point right after their declaration +until the end of a ``{ }``-block. As an exception to this rule, variables declared in the +initialization part of a for-loop are only visible until the end of the for-loop. + +Variables and other items declared outside of a code block, for example functions, contracts, +user-defined types, etc., do not change their scoping behaviour. This means you can +use state variables before they are declared and call functions recursively. + +These rules are already introduced now as an experimental feature. + +As a consequence, the following examples will compile without warnings, since +the two variables have the same name but disjoint scopes. In non-0.5.0-mode, +they have the same scope (the function ``minimalScoping``) and thus it does +not compile there. + +:: + + pragma solidity ^0.4.0; + pragma experimental "v0.5.0"; + contract C { + function minimalScoping() pure public { + { + uint same2 = 0; + } + + { + uint same2 = 0; + } + } + } + +As a special example of the C99 scoping rules, note that in the following, +the first assignment to ``x`` will actually assign the outer and not the inner variable. +In any case, you will get a warning about the outer variable being shadowed. + +:: + + pragma solidity ^0.4.0; + pragma experimental "v0.5.0"; + contract C { + function f() pure public returns (uint) { + uint x = 1; + { + x = 2; // this will assign to the outer variable + uint x; + } + return x; // x has value 2 + } + } + .. index:: ! exception, ! throw, ! assert, ! require, ! revert Error handling: Assert, Require, Revert and Exceptions -- cgit v1.2.3 From d64aa0eaad50741896020d31c0c93b64c3f03bc1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 26 Feb 2018 21:14:20 +0100 Subject: Some more scoping tests. --- test/libsolidity/SolidityEndToEndTest.cpp | 28 ++++++++ test/libsolidity/SolidityNameAndTypeResolution.cpp | 75 +++++++++++++++++++++- 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 7bae3cd6..c352a2c2 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -298,10 +298,38 @@ BOOST_AUTO_TEST_CASE(C99_scoping_activation) } return x; } + function g() pure public returns (uint x) { + x = 7; + { + x = 3; + uint x; + return x; // This returns the new variable, i.e. 0 + } + } + function h() pure public returns (uint x, uint a, uint b) { + x = 7; + { + x = 3; + a = x; // This should read from the outer + uint x = 4; + b = x; + } + } + function i() pure public returns (uint x, uint a) { + x = 7; + { + x = 3; + uint x = x; // This should read from the outer and assign to the inner + a = x; + } + } } )"; compileAndRun(sourceCode); ABI_CHECK(callContractFunction("f()"), encodeArgs(3)); + ABI_CHECK(callContractFunction("g()"), encodeArgs(0)); + ABI_CHECK(callContractFunction("h()"), encodeArgs(3, 3, 4)); + ABI_CHECK(callContractFunction("i()"), encodeArgs(3, 3)); } BOOST_AUTO_TEST_CASE(recursive_calls) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index e5bd0103..419e46a7 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -106,6 +106,67 @@ BOOST_AUTO_TEST_CASE(double_variable_declaration_050) })); } +BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope) +{ + string text = R"( + contract test { + function f() pure public { + { uint x; } + { uint x; } + } + } + )"; + CHECK_ERROR(text, DeclarationError, "Identifier already declared"); +} + +BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_050) +{ + string text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public { + { uint x; } + { uint x; } + } + } + )"; + CHECK_WARNING_ALLOW_MULTI(text, (vector{ + "Experimental features", + "Unused local variable", + "Unused local variable" + })); +} + +BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_activation) +{ + string text = R"( + contract test { + function f() pure public { + { uint x; } + uint x; + } + } + )"; + CHECK_ERROR(text, DeclarationError, "Identifier already declared"); +} + +BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_activation_050) +{ + string text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public { + { uint x; } + uint x; + } + } + )"; + CHECK_WARNING_ALLOW_MULTI(text, (vector{ + "Experimental features", + "Unused local variable", + "Unused local variable" + })); +} BOOST_AUTO_TEST_CASE(scoping_old) { char const* text = R"( @@ -163,11 +224,23 @@ BOOST_AUTO_TEST_CASE(scoping_activation) } BOOST_AUTO_TEST_CASE(scoping_self_use) +{ + char const* text = R"( + contract test { + function f() pure public { + uint a = a; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(scoping_self_use_050) { char const* text = R"( pragma experimental "v0.5.0"; contract test { - function f() public { + function f() pure public { uint a = a; } } -- cgit v1.2.3 From 9e0446a22c78e8a293a742492f2d2d7cd67af4bd Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 6 Feb 2017 22:42:27 +0000 Subject: Document bitwise shift operators in assembly --- docs/assembly.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/assembly.rst b/docs/assembly.rst index 46416142..245b5383 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -206,6 +206,16 @@ In the grammar, opcodes are represented as pre-defined identifiers. +-------------------------+-----+---+-----------------------------------------------------------------+ | byte(n, x) | | F | nth byte of x, where the most significant byte is the 0th byte | +-------------------------+-----+---+-----------------------------------------------------------------+ +| shl(x, y) | | C | logical shift left x by y bits | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| shr(x, y) | | C | logical shift right x by y bits | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| sar(x, y) | | C | arithmetic shift right x by y bits | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| ror(x, y) | | C | rotate right x by y bits | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| rol(x, y) | | C | rotate left x by y bits | ++-------------------------+-----+---+-----------------------------------------------------------------+ | addmod(x, y, m) | | F | (x + y) % m with arbitrary precision arithmetics | +-------------------------+-----+---+-----------------------------------------------------------------+ | mulmod(x, y, m) | | F | (x * y) % m with arbitrary precision arithmetics | -- cgit v1.2.3 From 468d0f6199e71f0c7f4b8bd667c8f31feba41a9d Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 15 Jun 2017 12:14:14 +0100 Subject: Warn on using shift instructions --- libsolidity/inlineasm/AsmAnalysis.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index a05ac57d..7653ee51 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -548,6 +548,22 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio "the Metropolis hard fork. Before that it acts as an invalid instruction." ); + static set experimentalInstructions{ + solidity::Instruction::SHL, + solidity::Instruction::SHR, + solidity::Instruction::SAR, + solidity::Instruction::ROL, + solidity::Instruction::ROR + }; + if (experimentalInstructions.count(_instr)) + m_errorReporter.warning( + _location, + "The \"" + + boost::to_lower_copy(instructionInfo(_instr).name) + + "\" instruction is only available after " + + "the Constantinople hard fork. Before that it acts as an invalid instruction." + ); + if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI || _instr == solidity::Instruction::JUMPDEST) m_errorReporter.warning( _location, -- cgit v1.2.3 From afa4a48e3770630a744ef98ccd518601e1f35c86 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 9 Jul 2017 08:42:54 +0100 Subject: Remove ROL/ROR as they are not part of EIP145 anymore --- docs/assembly.rst | 4 ---- libevmasm/Instruction.cpp | 4 ---- libevmasm/Instruction.h | 2 -- libsolidity/inlineasm/AsmAnalysis.cpp | 4 +--- 4 files changed, 1 insertion(+), 13 deletions(-) diff --git a/docs/assembly.rst b/docs/assembly.rst index 245b5383..9eabc99e 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -212,10 +212,6 @@ In the grammar, opcodes are represented as pre-defined identifiers. +-------------------------+-----+---+-----------------------------------------------------------------+ | sar(x, y) | | C | arithmetic shift right x by y bits | +-------------------------+-----+---+-----------------------------------------------------------------+ -| ror(x, y) | | C | rotate right x by y bits | -+-------------------------+-----+---+-----------------------------------------------------------------+ -| rol(x, y) | | C | rotate left x by y bits | -+-------------------------+-----+---+-----------------------------------------------------------------+ | addmod(x, y, m) | | F | (x + y) % m with arbitrary precision arithmetics | +-------------------------+-----+---+-----------------------------------------------------------------+ | mulmod(x, y, m) | | F | (x * y) % m with arbitrary precision arithmetics | diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index c25c9653..a677a631 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -53,8 +53,6 @@ const std::map dev::solidity::c_instructions = { "SHL", Instruction::SHL }, { "SHR", Instruction::SHR }, { "SAR", Instruction::SAR }, - { "ROL", Instruction::ROL }, - { "ROR", Instruction::ROR }, { "ADDMOD", Instruction::ADDMOD }, { "MULMOD", Instruction::MULMOD }, { "SIGNEXTEND", Instruction::SIGNEXTEND }, @@ -198,8 +196,6 @@ static const std::map c_instructionInfo = { Instruction::SHL, { "SHL", 0, 2, 1, false, Tier::VeryLow } }, { Instruction::SHR, { "SHR", 0, 2, 1, false, Tier::VeryLow } }, { Instruction::SAR, { "SAR", 0, 2, 1, false, Tier::VeryLow } }, - { Instruction::ROL, { "ROL", 0, 2, 1, false, Tier::VeryLow } }, - { Instruction::ROR, { "ROR", 0, 2, 1, false, Tier::VeryLow } }, { Instruction::ADDMOD, { "ADDMOD", 0, 3, 1, false, Tier::Mid } }, { Instruction::MULMOD, { "MULMOD", 0, 3, 1, false, Tier::Mid } }, { Instruction::SIGNEXTEND, { "SIGNEXTEND", 0, 2, 1, false, Tier::Low } }, diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index e56d4c9a..be788ddb 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -64,8 +64,6 @@ enum class Instruction: uint8_t SHL, ///< bitwise SHL operation SHR, ///< bitwise SHR operation SAR, ///< bitwise SAR operation - ROL, ///< bitwise ROL operation - ROR, ///< bitwise ROR operation KECCAK256 = 0x20, ///< compute KECCAK-256 hash diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 7653ee51..1030523a 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -551,9 +551,7 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio static set experimentalInstructions{ solidity::Instruction::SHL, solidity::Instruction::SHR, - solidity::Instruction::SAR, - solidity::Instruction::ROL, - solidity::Instruction::ROR + solidity::Instruction::SAR }; if (experimentalInstructions.count(_instr)) m_errorReporter.warning( -- cgit v1.2.3 From 317e017849e3ada43842c1e0268338e54c0c4d8e Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 5 Jan 2018 00:29:14 +0000 Subject: Shift operands were swapped in accepted EIP145 --- docs/assembly.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/assembly.rst b/docs/assembly.rst index 9eabc99e..cf9bf840 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -206,11 +206,11 @@ In the grammar, opcodes are represented as pre-defined identifiers. +-------------------------+-----+---+-----------------------------------------------------------------+ | byte(n, x) | | F | nth byte of x, where the most significant byte is the 0th byte | +-------------------------+-----+---+-----------------------------------------------------------------+ -| shl(x, y) | | C | logical shift left x by y bits | +| shl(x, y) | | C | logical shift left y by x bits | +-------------------------+-----+---+-----------------------------------------------------------------+ -| shr(x, y) | | C | logical shift right x by y bits | +| shr(x, y) | | C | logical shift right y by x bits | +-------------------------+-----+---+-----------------------------------------------------------------+ -| sar(x, y) | | C | arithmetic shift right x by y bits | +| sar(x, y) | | C | arithmetic shift right y by x bits | +-------------------------+-----+---+-----------------------------------------------------------------+ | addmod(x, y, m) | | F | (x + y) % m with arbitrary precision arithmetics | +-------------------------+-----+---+-----------------------------------------------------------------+ -- cgit v1.2.3 From 73c5d99bfaa4d17a97d4554a89d1005b25e62f98 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Feb 2018 12:09:22 +0100 Subject: Add basic test for shift opcodes --- test/libsolidity/InlineAssembly.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 45fb54f8..ea120657 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -774,6 +774,20 @@ BOOST_AUTO_TEST_CASE(create2) BOOST_CHECK(successAssemble("{ pop(create2(10, 0x123, 32, 64)) }")); } +BOOST_AUTO_TEST_CASE(shift) +{ + BOOST_CHECK(successAssemble("{ pop(shl(10, 32)) }")); + BOOST_CHECK(successAssemble("{ pop(shr(10, 32)) }")); + BOOST_CHECK(successAssemble("{ pop(sar(10, 32)) }")); +} + +BOOST_AUTO_TEST_CASE(shift_constantinople_warning) +{ + CHECK_PARSE_WARNING("{ pop(shl(10, 32)) }", Warning, "The \"shl\" instruction is only available after the Constantinople hard fork"); + CHECK_PARSE_WARNING("{ pop(shr(10, 32)) }", Warning, "The \"shr\" instruction is only available after the Constantinople hard fork"); + CHECK_PARSE_WARNING("{ pop(sar(10, 32)) }", Warning, "The \"sar\" instruction is only available after the Constantinople hard fork"); +} + BOOST_AUTO_TEST_CASE(jump_warning) { CHECK_PARSE_WARNING("{ 1 jump }", Warning, "Jump instructions"); -- cgit v1.2.3 From 9a8b0d5726a4402135151dce9208dc4f131713c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 18 Sep 2017 12:53:31 +0200 Subject: Fix install_deps.sh for Debian without lsb_release --- scripts/install_deps.sh | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh index d62cffb7..e884ed65 100755 --- a/scripts/install_deps.sh +++ b/scripts/install_deps.sh @@ -168,11 +168,12 @@ case $(uname -s) in # Debian #------------------------------------------------------------------------------ - Debian) + Debian*) #Debian + . /etc/os-release install_z3="" - case $(lsb_release -cs) in - wheezy) + case $VERSION_ID in + 7) #wheezy echo "Installing solidity dependencies on Debian Wheezy (7.x)." echo "ERROR - 'install_deps.sh' doesn't have Debian Wheezy support yet." @@ -182,16 +183,16 @@ case $(uname -s) in echo "See also https://github.com/ethereum/webthree-umbrella/issues/495 where we are working through Alpine support." exit 1 ;; - jessie) + 8) #jessie echo "Installing solidity dependencies on Debian Jesse (8.x)." ;; - stretch) + 9) #stretch echo "Installing solidity dependencies on Debian Stretch (9.x)." install_z3="libz3-dev" ;; - buster) + 10) #buster echo "Installing solidity dependencies on Debian Buster (10.x)." install_z3="libz3-dev" -- cgit v1.2.3 From a566825589a2c0d41e2e136527b98737cffb5701 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Feb 2018 15:21:23 +0100 Subject: Issue error if no visibility is specified (on 0.5.0) --- Changelog.md | 1 + libsolidity/analysis/SyntaxChecker.cpp | 19 +++++++++----- test/libsolidity/SolidityNameAndTypeResolution.cpp | 29 +++++++++++++++++----- test/libsolidity/ViewPureChecker.cpp | 2 +- 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/Changelog.md b/Changelog.md index acd3aa54..fdc0814a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ Features: * Support and recommend using ``emit EventName();`` to call events explicitly. * Syntax Analyser: Do not warn about experimental features if they do not concern code generation. * Syntax Checker: Mark ``throw`` as an error as experimental 0.5.0 feature. + * Syntax Checker: Issue error if no visibility is specified on contract functions as experimental 0.5.0 feature. Bugfixes: * Assembly: Raise error on oversized number literals in assembly. diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 1dcfeb27..ddac194b 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -212,13 +212,20 @@ bool SyntaxChecker::visit(PlaceholderStatement const&) bool SyntaxChecker::visit(FunctionDefinition const& _function) { + bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050); + if (_function.noVisibilitySpecified()) - m_errorReporter.warning( - _function.location(), - "No visibility specified. Defaulting to \"" + - Declaration::visibilityToString(_function.visibility()) + - "\"." - ); + { + if (v050) + m_errorReporter.syntaxError(_function.location(), "No visibility specified."); + else + m_errorReporter.warning( + _function.location(), + "No visibility specified. Defaulting to \"" + + Declaration::visibilityToString(_function.visibility()) + + "\"." + ); + } return true; } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index be147e48..be7d09ef 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7741,7 +7741,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) char const* text = R"( pragma experimental "v0.5.0"; contract C { - function f() { + function f() public { this.balance; } } @@ -7750,7 +7750,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) text = R"( pragma experimental "v0.5.0"; contract C { - function f() { + function f() public { this.transfer; } } @@ -7759,7 +7759,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) text = R"( pragma experimental "v0.5.0"; contract C { - function f() { + function f() public { this.send; } } @@ -7768,7 +7768,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) text = R"( pragma experimental "v0.5.0"; contract C { - function f() { + function f() public { this.call; } } @@ -7777,7 +7777,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) text = R"( pragma experimental "v0.5.0"; contract C { - function f() { + function f() public { this.callcode; } } @@ -7786,7 +7786,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) text = R"( pragma experimental "v0.5.0"; contract C { - function f() { + function f() public { this.delegatecall; } } @@ -7870,6 +7870,23 @@ BOOST_AUTO_TEST_CASE(getter_is_memory_type) } } +BOOST_AUTO_TEST_CASE(require_visibility_specifiers) +{ + char const* text = R"( + contract C { + function f() pure { } + } + )"; + CHECK_WARNING(text, "No visibility specified. Defaulting to"); + text = R"( + pragma experimental "v0.5.0"; + contract C { + function f() pure { } + } + )"; + CHECK_ERROR(text, SyntaxError, "No visibility specified."); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index 3a03c877..2599ca28 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -148,7 +148,7 @@ BOOST_AUTO_TEST_CASE(environment_access) BOOST_AUTO_TEST_CASE(view_error_for_050) { CHECK_ERROR( - "pragma experimental \"v0.5.0\"; contract C { uint x; function f() view { x = 2; } }", + "pragma experimental \"v0.5.0\"; contract C { uint x; function f() view public { x = 2; } }", TypeError, "Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable." ); -- cgit v1.2.3 From 029e19983cdc444749258d21b448e52c48d2088d Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Feb 2018 19:28:48 +0100 Subject: Split compileContract off compileAndRunWithoutCheck in SolidityExecutionFramework --- test/libsolidity/SolidityExecutionFramework.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h index b853d558..f562721d 100644 --- a/test/libsolidity/SolidityExecutionFramework.h +++ b/test/libsolidity/SolidityExecutionFramework.h @@ -51,6 +51,17 @@ public: bytes const& _arguments = bytes(), std::map const& _libraryAddresses = std::map() ) override + { + bytes bytecode = compileContract(_sourceCode, _contractName, _libraryAddresses); + sendMessage(bytecode + _arguments, true, _value); + return m_output; + } + + bytes compileContract( + std::string const& _sourceCode, + std::string const& _contractName = "", + std::map const& _libraryAddresses = std::map() + ) { // Silence compiler version warning std::string sourceCode = "pragma solidity >=0.0;\n" + _sourceCode; @@ -72,8 +83,7 @@ public: } eth::LinkerObject obj = m_compiler.object(_contractName.empty() ? m_compiler.lastContractName() : _contractName); BOOST_REQUIRE(obj.linkReferences.empty()); - sendMessage(obj.bytecode + _arguments, true, _value); - return m_output; + return obj.bytecode; } protected: -- cgit v1.2.3 From 0346f723422698158b4d5d9b4d7737d185cd8e52 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Feb 2018 19:32:05 +0100 Subject: Use compileContract in contract tests --- test/contracts/AuctionRegistrar.cpp | 9 ++------- test/contracts/FixedFeeRegistrar.cpp | 9 ++------- test/contracts/Wallet.cpp | 9 ++------- 3 files changed, 6 insertions(+), 21 deletions(-) diff --git a/test/contracts/AuctionRegistrar.cpp b/test/contracts/AuctionRegistrar.cpp index c9c744af..5e4991e2 100644 --- a/test/contracts/AuctionRegistrar.cpp +++ b/test/contracts/AuctionRegistrar.cpp @@ -220,13 +220,8 @@ protected: void deployRegistrar() { if (!s_compiledRegistrar) - { - m_compiler.reset(false); - m_compiler.addSource("", registrarCode); - m_compiler.setOptimiserSettings(m_optimize, m_optimizeRuns); - BOOST_REQUIRE_MESSAGE(m_compiler.compile(), "Compiling contract failed"); - s_compiledRegistrar.reset(new bytes(m_compiler.object("GlobalRegistrar").bytecode)); - } + s_compiledRegistrar.reset(new bytes(compileContract(registrarCode, "GlobalRegistrar"))); + sendMessage(*s_compiledRegistrar, true); BOOST_REQUIRE(!m_output.empty()); } diff --git a/test/contracts/FixedFeeRegistrar.cpp b/test/contracts/FixedFeeRegistrar.cpp index 8327999d..a3a27c37 100644 --- a/test/contracts/FixedFeeRegistrar.cpp +++ b/test/contracts/FixedFeeRegistrar.cpp @@ -132,13 +132,8 @@ protected: void deployRegistrar() { if (!s_compiledRegistrar) - { - m_compiler.reset(false); - m_compiler.addSource("", registrarCode); - m_compiler.setOptimiserSettings(m_optimize, m_optimizeRuns); - BOOST_REQUIRE_MESSAGE(m_compiler.compile(), "Compiling contract failed"); - s_compiledRegistrar.reset(new bytes(m_compiler.object("FixedFeeRegistrar").bytecode)); - } + s_compiledRegistrar.reset(new bytes(compileContract(registrarCode, "FixedFeeRegistrar"))); + sendMessage(*s_compiledRegistrar, true); BOOST_REQUIRE(!m_output.empty()); } diff --git a/test/contracts/Wallet.cpp b/test/contracts/Wallet.cpp index 90334ad6..1031e8f1 100644 --- a/test/contracts/Wallet.cpp +++ b/test/contracts/Wallet.cpp @@ -447,13 +447,8 @@ protected: ) { if (!s_compiledWallet) - { - m_compiler.reset(false); - m_compiler.addSource("", walletCode); - m_compiler.setOptimiserSettings(m_optimize, m_optimizeRuns); - BOOST_REQUIRE_MESSAGE(m_compiler.compile(), "Compiling contract failed"); - s_compiledWallet.reset(new bytes(m_compiler.object("Wallet").bytecode)); - } + s_compiledWallet.reset(new bytes(compileContract(walletCode, "Wallet"))); + bytes args = encodeArgs(u256(0x60), _required, _dailyLimit, u256(_owners.size()), _owners); sendMessage(*s_compiledWallet + args, true, _value); BOOST_REQUIRE(!m_output.empty()); -- cgit v1.2.3 From 5fd9ea4b2abf9d2498fa725cfdbdf152c3d3d15a Mon Sep 17 00:00:00 2001 From: Zhen Zhang Date: Tue, 27 Feb 2018 12:31:09 +0800 Subject: Stop allow_guessing for command line option parser --- Changelog.md | 1 + solc/CommandLineInterface.cpp | 1 + test/cmdlineTests.sh | 15 +++++++++++++++ 3 files changed, 17 insertions(+) diff --git a/Changelog.md b/Changelog.md index 3af3a99d..8aa7ba0c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -14,6 +14,7 @@ Bugfixes: * Standalone Assembly: Do not ignore input after closing brace of top level block. * Standard JSON: catch errors properly when invalid "sources" are passed * Type Checker: Properly warn when using ``_offset`` and ``_slot`` for constants in inline assembly. + * Commandline interface: throw error if option is unknown ### 0.4.20 (2018-02-14) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 8383afed..62b24975 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -627,6 +627,7 @@ Allowed options)", try { po::command_line_parser cmdLineParser(_argc, _argv); + cmdLineParser.style(po::command_line_style::default_style & (~po::command_line_style::allow_guessing)); cmdLineParser.options(allOptions).positional(filesPositions); po::store(cmdLineParser.run(), m_args); } diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index a249b601..f2c1f2c3 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -83,6 +83,21 @@ function compileWithoutWarning() test -z "$output" -a "$failed" -eq 0 } +printTask "Testing unknown options..." +( + set +e + output=$("$SOLC" --allow=test 2>&1) + failed=$? + set -e + + if [ "$output" == "unrecognised option '--allow=test'" ] && [ $failed -ne 0 ] ; then + echo "Passed" + else + printError "Incorrect response to unknown options: $STDERR" + exit 1 + fi +) + printTask "Compiling various other contracts and libraries..." ( cd "$REPO_ROOT"/test/compilationTests/ -- cgit v1.2.3 From 4f34d092ca84f71cb477129ffeeb471dba6bf772 Mon Sep 17 00:00:00 2001 From: Jordi Baylina Date: Wed, 28 Feb 2018 09:35:07 +0100 Subject: Update miscellaneous.rst --- docs/miscellaneous.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 328ec6ea..b5d605ac 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -219,7 +219,7 @@ This means the following source mappings represent the same information: ``1:2:1;1:9:1;2:1:2;2:1:2;2:1:2`` -``1:2:1;:9;2::2;;`` +``1:2:1;:9;2:1:2;;`` *************** Tips and Tricks -- cgit v1.2.3 From 83fec0232d38eb6214eb41104b6cd51b6f21f282 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 28 Feb 2018 10:36:07 +0100 Subject: Add more comprehensive tests and checks for libraries in JSONIO --- libsolidity/interface/StandardCompiler.cpp | 6 +- test/libsolidity/StandardCompiler.cpp | 110 +++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index fb973d51..6b113654 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -327,10 +327,14 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) m_compilerStack.setOptimiserSettings(optimize, optimizeRuns); map libraries; - Json::Value jsonLibraries = settings.get("libraries", Json::Value()); + Json::Value jsonLibraries = settings.get("libraries", Json::Value(Json::objectValue)); + if (!jsonLibraries.isObject()) + return formatFatalError("JSONError", "\"libraries\" is not a JSON object."); for (auto const& sourceName: jsonLibraries.getMemberNames()) { auto const& jsonSourceName = jsonLibraries[sourceName]; + if (!jsonSourceName.isObject()) + return formatFatalError("JSONError", "library entry is not a JSON object."); for (auto const& library: jsonSourceName.getMemberNames()) // @TODO use libraries only for the given source libraries[library] = h160(jsonSourceName[library].asString()); diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 404f709d..c4caf203 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -570,6 +570,116 @@ BOOST_AUTO_TEST_CASE(library_filename_with_colon) BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["git:library.sol"]["L"][0].isObject()); } +BOOST_AUTO_TEST_CASE(libraries_invalid_top_level) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "libraries": "42" + }, + "sources": { + "empty": { + "content": "" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "\"libraries\" is not a JSON object.")); +} + +BOOST_AUTO_TEST_CASE(libraries_invalid_entry) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "libraries": { + "L": "42" + } + }, + "sources": { + "empty": { + "content": "" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "library entry is not a JSON object.")); +} + +BOOST_AUTO_TEST_CASE(libraries_various_addresses) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "libraries": { + "library.sol": { + "L": 42, + "L3": "42", + "L4": "0x42", + "L5": "0x4200000000000000000000000000000000000001", + "L6": "4200000000000000000000000000000000000001" + } + } + }, + "sources": { + "empty": { + "content": "" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsAtMostWarnings(result)); +} + +BOOST_AUTO_TEST_CASE(library_linking) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "libraries": { + "library.sol": { + "L": "0x4200000000000000000000000000000000000001" + } + }, + "outputSelection": { + "fileA": { + "A": [ + "evm.bytecode" + ] + } + } + }, + "sources": { + "fileA": { + "content": "import \"library.sol\"; import \"library2.sol\"; contract A { function f() returns (uint) { L2.g(); return L.g(); } }" + }, + "library.sol": { + "content": "library L { function g() returns (uint) { return 1; } }" + }, + "library2.sol": { + "content": "library L2 { function g() { } }" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsAtMostWarnings(result)); + Json::Value contract = getContractResult(result, "fileA", "A"); + BOOST_CHECK(contract.isObject()); + BOOST_CHECK(contract["evm"]["bytecode"].isObject()); + BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"].isObject()); + BOOST_CHECK(!contract["evm"]["bytecode"]["linkReferences"]["library.sol"].isObject()); + BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["library2.sol"].isObject()); + BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["library2.sol"]["L2"].isArray()); + BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["library2.sol"]["L2"][0].isObject()); +} BOOST_AUTO_TEST_SUITE_END() -- cgit v1.2.3 From 7897301b7179603a1bc74d7be9eff6ccc67398db Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 28 Feb 2018 10:44:48 +0100 Subject: Properly validate invalid hex characters in JSONIO libraries --- libsolidity/interface/StandardCompiler.cpp | 16 ++++++++++++++-- test/libsolidity/StandardCompiler.cpp | 23 +++++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 6b113654..8c64c164 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -336,8 +336,20 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) if (!jsonSourceName.isObject()) return formatFatalError("JSONError", "library entry is not a JSON object."); for (auto const& library: jsonSourceName.getMemberNames()) - // @TODO use libraries only for the given source - libraries[library] = h160(jsonSourceName[library].asString()); + { + try + { + // @TODO use libraries only for the given source + libraries[library] = h160(jsonSourceName[library].asString()); + } + catch (dev::BadHexCharacter) + { + return formatFatalError( + "JSONError", + "Invalid library address (\"" + jsonSourceName[library].asString() + "\") supplied." + ); + } + } } m_compilerStack.setLibraries(libraries); diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index c4caf203..0bb94172 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -610,6 +610,29 @@ BOOST_AUTO_TEST_CASE(libraries_invalid_entry) BOOST_CHECK(containsError(result, "JSONError", "library entry is not a JSON object.")); } +BOOST_AUTO_TEST_CASE(libraries_invalid_hex) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "libraries": { + "library.sol": { + "L": "0x4200000000000000000000000000000000000xx1" + } + } + }, + "sources": { + "empty": { + "content": "" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "Invalid library address (\"0x4200000000000000000000000000000000000xx1\") supplied.")); +} + BOOST_AUTO_TEST_CASE(libraries_various_addresses) { char const* input = R"( -- cgit v1.2.3 From f41591b3ddfd3e5c065271058dadb3c6b7f31bbb Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Wed, 17 Jan 2018 21:02:23 +0100 Subject: [SMTChecker] A little refactoring on SSA vars --- libsolidity/formal/SMTChecker.cpp | 81 +++++++++++++----------------- libsolidity/formal/SMTChecker.h | 12 ++--- libsolidity/formal/SSAVariable.cpp | 66 ++++++++++++++++++++++++ libsolidity/formal/SSAVariable.h | 79 +++++++++++++++++++++++++++++ libsolidity/formal/SymbolicIntVariable.cpp | 54 ++++++++++++++++++++ libsolidity/formal/SymbolicIntVariable.h | 51 +++++++++++++++++++ libsolidity/formal/SymbolicVariable.cpp | 38 ++++++++++++++ libsolidity/formal/SymbolicVariable.h | 68 +++++++++++++++++++++++++ 8 files changed, 395 insertions(+), 54 deletions(-) create mode 100644 libsolidity/formal/SSAVariable.cpp create mode 100644 libsolidity/formal/SSAVariable.h create mode 100644 libsolidity/formal/SymbolicIntVariable.cpp create mode 100644 libsolidity/formal/SymbolicIntVariable.h create mode 100644 libsolidity/formal/SymbolicVariable.cpp create mode 100644 libsolidity/formal/SymbolicVariable.h diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index a64024b3..37dce96c 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -23,6 +23,7 @@ #include #endif +#include #include #include @@ -69,8 +70,7 @@ bool SMTChecker::visit(FunctionDefinition const& _function) // We only handle local variables, so we clear at the beginning of the function. // If we add storage variables, those should be cleared differently. m_interface->reset(); - m_currentSequenceCounter.clear(); - m_nextFreeSequenceCounter.clear(); + m_variables.clear(); m_pathConditions.clear(); m_conditionalExecutionHappened = false; initializeLocalVariables(_function); @@ -91,14 +91,18 @@ bool SMTChecker::visit(IfStatement const& _node) checkBooleanNotConstant(_node.condition(), "Condition is always $VALUE."); - auto countersEndFalse = m_currentSequenceCounter; auto countersEndTrue = visitBranch(_node.trueStatement(), expr(_node.condition())); vector touchedVariables = m_variableUsage->touchedVariables(_node.trueStatement()); + decltype(countersEndTrue) countersEndFalse; if (_node.falseStatement()) { countersEndFalse = visitBranch(*_node.falseStatement(), !expr(_node.condition())); touchedVariables += m_variableUsage->touchedVariables(*_node.falseStatement()); } + else + { + countersEndFalse = m_variables; + } mergeVariables(touchedVariables, expr(_node.condition()), countersEndTrue, countersEndFalse); @@ -152,7 +156,7 @@ bool SMTChecker::visit(ForStatement const& _node) checkBooleanNotConstant(*_node.condition(), "For loop condition is always $VALUE."); } - VariableSequenceCounters sequenceCountersStart = m_currentSequenceCounter; + VariableSequenceCounters sequenceCountersStart = m_variables; m_interface->push(); if (_node.condition()) m_interface->addAssertion(expr(*_node.condition())); @@ -163,7 +167,7 @@ bool SMTChecker::visit(ForStatement const& _node) m_interface->pop(); m_conditionalExecutionHappened = true; - m_currentSequenceCounter = sequenceCountersStart; + std::swap(sequenceCountersStart, m_variables); resetVariables(touchedVariables); @@ -514,7 +518,7 @@ SMTChecker::VariableSequenceCounters SMTChecker::visitBranch(Statement const& _s SMTChecker::VariableSequenceCounters SMTChecker::visitBranch(Statement const& _statement, smt::Expression const* _condition) { - VariableSequenceCounters sequenceCountersStart = m_currentSequenceCounter; + VariableSequenceCounters beforeVars = m_variables; if (_condition) pushPathCondition(*_condition); @@ -523,8 +527,9 @@ SMTChecker::VariableSequenceCounters SMTChecker::visitBranch(Statement const& _s popPathCondition(); m_conditionalExecutionHappened = true; - std::swap(sequenceCountersStart, m_currentSequenceCounter); - return sequenceCountersStart; + std::swap(m_variables, beforeVars); + + return beforeVars; } void SMTChecker::checkCondition( @@ -709,8 +714,8 @@ void SMTChecker::mergeVariables(vector const& _variables, sm set uniqueVars(_variables.begin(), _variables.end()); for (auto const* decl: uniqueVars) { - int trueCounter = _countersEndTrue.at(decl); - int falseCounter = _countersEndFalse.at(decl); + int trueCounter = _countersEndTrue.at(decl).index(); + int falseCounter = _countersEndFalse.at(decl).index(); solAssert(trueCounter != falseCounter, ""); m_interface->addAssertion(newValue(*decl) == smt::Expression::ite( _condition, @@ -724,12 +729,8 @@ bool SMTChecker::createVariable(VariableDeclaration const& _varDecl) { if (dynamic_cast(_varDecl.type().get())) { - solAssert(m_currentSequenceCounter.count(&_varDecl) == 0, ""); - solAssert(m_nextFreeSequenceCounter.count(&_varDecl) == 0, ""); solAssert(m_variables.count(&_varDecl) == 0, ""); - m_currentSequenceCounter[&_varDecl] = 0; - m_nextFreeSequenceCounter[&_varDecl] = 1; - m_variables.emplace(&_varDecl, m_interface->newFunction(uniqueSymbol(_varDecl), smt::Sort::Int, smt::Sort::Int)); + m_variables.emplace(&_varDecl, SSAVariable(&_varDecl, *m_interface)); return true; } else @@ -742,11 +743,6 @@ bool SMTChecker::createVariable(VariableDeclaration const& _varDecl) } } -string SMTChecker::uniqueSymbol(Declaration const& _decl) -{ - return _decl.name() + "_" + to_string(_decl.id()); -} - string SMTChecker::uniqueSymbol(Expression const& _expr) { return "expr_" + to_string(_expr.id()); @@ -754,48 +750,38 @@ string SMTChecker::uniqueSymbol(Expression const& _expr) bool SMTChecker::knownVariable(Declaration const& _decl) { - return m_currentSequenceCounter.count(&_decl); + return m_variables.count(&_decl); } smt::Expression SMTChecker::currentValue(Declaration const& _decl) { - solAssert(m_currentSequenceCounter.count(&_decl), ""); - return valueAtSequence(_decl, m_currentSequenceCounter.at(&_decl)); + solAssert(knownVariable(_decl), ""); + return m_variables.at(&_decl)(); } -smt::Expression SMTChecker::valueAtSequence(const Declaration& _decl, int _sequence) +smt::Expression SMTChecker::valueAtSequence(Declaration const& _decl, int _sequence) { - return var(_decl)(_sequence); + solAssert(knownVariable(_decl), ""); + return m_variables.at(&_decl)(_sequence); } smt::Expression SMTChecker::newValue(Declaration const& _decl) { - solAssert(m_nextFreeSequenceCounter.count(&_decl), ""); - m_currentSequenceCounter[&_decl] = m_nextFreeSequenceCounter[&_decl]++; - return currentValue(_decl); + solAssert(knownVariable(_decl), ""); + ++m_variables.at(&_decl); + return m_variables.at(&_decl)(); } void SMTChecker::setZeroValue(Declaration const& _decl) { - solAssert(_decl.type()->category() == Type::Category::Integer, ""); - m_interface->addAssertion(currentValue(_decl) == 0); + solAssert(knownVariable(_decl), ""); + m_variables.at(&_decl).setZeroValue(); } void SMTChecker::setUnknownValue(Declaration const& _decl) { - auto const& intType = dynamic_cast(*_decl.type()); - m_interface->addAssertion(currentValue(_decl) >= minValue(intType)); - m_interface->addAssertion(currentValue(_decl) <= maxValue(intType)); -} - -smt::Expression SMTChecker::minValue(IntegerType const& _t) -{ - return smt::Expression(_t.minValue()); -} - -smt::Expression SMTChecker::maxValue(IntegerType const& _t) -{ - return smt::Expression(_t.maxValue()); + solAssert(knownVariable(_decl), ""); + m_variables.at(&_decl).setUnknownValue(); } smt::Expression SMTChecker::expr(Expression const& _e) @@ -842,12 +828,15 @@ void SMTChecker::defineExpr(Expression const& _e, smt::Expression _value) m_interface->addAssertion(expr(_e) == _value); } -smt::Expression SMTChecker::var(Declaration const& _decl) +smt::Expression SMTChecker::minValue(IntegerType const& _t) { - solAssert(m_variables.count(&_decl), ""); - return m_variables.at(&_decl); + return smt::Expression(_t.minValue()); } +smt::Expression SMTChecker::maxValue(IntegerType const& _t) +{ + return smt::Expression(_t.maxValue()); +} void SMTChecker::popPathCondition() { solAssert(m_pathConditions.size() > 0, "Cannot pop path condition, empty."); diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index b57f0f96..7481e1c8 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -20,6 +20,8 @@ #include +#include + #include #include @@ -76,7 +78,7 @@ private: void assignment(Declaration const& _variable, smt::Expression const& _value, SourceLocation const& _location); /// Maps a variable to an SSA index. - using VariableSequenceCounters = std::map; + using VariableSequenceCounters = std::map; /// Visits the branch given by the statement, pushes and pops the current path conditions. /// @param _condition if present, asserts that this condition is true within the branch. @@ -118,7 +120,6 @@ private: /// This fails if the type is not supported. bool createVariable(VariableDeclaration const& _varDecl); - static std::string uniqueSymbol(Declaration const& _decl); static std::string uniqueSymbol(Expression const& _expr); /// @returns true if _delc is a variable that is known at the current point, i.e. @@ -148,9 +149,6 @@ private: void createExpr(Expression const& _e); /// Creates the expression and sets its value. void defineExpr(Expression const& _e, smt::Expression _value); - /// Returns the function declaration corresponding to the given variable. - /// The function takes one argument which is the "sequence number". - smt::Expression var(Declaration const& _decl); /// Adds a new path condition void pushPathCondition(smt::Expression const& _e); @@ -166,10 +164,8 @@ private: std::shared_ptr m_interface; std::shared_ptr m_variableUsage; bool m_conditionalExecutionHappened = false; - std::map m_currentSequenceCounter; - std::map m_nextFreeSequenceCounter; std::map m_expressions; - std::map m_variables; + std::map m_variables; std::vector m_pathConditions; ErrorReporter& m_errorReporter; diff --git a/libsolidity/formal/SSAVariable.cpp b/libsolidity/formal/SSAVariable.cpp new file mode 100644 index 00000000..d6e97a8d --- /dev/null +++ b/libsolidity/formal/SSAVariable.cpp @@ -0,0 +1,66 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +#include + +using namespace std; +using namespace dev; +using namespace dev::solidity; + +SSAVariable::SSAVariable(Declaration const* _decl, + smt::SolverInterface& _interface) +{ + resetIndex(); + + if (dynamic_cast(_decl->type().get())) + m_symbVar = make_shared(_decl, _interface); + else + { + //solAssert(false, ""); + } +} + +void SSAVariable::resetIndex() +{ + m_currentSequenceCounter = 0; + m_nextFreeSequenceCounter.reset (new int); + *m_nextFreeSequenceCounter = 1; +} + +int SSAVariable::index() const +{ + return m_currentSequenceCounter; +} + +int SSAVariable::next() const +{ + return *m_nextFreeSequenceCounter; +} + +void SSAVariable::setZeroValue() +{ + m_symbVar->setZeroValue(index()); +} + +void SSAVariable::setUnknownValue() +{ + m_symbVar->setUnknownValue(index()); +} diff --git a/libsolidity/formal/SSAVariable.h b/libsolidity/formal/SSAVariable.h new file mode 100644 index 00000000..3d7eb80c --- /dev/null +++ b/libsolidity/formal/SSAVariable.h @@ -0,0 +1,79 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#pragma once + +#include + +#include + +namespace dev +{ +namespace solidity +{ + +class Declaration; + +/** + * This class represents the SSA representation of a program variable. + */ +class SSAVariable +{ +public: + explicit SSAVariable(Declaration const* _decl, + smt::SolverInterface& _interface); + SSAVariable(SSAVariable const&) = default; + SSAVariable(SSAVariable&&) = default; + SSAVariable& operator=(SSAVariable const&) = default; + SSAVariable& operator=(SSAVariable&&) = default; + + void resetIndex(); + + int index() const; + int next() const; + + int operator++() + { + return m_currentSequenceCounter = (*m_nextFreeSequenceCounter)++; + } + + smt::Expression operator()() const + { + return valueAtSequence(index()); + } + + smt::Expression operator()(int _seq) const + { + return valueAtSequence(_seq); + } + + void setZeroValue(); + void setUnknownValue(); + +private: + smt::Expression valueAtSequence(int _seq) const + { + return (*m_symbVar)(_seq); + } + + std::shared_ptr m_symbVar = nullptr; + int m_currentSequenceCounter; + std::shared_ptr m_nextFreeSequenceCounter; +}; + +} +} diff --git a/libsolidity/formal/SymbolicIntVariable.cpp b/libsolidity/formal/SymbolicIntVariable.cpp new file mode 100644 index 00000000..6ed7037f --- /dev/null +++ b/libsolidity/formal/SymbolicIntVariable.cpp @@ -0,0 +1,54 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +using namespace std; +using namespace dev; +using namespace dev::solidity; + +SymbolicIntVariable::SymbolicIntVariable(Declaration const* _decl, + smt::SolverInterface&_interface) + : SymbolicVariable(_decl, _interface) +{ + solAssert(m_declaration->type()->category() == Type::Category::Integer, ""); + m_expression = make_shared(m_interface.newFunction(uniqueSymbol(), smt::Sort::Int, smt::Sort::Int)); +} + +void SymbolicIntVariable::setZeroValue(int _seq) +{ + m_interface.addAssertion(valueAtSequence(_seq) == 0); +} + +void SymbolicIntVariable::setUnknownValue(int _seq) +{ + auto const& intType = dynamic_cast(*m_declaration->type()); + m_interface.addAssertion(valueAtSequence(_seq) >= minValue(intType)); + m_interface.addAssertion(valueAtSequence(_seq) <= maxValue(intType)); +} + +smt::Expression SymbolicIntVariable::minValue(IntegerType const& _t) const +{ + return smt::Expression(_t.minValue()); +} + +smt::Expression SymbolicIntVariable::maxValue(IntegerType const& _t) const +{ + return smt::Expression(_t.maxValue()); +} diff --git a/libsolidity/formal/SymbolicIntVariable.h b/libsolidity/formal/SymbolicIntVariable.h new file mode 100644 index 00000000..efe05af8 --- /dev/null +++ b/libsolidity/formal/SymbolicIntVariable.h @@ -0,0 +1,51 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#pragma once + +#include + +#include + +namespace dev +{ +namespace solidity +{ + +/** + * Specialization of SymbolicVariable for Integers + */ +class SymbolicIntVariable : public SymbolicVariable +{ +public: + explicit SymbolicIntVariable(Declaration const* _decl, + smt::SolverInterface& _interface); + SymbolicIntVariable(SymbolicIntVariable const&) = default; + SymbolicIntVariable(SymbolicIntVariable&&) = default; + SymbolicIntVariable& operator=(SymbolicIntVariable const&) = default; + SymbolicIntVariable& operator=(SymbolicIntVariable&&) = default; + + void setZeroValue(int _seq); + void setUnknownValue(int _seq); + +private: + smt::Expression minValue(IntegerType const& _t) const; + smt::Expression maxValue(IntegerType const& _t) const; +}; + +} +} diff --git a/libsolidity/formal/SymbolicVariable.cpp b/libsolidity/formal/SymbolicVariable.cpp new file mode 100644 index 00000000..13f5d9b6 --- /dev/null +++ b/libsolidity/formal/SymbolicVariable.cpp @@ -0,0 +1,38 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +using namespace std; +using namespace dev; +using namespace dev::solidity; + +SymbolicVariable::SymbolicVariable(Declaration const* _decl, + smt::SolverInterface& _interface) + : m_declaration(_decl), + m_interface(_interface) +{ +} + +string SymbolicVariable::uniqueSymbol() const +{ + return m_declaration->name() + "_" + to_string(m_declaration->id()); +} + + diff --git a/libsolidity/formal/SymbolicVariable.h b/libsolidity/formal/SymbolicVariable.h new file mode 100644 index 00000000..4de59504 --- /dev/null +++ b/libsolidity/formal/SymbolicVariable.h @@ -0,0 +1,68 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#pragma once + +#include + +#include + +#include + +namespace dev +{ +namespace solidity +{ + +class Declaration; + +/** + * This class represents the symbolic version of a program variable. + */ +class SymbolicVariable +{ +public: + explicit SymbolicVariable(Declaration const* _decl, + smt::SolverInterface& _interface); + SymbolicVariable(SymbolicVariable const&) = default; + SymbolicVariable(SymbolicVariable&&) = default; + SymbolicVariable& operator=(SymbolicVariable const&) = default; + SymbolicVariable& operator=(SymbolicVariable&&) = default; + + smt::Expression operator()(int _seq) const + { + return valueAtSequence(_seq); + } + + std::string uniqueSymbol() const; + + virtual void setZeroValue(int _seq) = 0; + virtual void setUnknownValue(int _seq) = 0; + +protected: + smt::Expression valueAtSequence(int _seq) const + { + return (*m_expression)(_seq); + } + + Declaration const* m_declaration; + std::shared_ptr m_expression = nullptr; + smt::SolverInterface& m_interface; +}; + +} +} -- cgit v1.2.3 From 3b2851ee4163bcfbca9e4e23650dfeee1a06653a Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Sat, 17 Feb 2018 09:34:38 +0100 Subject: Integer min and max values placed under SymbolicIntVar instead of SMTChecker --- libsolidity/formal/SMTChecker.cpp | 14 +++----------- libsolidity/formal/SMTChecker.h | 3 --- libsolidity/formal/SymbolicIntVariable.cpp | 4 ++-- libsolidity/formal/SymbolicIntVariable.h | 7 ++++--- 4 files changed, 9 insertions(+), 19 deletions(-) diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index 37dce96c..ad42c105 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -24,6 +24,7 @@ #endif #include +#include #include #include @@ -244,14 +245,14 @@ void SMTChecker::endVisit(TupleExpression const& _tuple) void SMTChecker::checkUnderOverflow(smt::Expression _value, IntegerType const& _type, SourceLocation const& _location) { checkCondition( - _value < minValue(_type), + _value < SymbolicIntVariable::minValue(_type), _location, "Underflow (resulting value less than " + formatNumber(_type.minValue()) + ")", "value", &_value ); checkCondition( - _value > maxValue(_type), + _value > SymbolicIntVariable::maxValue(_type), _location, "Overflow (resulting value larger than " + formatNumber(_type.maxValue()) + ")", "value", @@ -828,15 +829,6 @@ void SMTChecker::defineExpr(Expression const& _e, smt::Expression _value) m_interface->addAssertion(expr(_e) == _value); } -smt::Expression SMTChecker::minValue(IntegerType const& _t) -{ - return smt::Expression(_t.minValue()); -} - -smt::Expression SMTChecker::maxValue(IntegerType const& _t) -{ - return smt::Expression(_t.maxValue()); -} void SMTChecker::popPathCondition() { solAssert(m_pathConditions.size() > 0, "Cannot pop path condition, empty."); diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index 7481e1c8..7e7996cf 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -140,9 +140,6 @@ private: /// Resets the variable to an unknown value (in its range). void setUnknownValue(Declaration const& decl); - static smt::Expression minValue(IntegerType const& _t); - static smt::Expression maxValue(IntegerType const& _t); - /// Returns the expression corresponding to the AST node. Throws if the expression does not exist. smt::Expression expr(Expression const& _e); /// Creates the expression (value can be arbitrary) diff --git a/libsolidity/formal/SymbolicIntVariable.cpp b/libsolidity/formal/SymbolicIntVariable.cpp index 6ed7037f..c206f1cd 100644 --- a/libsolidity/formal/SymbolicIntVariable.cpp +++ b/libsolidity/formal/SymbolicIntVariable.cpp @@ -43,12 +43,12 @@ void SymbolicIntVariable::setUnknownValue(int _seq) m_interface.addAssertion(valueAtSequence(_seq) <= maxValue(intType)); } -smt::Expression SymbolicIntVariable::minValue(IntegerType const& _t) const +smt::Expression SymbolicIntVariable::minValue(IntegerType const& _t) { return smt::Expression(_t.minValue()); } -smt::Expression SymbolicIntVariable::maxValue(IntegerType const& _t) const +smt::Expression SymbolicIntVariable::maxValue(IntegerType const& _t) { return smt::Expression(_t.maxValue()); } diff --git a/libsolidity/formal/SymbolicIntVariable.h b/libsolidity/formal/SymbolicIntVariable.h index efe05af8..0066bb75 100644 --- a/libsolidity/formal/SymbolicIntVariable.h +++ b/libsolidity/formal/SymbolicIntVariable.h @@ -39,12 +39,13 @@ public: SymbolicIntVariable& operator=(SymbolicIntVariable const&) = default; SymbolicIntVariable& operator=(SymbolicIntVariable&&) = default; + /// Sets the var to 0. void setZeroValue(int _seq); + /// Sets the valid interval for the var. void setUnknownValue(int _seq); -private: - smt::Expression minValue(IntegerType const& _t) const; - smt::Expression maxValue(IntegerType const& _t) const; + static smt::Expression minValue(IntegerType const& _t); + static smt::Expression maxValue(IntegerType const& _t); }; } -- cgit v1.2.3 From 21c6b80fc98f6d584f240a47d4a01827768f18f3 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Sat, 17 Feb 2018 09:35:37 +0100 Subject: Supported types listed in SSAVariable --- libsolidity/formal/SMTChecker.cpp | 4 ++-- libsolidity/formal/SSAVariable.cpp | 7 ++++++- libsolidity/formal/SSAVariable.h | 9 +++++++++ libsolidity/formal/SymbolicVariable.h | 3 +++ 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index ad42c105..1da5b291 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -370,7 +370,7 @@ void SMTChecker::endVisit(Identifier const& _identifier) { // Will be translated as part of the node that requested the lvalue. } - else if (dynamic_cast(_identifier.annotation().type.get())) + else if (SSAVariable::supportedType(_identifier.annotation().type.get())) defineExpr(_identifier, currentValue(*decl)); else if (FunctionType const* fun = dynamic_cast(_identifier.annotation().type.get())) { @@ -728,7 +728,7 @@ void SMTChecker::mergeVariables(vector const& _variables, sm bool SMTChecker::createVariable(VariableDeclaration const& _varDecl) { - if (dynamic_cast(_varDecl.type().get())) + if (SSAVariable::supportedType(_varDecl.type().get())) { solAssert(m_variables.count(&_varDecl) == 0, ""); m_variables.emplace(&_varDecl, SSAVariable(&_varDecl, *m_interface)); diff --git a/libsolidity/formal/SSAVariable.cpp b/libsolidity/formal/SSAVariable.cpp index d6e97a8d..7f214687 100644 --- a/libsolidity/formal/SSAVariable.cpp +++ b/libsolidity/formal/SSAVariable.cpp @@ -34,10 +34,15 @@ SSAVariable::SSAVariable(Declaration const* _decl, m_symbVar = make_shared(_decl, _interface); else { - //solAssert(false, ""); + solAssert(false, ""); } } +bool SSAVariable::supportedType(Type const* _decl) +{ + return dynamic_cast(_decl); +} + void SSAVariable::resetIndex() { m_currentSequenceCounter = 0; diff --git a/libsolidity/formal/SSAVariable.h b/libsolidity/formal/SSAVariable.h index 3d7eb80c..b87693c2 100644 --- a/libsolidity/formal/SSAVariable.h +++ b/libsolidity/formal/SSAVariable.h @@ -34,6 +34,8 @@ class Declaration; class SSAVariable { public: + /// @param _decl Used to determine the type and forwarded to the symbolic var. + /// @param _interface Forwarded to the symbolic var such that it can give constraints to the solver. explicit SSAVariable(Declaration const* _decl, smt::SolverInterface& _interface); SSAVariable(SSAVariable const&) = default; @@ -61,9 +63,14 @@ public: return valueAtSequence(_seq); } + /// These two functions forward the call to the symbolic var + /// which generates the constraints according to the type. void setZeroValue(); void setUnknownValue(); + /// So far Int is supported. + static bool supportedType(Type const* _decl); + private: smt::Expression valueAtSequence(int _seq) const { @@ -72,6 +79,8 @@ private: std::shared_ptr m_symbVar = nullptr; int m_currentSequenceCounter; + /// The next free sequence counter is a shared pointer because we want + /// the copy and the copied to share it. std::shared_ptr m_nextFreeSequenceCounter; }; diff --git a/libsolidity/formal/SymbolicVariable.h b/libsolidity/formal/SymbolicVariable.h index 4de59504..66633b73 100644 --- a/libsolidity/formal/SymbolicVariable.h +++ b/libsolidity/formal/SymbolicVariable.h @@ -50,7 +50,10 @@ public: std::string uniqueSymbol() const; + /// Sets the var to the default value of its type. virtual void setZeroValue(int _seq) = 0; + /// The unknown value depends on the type. For example, an interval is set for Integers. + /// This is decided by the subclasses. virtual void setUnknownValue(int _seq) = 0; protected: -- cgit v1.2.3 From cff0836c032ecee2710f1c17c49eec0a3b4aa9fc Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Wed, 28 Feb 2018 18:00:13 +0100 Subject: Fix PR comments --- libsolidity/formal/SSAVariable.cpp | 12 +++++++----- libsolidity/formal/SSAVariable.h | 12 ++++++++---- libsolidity/formal/SymbolicIntVariable.cpp | 8 +++++--- libsolidity/formal/SymbolicIntVariable.h | 10 ++++++---- libsolidity/formal/SymbolicVariable.cpp | 8 +++++--- libsolidity/formal/SymbolicVariable.h | 10 ++++++---- 6 files changed, 37 insertions(+), 23 deletions(-) diff --git a/libsolidity/formal/SSAVariable.cpp b/libsolidity/formal/SSAVariable.cpp index 7f214687..4e6bcbcb 100644 --- a/libsolidity/formal/SSAVariable.cpp +++ b/libsolidity/formal/SSAVariable.cpp @@ -25,13 +25,15 @@ using namespace std; using namespace dev; using namespace dev::solidity; -SSAVariable::SSAVariable(Declaration const* _decl, - smt::SolverInterface& _interface) +SSAVariable::SSAVariable( + Declaration const* _decl, + smt::SolverInterface& _interface +) { resetIndex(); if (dynamic_cast(_decl->type().get())) - m_symbVar = make_shared(_decl, _interface); + m_symbolicVar = make_shared(_decl, _interface); else { solAssert(false, ""); @@ -62,10 +64,10 @@ int SSAVariable::next() const void SSAVariable::setZeroValue() { - m_symbVar->setZeroValue(index()); + m_symbolicVar->setZeroValue(index()); } void SSAVariable::setUnknownValue() { - m_symbVar->setUnknownValue(index()); + m_symbolicVar->setUnknownValue(index()); } diff --git a/libsolidity/formal/SSAVariable.h b/libsolidity/formal/SSAVariable.h index b87693c2..4ec92aa1 100644 --- a/libsolidity/formal/SSAVariable.h +++ b/libsolidity/formal/SSAVariable.h @@ -36,8 +36,10 @@ class SSAVariable public: /// @param _decl Used to determine the type and forwarded to the symbolic var. /// @param _interface Forwarded to the symbolic var such that it can give constraints to the solver. - explicit SSAVariable(Declaration const* _decl, - smt::SolverInterface& _interface); + SSAVariable( + Declaration const* _decl, + smt::SolverInterface& _interface + ); SSAVariable(SSAVariable const&) = default; SSAVariable(SSAVariable&&) = default; SSAVariable& operator=(SSAVariable const&) = default; @@ -45,7 +47,9 @@ public: void resetIndex(); + /// This function returns the current index of this SSA variable. int index() const; + /// This function returns the next free index of this SSA variable. int next() const; int operator++() @@ -74,10 +78,10 @@ public: private: smt::Expression valueAtSequence(int _seq) const { - return (*m_symbVar)(_seq); + return (*m_symbolicVar)(_seq); } - std::shared_ptr m_symbVar = nullptr; + std::shared_ptr m_symbolicVar = nullptr; int m_currentSequenceCounter; /// The next free sequence counter is a shared pointer because we want /// the copy and the copied to share it. diff --git a/libsolidity/formal/SymbolicIntVariable.cpp b/libsolidity/formal/SymbolicIntVariable.cpp index c206f1cd..d08dc155 100644 --- a/libsolidity/formal/SymbolicIntVariable.cpp +++ b/libsolidity/formal/SymbolicIntVariable.cpp @@ -23,9 +23,11 @@ using namespace std; using namespace dev; using namespace dev::solidity; -SymbolicIntVariable::SymbolicIntVariable(Declaration const* _decl, - smt::SolverInterface&_interface) - : SymbolicVariable(_decl, _interface) +SymbolicIntVariable::SymbolicIntVariable( + Declaration const* _decl, + smt::SolverInterface& _interface +): + SymbolicVariable(_decl, _interface) { solAssert(m_declaration->type()->category() == Type::Category::Integer, ""); m_expression = make_shared(m_interface.newFunction(uniqueSymbol(), smt::Sort::Int, smt::Sort::Int)); diff --git a/libsolidity/formal/SymbolicIntVariable.h b/libsolidity/formal/SymbolicIntVariable.h index 0066bb75..8a9b5d5d 100644 --- a/libsolidity/formal/SymbolicIntVariable.h +++ b/libsolidity/formal/SymbolicIntVariable.h @@ -29,11 +29,13 @@ namespace solidity /** * Specialization of SymbolicVariable for Integers */ -class SymbolicIntVariable : public SymbolicVariable +class SymbolicIntVariable: public SymbolicVariable { public: - explicit SymbolicIntVariable(Declaration const* _decl, - smt::SolverInterface& _interface); + SymbolicIntVariable( + Declaration const* _decl, + smt::SolverInterface& _interface + ); SymbolicIntVariable(SymbolicIntVariable const&) = default; SymbolicIntVariable(SymbolicIntVariable&&) = default; SymbolicIntVariable& operator=(SymbolicIntVariable const&) = default; @@ -41,7 +43,7 @@ public: /// Sets the var to 0. void setZeroValue(int _seq); - /// Sets the valid interval for the var. + /// Sets the variable to the full valid value range. void setUnknownValue(int _seq); static smt::Expression minValue(IntegerType const& _t); diff --git a/libsolidity/formal/SymbolicVariable.cpp b/libsolidity/formal/SymbolicVariable.cpp index 13f5d9b6..629049ea 100644 --- a/libsolidity/formal/SymbolicVariable.cpp +++ b/libsolidity/formal/SymbolicVariable.cpp @@ -23,9 +23,11 @@ using namespace std; using namespace dev; using namespace dev::solidity; -SymbolicVariable::SymbolicVariable(Declaration const* _decl, - smt::SolverInterface& _interface) - : m_declaration(_decl), +SymbolicVariable::SymbolicVariable( + Declaration const* _decl, + smt::SolverInterface& _interface +): + m_declaration(_decl), m_interface(_interface) { } diff --git a/libsolidity/formal/SymbolicVariable.h b/libsolidity/formal/SymbolicVariable.h index 66633b73..2b59e57a 100644 --- a/libsolidity/formal/SymbolicVariable.h +++ b/libsolidity/formal/SymbolicVariable.h @@ -36,8 +36,10 @@ class Declaration; class SymbolicVariable { public: - explicit SymbolicVariable(Declaration const* _decl, - smt::SolverInterface& _interface); + SymbolicVariable( + Declaration const* _decl, + smt::SolverInterface& _interface + ); SymbolicVariable(SymbolicVariable const&) = default; SymbolicVariable(SymbolicVariable&&) = default; SymbolicVariable& operator=(SymbolicVariable const&) = default; @@ -52,8 +54,8 @@ public: /// Sets the var to the default value of its type. virtual void setZeroValue(int _seq) = 0; - /// The unknown value depends on the type. For example, an interval is set for Integers. - /// This is decided by the subclasses. + /// The unknown value is the full range of valid values, + /// and that's sub-type dependent. virtual void setUnknownValue(int _seq) = 0; protected: -- cgit v1.2.3 From 8b1b4b78c043d54f493dc9e2330bf9df5cf93755 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Wed, 28 Feb 2018 18:31:11 +0100 Subject: Fix PR comments --- libsolidity/formal/SSAVariable.h | 4 ---- libsolidity/formal/SymbolicIntVariable.h | 4 ---- libsolidity/formal/SymbolicVariable.h | 4 ---- 3 files changed, 12 deletions(-) diff --git a/libsolidity/formal/SSAVariable.h b/libsolidity/formal/SSAVariable.h index 4ec92aa1..275e8590 100644 --- a/libsolidity/formal/SSAVariable.h +++ b/libsolidity/formal/SSAVariable.h @@ -40,10 +40,6 @@ public: Declaration const* _decl, smt::SolverInterface& _interface ); - SSAVariable(SSAVariable const&) = default; - SSAVariable(SSAVariable&&) = default; - SSAVariable& operator=(SSAVariable const&) = default; - SSAVariable& operator=(SSAVariable&&) = default; void resetIndex(); diff --git a/libsolidity/formal/SymbolicIntVariable.h b/libsolidity/formal/SymbolicIntVariable.h index 8a9b5d5d..afa25f1b 100644 --- a/libsolidity/formal/SymbolicIntVariable.h +++ b/libsolidity/formal/SymbolicIntVariable.h @@ -36,10 +36,6 @@ public: Declaration const* _decl, smt::SolverInterface& _interface ); - SymbolicIntVariable(SymbolicIntVariable const&) = default; - SymbolicIntVariable(SymbolicIntVariable&&) = default; - SymbolicIntVariable& operator=(SymbolicIntVariable const&) = default; - SymbolicIntVariable& operator=(SymbolicIntVariable&&) = default; /// Sets the var to 0. void setZeroValue(int _seq); diff --git a/libsolidity/formal/SymbolicVariable.h b/libsolidity/formal/SymbolicVariable.h index 2b59e57a..93258250 100644 --- a/libsolidity/formal/SymbolicVariable.h +++ b/libsolidity/formal/SymbolicVariable.h @@ -40,10 +40,6 @@ public: Declaration const* _decl, smt::SolverInterface& _interface ); - SymbolicVariable(SymbolicVariable const&) = default; - SymbolicVariable(SymbolicVariable&&) = default; - SymbolicVariable& operator=(SymbolicVariable const&) = default; - SymbolicVariable& operator=(SymbolicVariable&&) = default; smt::Expression operator()(int _seq) const { -- cgit v1.2.3 From b6dfd9ef5422d80177b622fc2c486de00fcc0f73 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 28 Feb 2018 16:57:35 +0100 Subject: Ensure that library addresses supplied are of correct length and hex prefixed in JSONIO --- Changelog.md | 3 ++- libsolidity/interface/StandardCompiler.cpp | 20 ++++++++++++++++-- test/libsolidity/StandardCompiler.cpp | 34 ++++++++++++++++++++++++------ 3 files changed, 47 insertions(+), 10 deletions(-) diff --git a/Changelog.md b/Changelog.md index e027e8ad..4262f839 100644 --- a/Changelog.md +++ b/Changelog.md @@ -15,7 +15,8 @@ Bugfixes: * JSON-AST: Add "documentation" property to function, event and modifier definition. * Resolver: Properly determine shadowing for imports with aliases. * Standalone Assembly: Do not ignore input after closing brace of top level block. - * Standard JSON: catch errors properly when invalid "sources" are passed + * Standard JSON: Catch errors properly when invalid "sources" are passed. + * Standard JSON: Ensure that library addresses supplied are of correct length and hex prefixed. * Type Checker: Properly warn when using ``_offset`` and ``_slot`` for constants in inline assembly. * Commandline interface: throw error if option is unknown diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 8c64c164..91fe72ae 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -27,6 +27,8 @@ #include #include +#include + using namespace std; using namespace dev; using namespace dev::solidity; @@ -337,16 +339,30 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) return formatFatalError("JSONError", "library entry is not a JSON object."); for (auto const& library: jsonSourceName.getMemberNames()) { + string address = jsonSourceName[library].asString(); + + if (!boost::starts_with(address, "0x")) + return formatFatalError( + "JSONError", + "Library address is not prefixed with \"0x\"." + ); + + if (address.length() != 42) + return formatFatalError( + "JSONError", + "Library address is of invalid length." + ); + try { // @TODO use libraries only for the given source - libraries[library] = h160(jsonSourceName[library].asString()); + libraries[library] = h160(address); } catch (dev::BadHexCharacter) { return formatFatalError( "JSONError", - "Invalid library address (\"" + jsonSourceName[library].asString() + "\") supplied." + "Invalid library address (\"" + address + "\") supplied." ); } } diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 0bb94172..eb2773ba 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -633,7 +633,7 @@ BOOST_AUTO_TEST_CASE(libraries_invalid_hex) BOOST_CHECK(containsError(result, "JSONError", "Invalid library address (\"0x4200000000000000000000000000000000000xx1\") supplied.")); } -BOOST_AUTO_TEST_CASE(libraries_various_addresses) +BOOST_AUTO_TEST_CASE(libraries_invalid_length) { char const* input = R"( { @@ -641,11 +641,8 @@ BOOST_AUTO_TEST_CASE(libraries_various_addresses) "settings": { "libraries": { "library.sol": { - "L": 42, - "L3": "42", - "L4": "0x42", - "L5": "0x4200000000000000000000000000000000000001", - "L6": "4200000000000000000000000000000000000001" + "L1": "0x42", + "L2": "0x4200000000000000000000000000000000000001ff" } } }, @@ -657,7 +654,30 @@ BOOST_AUTO_TEST_CASE(libraries_various_addresses) } )"; Json::Value result = compile(input); - BOOST_CHECK(containsAtMostWarnings(result)); + BOOST_CHECK(containsError(result, "JSONError", "Library address is of invalid length.")); +} + +BOOST_AUTO_TEST_CASE(libraries_missing_hex_prefix) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "libraries": { + "library.sol": { + "L": "4200000000000000000000000000000000000001" + } + } + }, + "sources": { + "empty": { + "content": "" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "Library address is not prefixed with \"0x\".")); } BOOST_AUTO_TEST_CASE(library_linking) -- cgit v1.2.3 From 754076319659a387a2c447fe69285aad07e0abb6 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 19 Feb 2018 18:20:49 +0100 Subject: Tests for multi-dimensional arrays. --- test/libsolidity/SolidityNameAndTypeResolution.cpp | 56 ++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 8c2d853c..1fadcbde 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -979,6 +979,62 @@ BOOST_AUTO_TEST_CASE(functions_with_stucts_of_non_external_types_in_interface_ne CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions."); } +BOOST_AUTO_TEST_CASE(returning_multi_dimensional_arrays_new_abi) +{ + char const* text = R"( + pragma experimental ABIEncoderV2; + + contract C { + function f() public pure returns (string[][]) {} + } + )"; + CHECK_WARNING(text, "Experimental features"); +} + +BOOST_AUTO_TEST_CASE(returning_multi_dimensional_arrays) +{ + char const* text = R"( + contract C { + function f() public pure returns (string[][]) {} + } + )"; + CHECK_ERROR(text, TypeError, "only supported in the new experimental ABI encoder"); +} + +BOOST_AUTO_TEST_CASE(returning_multi_dimensional_static_arrays) +{ + char const* text = R"( + contract C { + function f() public pure returns (uint[][2]) {} + } + )"; + CHECK_ERROR(text, TypeError, "only supported in the new experimental ABI encoder"); +} + +BOOST_AUTO_TEST_CASE(returning_arrays_in_structs_new_abi) +{ + char const* text = R"( + pragma experimental ABIEncoderV2; + + contract C { + struct S { string[] s; } + function f() public pure returns (S) {} + } + )"; + CHECK_WARNING(text, "Experimental features"); +} + +BOOST_AUTO_TEST_CASE(returning_arrays_in_structs_arrays) +{ + char const* text = R"( + contract C { + struct S { string[] s; } + function f() public pure returns (S x) {} + } + )"; + CHECK_ERROR(text, TypeError, "only supported in the new experimental ABI encoder"); +} + BOOST_AUTO_TEST_CASE(function_external_call_allowed_conversion) { char const* text = R"( -- cgit v1.2.3 From 08b6a72d37f4e4149575bfc695d76c61b666a8a9 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 19 Feb 2018 18:21:02 +0100 Subject: Fix multi-dimensional arrays in the ABI. --- libsolidity/analysis/TypeChecker.cpp | 30 ++++++++++++++++++++++++++---- libsolidity/ast/Types.cpp | 2 -- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 2914472a..a2d94be4 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -34,6 +34,29 @@ using namespace std; using namespace dev; using namespace dev::solidity; +namespace +{ + +bool typeSupportedByOldABIEncoder(Type const& _type) +{ + if (_type.dataStoredIn(DataLocation::Storage)) + return true; + else if (_type.category() == Type::Category::Struct) + return false; + else if (_type.category() == Type::Category::Array) + { + auto const& arrayType = dynamic_cast(_type); + auto base = arrayType.baseType(); + if (!typeSupportedByOldABIEncoder(*base)) + return false; + else if (base->category() == Type::Category::Array && base->isDynamicallySized()) + return false; + } + return true; +} + +} + bool TypeChecker::checkTypeRequirements(ASTNode const& _contract) { @@ -561,13 +584,12 @@ bool TypeChecker::visit(FunctionDefinition const& _function) m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions."); if ( _function.visibility() > FunctionDefinition::Visibility::Internal && - type(*var)->category() == Type::Category::Struct && - !type(*var)->dataStoredIn(DataLocation::Storage) && - !_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) + !_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) && + !typeSupportedByOldABIEncoder(*type(*var)) ) m_errorReporter.typeError( var->location(), - "Structs are only supported in the new experimental ABI encoder. " + "This type is only supported in the new experimental ABI encoder. " "Use \"pragma experimental ABIEncoderV2;\" to enable the feature." ); diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 771ae643..6a9707f3 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1589,8 +1589,6 @@ bool ArrayType::canBeUsedExternally(bool _inLibrary) const return true; else if (!m_baseType->canBeUsedExternally(_inLibrary)) return false; - else if (m_baseType->category() == Category::Array && m_baseType->isDynamicallySized()) - return false; else return true; } -- cgit v1.2.3 From 98e8a9385456b5d3fe3ca7ef2e0c923b0aab2ea3 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 19 Feb 2018 18:45:42 +0100 Subject: Changelog entry. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 4262f839..ebdb2e4e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -17,6 +17,7 @@ Bugfixes: * Standalone Assembly: Do not ignore input after closing brace of top level block. * Standard JSON: Catch errors properly when invalid "sources" are passed. * Standard JSON: Ensure that library addresses supplied are of correct length and hex prefixed. + * Type Checker: Properly detect which array and struct types are unsupported by the old ABI encoder. * Type Checker: Properly warn when using ``_offset`` and ``_slot`` for constants in inline assembly. * Commandline interface: throw error if option is unknown -- cgit v1.2.3 From a0d006015e253b88626d2489e8cd8b5b537f5079 Mon Sep 17 00:00:00 2001 From: Oleksii Matiiasevych Date: Thu, 1 Mar 2018 22:54:04 +0700 Subject: Update Tips and Tricks on structs initialization. (#3626) * Update Tips and Tricks on structs initialization. --- docs/miscellaneous.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index b5d605ac..70ed6201 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -230,7 +230,10 @@ Tips and Tricks * Make your state variables public - the compiler will create :ref:`getters ` for you automatically. * If you end up checking conditions on input or state a lot at the beginning of your functions, try using :ref:`modifiers`. * If your contract has a function called ``send`` but you want to use the built-in send-function, use ``address(contractVariable).send(amount)``. -* Initialise storage structs with a single assignment: ``x = MyStruct({a: 1, b: 2});`` +* Initialize storage structs with a single assignment: ``x = MyStruct({a: 1, b: 2});`` + +.. note:: + If the storage struct has tightly packed properties, initialize it with separate assignments: ``x.a = 1; x.b = 2;``. In this way it will be easier for the optimizer to update storage in one go, thus making assignment cheaper. ********** Cheatsheet -- cgit v1.2.3 From bd4c2b9bde02b94d367ad9af6e44b9434bb1bb93 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 1 Mar 2018 16:58:20 +0100 Subject: Deprecate using unit denominations in combination with hex numbers. Closes #3574. --- Changelog.md | 1 + libsolidity/analysis/TypeChecker.cpp | 15 +++++++++++++++ test/libsolidity/SolidityNameAndTypeResolution.cpp | 19 +++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/Changelog.md b/Changelog.md index e027e8ad..47e6c437 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,7 @@ Features: * Syntax Analyser: Do not warn about experimental features if they do not concern code generation. * Syntax Checker: Mark ``throw`` as an error as experimental 0.5.0 feature. * Syntax Checker: Issue error if no visibility is specified on contract functions as experimental 0.5.0 feature. + * Type Checker: disallow combining hex numbers and unit denominations as experimental 0.5.0 feature. Bugfixes: * Assembly: Raise error on oversized number literals in assembly. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 2914472a..4ff0fb8f 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2021,6 +2021,8 @@ void TypeChecker::endVisit(ElementaryTypeNameExpression const& _expr) void TypeChecker::endVisit(Literal const& _literal) { + bool const v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); + if (_literal.looksLikeAddress()) { if (_literal.passesAddressChecksum()) @@ -2034,6 +2036,19 @@ void TypeChecker::endVisit(Literal const& _literal) "For more information please see https://solidity.readthedocs.io/en/develop/types.html#address-literals" ); } + if (_literal.isHexNumber() && _literal.subDenomination() != Literal::SubDenomination::None) + { + if (v050) + m_errorReporter.fatalTypeError( + _literal.location(), + "Hexadecimal numbers cannot be used with unit denominations." + ); + else + m_errorReporter.warning( + _literal.location(), + "Hexadecimal numbers with unit denominations are deprecated." + ); + } if (!_literal.annotation().type) _literal.annotation().type = Type::forLiteral(_literal); diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 8c2d853c..eeefe818 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -2717,6 +2717,25 @@ BOOST_AUTO_TEST_CASE(explicit_conversion_from_decimal_to_bytesxx) CHECK_SUCCESS_NO_WARNINGS(text); } +BOOST_AUTO_TEST_CASE(combining_hex_and_denomination) +{ + char const* text = R"( + contract Foo { + uint constant x = 0x01 wei; + } + )"; + CHECK_WARNING(text, "Hexadecimal numbers with unit denominations are deprecated."); + + char const* textV050 = R"( + pragma experimental "v0.5.0"; + + contract Foo { + uint constant x = 0x01 wei; + } + )"; + CHECK_ERROR(textV050, TypeError, "Hexadecimal numbers cannot be used with unit denominations."); +} + BOOST_AUTO_TEST_CASE(assigning_value_to_const_variable) { char const* text = R"( -- cgit v1.2.3 From c9840c98f45e6fa9258ec4624219622f5f71c75c Mon Sep 17 00:00:00 2001 From: Elena Dimitrova Date: Thu, 1 Mar 2018 17:59:47 +0200 Subject: Documentation updates for internal constructors and function signature (#3365) * Add a note explaining return values not included in function signature * Add section on Constructors in documentation * Improve documented definition for abstract contract * Add benefits of abstraction to documentation --- docs/abi-spec.rst | 6 ++++++ docs/contracts.rst | 35 +++++++++++++++++++++++++++++++---- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 07c8e0ce..4a61d91f 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -26,6 +26,10 @@ The first four bytes of the call data for a function call specifies the function first (left, high-order in big-endian) four bytes of the Keccak (SHA-3) hash of the signature of the function. The signature is defined as the canonical expression of the basic prototype, i.e. the function name with the parenthesised list of parameter types. Parameter types are split by a single comma - no spaces are used. +.. note:: + The return type of a function is not part of this signature. In :ref:`Solidity's function overloading ` return types are not considered. The reason is to keep function call resolution context-independent. + The JSON description of the ABI however contains both inputs and outputs. See (the :ref:`JSON ABI `) + Argument Encoding ================= @@ -290,6 +294,8 @@ In effect, a log entry using this ABI is described as: For all fixed-length Solidity types, the ``EVENT_INDEXED_ARGS`` array contains the 32-byte encoded value directly. However, for *types of dynamic length*, which include ``string``, ``bytes``, and arrays, ``EVENT_INDEXED_ARGS`` will contain the *Keccak hash* of the encoded value, rather than the encoded value directly. This allows applications to efficiently query for values of dynamic-length types (by setting the hash of the encoded value as the topic), but leaves applications unable to decode indexed values they have not queried for. For dynamic-length types, application developers face a trade-off between fast search for predetermined values (if the argument is indexed) and legibility of arbitrary values (which requires that the arguments not be indexed). Developers may overcome this tradeoff and achieve both efficient search and arbitrary legibility by defining events with two arguments — one indexed, one not — intended to hold the same value. +.. _abi_json: + JSON ==== diff --git a/docs/contracts.rst b/docs/contracts.rst index 416dc649..967eb2c8 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -955,6 +955,31 @@ not known in the context of the class where it is used, although its type is known. This is similar for ordinary virtual method lookup. +.. index:: ! constructor + +Constructors +============ +A constructor is an optional function with the same name as the contract which is executed upon contract creation. +Constructor functions can be either ``public`` or ``internal``. + +:: + + pragma solidity ^0.4.11; + + contract A { + uint public a; + + function A(uint _a) internal { + a = _a; + } + } + + contract B is A(1) { + function B() public {} + } + +A constructor set as ``internal`` causes the contract to be marked as :ref:`abstract `. + .. index:: ! base;constructor Arguments for Base Constructors @@ -1027,11 +1052,13 @@ As an exception, a state variable getter can override a public function. .. index:: ! contract;abstract, ! abstract contract +.. _abstract-contract: + ****************** Abstract Contracts ****************** -Contract functions can lack an implementation as in the following example (note that the function declaration header is terminated by ``;``):: +Contracts are marked as abstract when at least one of their functions lacks an implementation as in the following example (note that the function declaration header is terminated by ``;``):: pragma solidity ^0.4.0; @@ -1039,9 +1066,7 @@ Contract functions can lack an implementation as in the following example (note function utterance() public returns (bytes32); } -Such contracts cannot be compiled (even if they contain -implemented functions alongside non-implemented functions), -but they can be used as base contracts:: +Such contracts cannot be compiled (even if they contain implemented functions alongside non-implemented functions), but they can be used as base contracts:: pragma solidity ^0.4.0; @@ -1065,6 +1090,8 @@ Example of a Function Type (a variable declaration, where the variable is of typ function(address) external returns (address) foo; +Abstract contracts decouple the definition of a contract from its implementation providing better extensibility and self-documentation and +facilitating patterns like the `Template method `_ and removing code duplication. .. index:: ! contract;interface, ! interface contract -- cgit v1.2.3 From 5ab4a1ae7819004415293bf72a86824beb43cd51 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 21 Feb 2018 23:43:40 +0100 Subject: Add ability to set the target EVM version. --- libsolidity/interface/CompilerStack.cpp | 7 +++ libsolidity/interface/CompilerStack.h | 27 ++++++---- libsolidity/interface/EVMVersion.h | 81 ++++++++++++++++++++++++++++++ libsolidity/interface/StandardCompiler.cpp | 8 +++ solc/CommandLineInterface.cpp | 24 +++++++-- 5 files changed, 135 insertions(+), 12 deletions(-) create mode 100644 libsolidity/interface/EVMVersion.h diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 3b5e65e8..7bc31c17 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -74,6 +74,12 @@ void CompilerStack::setRemappings(vector const& _remappings) swap(m_remappings, remappings); } +void CompilerStack::setEVMVersion(EVMVersion _version) +{ + solAssert(m_stackState < State::ParsingSuccessful, "Set EVM version after parsing."); + m_evmVersion = _version; +} + void CompilerStack::reset(bool _keepSources) { if (_keepSources) @@ -88,6 +94,7 @@ void CompilerStack::reset(bool _keepSources) m_sources.clear(); } m_libraries.clear(); + m_evmVersion = EVMVersion(); m_optimize = false; m_optimizeRuns = 200; m_globalContext.reset(); diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index b377b3aa..13c9cc7a 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -23,20 +23,26 @@ #pragma once +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include + #include #include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include namespace dev { @@ -116,6 +122,8 @@ public: m_optimizeRuns = _runs; } + void setEVMVersion(EVMVersion _version = EVMVersion{}); + /// Sets the list of requested contract names. If empty, no filtering is performed and every contract /// found in the supplied sources is compiled. Names are cleared iff @a _contractNames is missing. void setRequestedContractNames(std::set const& _contractNames = std::set{}) @@ -310,6 +318,7 @@ private: ReadCallback::Callback m_smtQuery; bool m_optimize = false; unsigned m_optimizeRuns = 200; + EVMVersion m_evmVersion; std::set m_requestedContractNames; std::map m_libraries; /// list of path prefix remappings, e.g. mylibrary: github.com/ethereum = /usr/local/ethereum diff --git a/libsolidity/interface/EVMVersion.h b/libsolidity/interface/EVMVersion.h new file mode 100644 index 00000000..1ddcd218 --- /dev/null +++ b/libsolidity/interface/EVMVersion.h @@ -0,0 +1,81 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * EVM versioning. + */ + +#pragma once + +#include + +#include + +namespace dev +{ +namespace solidity +{ + +/** + * A version specifier of the EVM we want to compile to. + * Defaults to the latest version. + */ +class EVMVersion +{ +public: + EVMVersion() {} + + static EVMVersion homestead() { return {Version::Homestead}; } + static EVMVersion byzantium() { return {Version::Byzantium}; } + + static boost::optional fromString(std::string const& _version) + { + if (_version == "homestead") + return homestead(); + else if (_version == "byzantium") + return byzantium(); + else + return {}; + } + + bool operator==(EVMVersion const& _other) const { return m_version == _other.m_version; } + bool operator!=(EVMVersion const& _other) const { return !this->operator==(_other); } + + std::string name() const { return m_version == Version::Byzantium ? "byzantium" : "homestead"; } + + /// Has the RETURNDATACOPY and RETURNDATASIZE opcodes. + bool supportsReturndata() const { return *this >= byzantium(); } + bool hasStaticCall() const { return *this >= byzantium(); } + + /// Whether we have to retain the costs for the call opcode itself (false), + /// or whether we can just forward easily all remaining gas (true). + bool canOverchargeGasForCall() const + { + // @TODO when exactly was this introduced? Was together with the call stack fix. + return m_version == Version::Byzantium; + } + +private: + enum class Version { Homestead, Byzantium }; + + EVMVersion(Version _version): m_version(_version) {} + + Version m_version = Version::Byzantium; +}; + + +} +} diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 91fe72ae..ee9b1440 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -318,6 +318,14 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) Json::Value const& settings = _input.get("settings", Json::Value()); + if (settings.isMember("evmVersion")) + { + boost::optional version = EVMVersion::fromString(settings.get("evmVersion", {}).asString()); + if (!version) + return formatFatalError("JSONError", "Invalid EVM version requested."); + m_compilerStack.setEVMVersion(*version); + } + vector remappings; for (auto const& remapping: settings.get("remappings", Json::Value())) remappings.push_back(remapping.asString()); diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 62b24975..04d6d1a8 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -71,7 +71,6 @@ namespace solidity static string const g_stdinFileNameStr = ""; static string const g_strAbi = "abi"; -static string const g_strAddStandard = "add-std"; static string const g_strAllowPaths = "allow-paths"; static string const g_strAsm = "asm"; static string const g_strAsmJson = "asm-json"; @@ -87,6 +86,7 @@ static string const g_strCompactJSON = "compact-format"; static string const g_strContracts = "contracts"; static string const g_strEVM = "evm"; static string const g_strEVM15 = "evm15"; +static string const g_strEVMVersion = "evm-version"; static string const g_streWasm = "ewasm"; static string const g_strFormal = "formal"; static string const g_strGas = "gas"; @@ -118,7 +118,6 @@ static string const g_strPrettyJson = "pretty-json"; static string const g_strVersion = "version"; static string const g_argAbi = g_strAbi; -static string const g_argAddStandard = g_strAddStandard; static string const g_argPrettyJson = g_strPrettyJson; static string const g_argAllowPaths = g_strAllowPaths; static string const g_argAsm = g_strAsm; @@ -537,13 +536,17 @@ Allowed options)", (g_argHelp.c_str(), "Show help message and exit.") (g_argVersion.c_str(), "Show version and exit.") (g_strLicense.c_str(), "Show licensing information and exit.") + ( + g_strEVMVersion.c_str(), + po::value()->value_name("version"), + "Select desired EVM version. Either homestead or byzantium (default)." + ) (g_argOptimize.c_str(), "Enable bytecode optimizer.") ( g_argOptimizeRuns.c_str(), po::value()->value_name("n")->default_value(200), "Estimated number of contract runs for optimizer tuning." ) - (g_argAddStandard.c_str(), "Add standard contracts.") (g_argPrettyJson.c_str(), "Output JSON in pretty format. Currently it only works with the combined JSON output.") ( g_argLibraries.c_str(), @@ -779,6 +782,19 @@ bool CommandLineInterface::processInput() m_compiler.reset(new CompilerStack(fileReader)); + EVMVersion evmVersion; + if (m_args.count(g_strEVMVersion)) + { + string versionOptionStr = m_args[g_strEVMVersion].as(); + boost::optional versionOption = EVMVersion::fromString(versionOptionStr); + if (!versionOption) + { + cerr << "Invalid option for --evm-version: " << versionOptionStr << endl; + return false; + } + evmVersion = *versionOption; + } + auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return m_compiler->scanner(_sourceName); }; SourceReferenceFormatter formatter(cerr, scannerFromSourceName); @@ -792,6 +808,8 @@ bool CommandLineInterface::processInput() m_compiler->addSource(sourceCode.first, sourceCode.second); if (m_args.count(g_argLibraries)) m_compiler->setLibraries(m_libraries); + if (m_args.count(g_strEVMVersion)) + m_compiler->setEVMVersion(evmVersion); // TODO: Perhaps we should not compile unless requested bool optimize = m_args.count(g_argOptimize) > 0; unsigned runs = m_args[g_argOptimizeRuns].as(); -- cgit v1.2.3 From 85785710e6182a5abb68e5339f052b4a451d086e Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 21 Feb 2018 23:45:08 +0100 Subject: Store EVM version in settings. --- libsolidity/interface/CompilerStack.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 7bc31c17..51dcf6f4 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -845,6 +845,7 @@ string CompilerStack::createMetadata(Contract const& _contract) const } meta["settings"]["optimizer"]["enabled"] = m_optimize; meta["settings"]["optimizer"]["runs"] = m_optimizeRuns; + meta["settings"]["evmVersion"] = m_evmVersion.name(); meta["settings"]["compilationTarget"][_contract.contract->sourceUnitName()] = _contract.contract->annotation().canonicalName; -- cgit v1.2.3 From f75a41132553d073202d8bc2ea65cc287c7085b7 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 21 Feb 2018 23:56:42 +0100 Subject: Provide target EVM version in CompilerContext. --- libsolidity/codegen/Compiler.h | 13 ++++++++----- libsolidity/codegen/CompilerContext.h | 9 ++++++++- libsolidity/codegen/ContractCompiler.h | 2 +- libsolidity/interface/CompilerStack.cpp | 4 ++-- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h index 06654486..f6865d75 100644 --- a/libsolidity/codegen/Compiler.h +++ b/libsolidity/codegen/Compiler.h @@ -22,22 +22,25 @@ #pragma once -#include -#include #include +#include + #include +#include +#include + namespace dev { namespace solidity { class Compiler { public: - explicit Compiler(bool _optimize = false, unsigned _runs = 200): + explicit Compiler(EVMVersion _evmVersion = EVMVersion{}, bool _optimize = false, unsigned _runs = 200): m_optimize(_optimize), m_optimizeRuns(_runs), - m_runtimeContext(), - m_context(&m_runtimeContext) + m_runtimeContext(_evmVersion), + m_context(_evmVersion, &m_runtimeContext) { } /// Compiles a contract. diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index a155a3a5..e6b2484a 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -24,6 +24,8 @@ #include +#include + #include #include #include @@ -50,14 +52,17 @@ namespace solidity { class CompilerContext { public: - explicit CompilerContext(CompilerContext* _runtimeContext = nullptr): + explicit CompilerContext(EVMVersion _evmVersion = EVMVersion{}, CompilerContext* _runtimeContext = nullptr): m_asm(std::make_shared()), + m_evmVersion(_evmVersion), m_runtimeContext(_runtimeContext) { if (m_runtimeContext) m_runtimeSub = size_t(m_asm->newSub(m_runtimeContext->m_asm).data()); } + EVMVersion const& evmVersion() const { return m_evmVersion; } + /// Update currently enabled set of experimental features. void setExperimentalFeatures(std::set const& _features) { m_experimentalFeatures = _features; } /// @returns true if the given feature is enabled. @@ -287,6 +292,8 @@ private: } m_functionCompilationQueue; eth::AssemblyPointer m_asm; + /// Version of the EVM to compile against. + EVMVersion m_evmVersion; /// Activated experimental features. std::set m_experimentalFeatures; /// Other already compiled contracts to be used in contract creation calls. diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index d698dc71..18f31967 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -43,7 +43,7 @@ public: m_runtimeCompiler(_runtimeCompiler), m_context(_context) { - m_context = CompilerContext(_runtimeCompiler ? &_runtimeCompiler->m_context : nullptr); + m_context = CompilerContext(_context.evmVersion(), _runtimeCompiler ? &_runtimeCompiler->m_context : nullptr); } void compileContract( diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 51dcf6f4..2f733825 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -684,7 +684,7 @@ void CompilerStack::compileContract( for (auto const* dependency: _contract.annotation().contractDependencies) compileContract(*dependency, _compiledContracts); - shared_ptr compiler = make_shared(m_optimize, m_optimizeRuns); + shared_ptr compiler = make_shared(m_evmVersion, m_optimize, m_optimizeRuns); Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName()); string metadata = createMetadata(compiledContract); bytes cborEncodedHash = @@ -743,7 +743,7 @@ void CompilerStack::compileContract( { if (!_contract.isLibrary()) { - Compiler cloneCompiler(m_optimize, m_optimizeRuns); + Compiler cloneCompiler(m_evmVersion, m_optimize, m_optimizeRuns); cloneCompiler.compileClone(_contract, _compiledContracts); compiledContract.cloneObject = cloneCompiler.assembledObject(); } -- cgit v1.2.3 From 60f7be5d4cee94935ad35e3ebacd419e5b52ecce Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 22 Feb 2018 14:47:36 +0100 Subject: Some tests. --- test/Metadata.cpp | 3 ++- test/libsolidity/StandardCompiler.cpp | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/test/Metadata.cpp b/test/Metadata.cpp index 1ebfd468..c130d346 100644 --- a/test/Metadata.cpp +++ b/test/Metadata.cpp @@ -60,7 +60,8 @@ bool isValidMetadata(string const& _metadata) !metadata.isMember("compiler") || !metadata.isMember("settings") || !metadata.isMember("sources") || - !metadata.isMember("output") + !metadata.isMember("output") || + !metadata["settings"].isMember("evmVersion") ) return false; diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index eb2773ba..df3ece7c 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -724,6 +724,39 @@ BOOST_AUTO_TEST_CASE(library_linking) BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["library2.sol"]["L2"][0].isObject()); } +BOOST_AUTO_TEST_CASE(evm_version) +{ + auto inputForVersion = [](string const& _version) + { + return R"( + { + "language": "Solidity", + "sources": { "fileA": { "content": "contract A { }" } }, + "settings": { + )" + _version + R"( + "outputSelection": { + "fileA": { + "A": [ "metadata" ] + } + } + } + } + )"; + }; + Json::Value result; + result = compile(inputForVersion("\"evmVersion\": \"homestead\",")); + BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"homestead\"") != string::npos); + result = compile(inputForVersion("\"evmVersion\": \"byzantium\",")); + BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"byzantium\"") != string::npos); + // test default + result = compile(inputForVersion("")); + BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"byzantium\"") != string::npos); + // test invalid + result = compile(inputForVersion("\"evmVersion\": \"invalid\",")); + BOOST_CHECK(result["errors"][0]["message"].asString() == "Invalid EVM version requested."); +} + + BOOST_AUTO_TEST_SUITE_END() } -- cgit v1.2.3 From 83515eadcf21decc9355c69c621c4d530a62b04e Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 22 Feb 2018 14:52:16 +0100 Subject: Changelog entry --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 4262f839..9e3005dd 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ Features: * C99/C++-style scoping rules (instead of JavaScript function scoping) take effect as experimental v0.5.0 feature. * Code Generator: Assert that ``k != 0`` for ``molmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature. + * Interface: Provide ability to select target EVM version (homestead or byzantium, with byzantium being the default). * Standard JSON: Reject badly formatted invalid JSON inputs. * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. * Support and recommend using ``emit EventName();`` to call events explicitly. -- cgit v1.2.3 From f2f61f1c2f3b4d553c07fcf1746f49e799cb7aa4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 22 Feb 2018 17:21:26 +0100 Subject: Test both EVM versions. --- circle.yml | 8 ------ scripts/tests.sh | 35 +++++++++++++++++++++------ test/ExecutionFramework.cpp | 15 +++++++++--- test/ExecutionFramework.h | 3 +++ test/TestHelper.cpp | 11 ++++++++- test/TestHelper.h | 6 ++++- test/libsolidity/SolidityExecutionFramework.h | 1 + 7 files changed, 58 insertions(+), 21 deletions(-) diff --git a/circle.yml b/circle.yml index add8a815..1ed09ada 100644 --- a/circle.yml +++ b/circle.yml @@ -5,10 +5,6 @@ jobs: - image: trzeci/emscripten:sdk-tag-1.37.21-64bit steps: - checkout - - run: - name: Init submodules - command: | - git submodule update --init - restore_cache: name: Restore Boost build key: &boost-cache-key emscripten-boost-{{ checksum "scripts/travis-emscripten/install_deps.sh" }}{{ checksum "scripts/travis-emscripten/build_emscripten.sh" }} @@ -94,10 +90,6 @@ jobs: command: | apt-get -qq update apt-get -qy install ccache cmake libboost-all-dev libz3-dev - - run: - name: Init submodules - command: | - git submodule update --init - run: name: Store commit hash and prerelease command: | diff --git a/scripts/tests.sh b/scripts/tests.sh index 3c80adc5..69b2b338 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -37,11 +37,9 @@ then echo "Usage: $0 [--junit_report ]" exit 1 fi - testargs_no_opt="--logger=JUNIT,test_suite,$2/no_opt.xml" - testargs_opt="--logger=JUNIT,test_suite,$2/opt.xml" + log_directory="$2" else - testargs_no_opt='' - testargs_opt='' + log_directory="" fi echo "Running commandline tests..." @@ -98,10 +96,31 @@ then progress="" fi -echo "--> Running tests without optimizer..." -"$REPO_ROOT"/build/test/soltest $testargs_no_opt $progress -- --ipcpath /tmp/test/geth.ipc -echo "--> Running tests WITH optimizer..." -"$REPO_ROOT"/build/test/soltest $testargs_opt $progress -- --optimize --ipcpath /tmp/test/geth.ipc +ERROR_CODE=0 +# And then run the Solidity unit-tests in the matrix combination of optimizer / no optimizer +# and homestead / byzantium VM, # pointing to that IPC endpoint. +for optimize in "" "--optimize" +do + for vm in homestead byzantium + do + echo "--> Running tests using "$optimize" --evm-version "$vm"..." + log="" + if [ -n "$log_directory" ] + then + if [ -n "$optimize" ] + then + log=--logger=JUNIT,test_suite,$log_directory/opt_$vm.xml $testargs + else + log=--logger=JUNIT,test_suite,$log_directory/noopt_$vm.xml $testargs_no_opt + fi + fi + set +e + "$REPO_ROOT"/build/test/soltest $progress $log -- "$optimize" --evm-version "$vm" --ipcpath /tmp/test/geth.ipc + THIS_ERR=$? + set -e + if [ $? -ne 0 ]; then ERROR_CODE=$?; fi + done +done wait $CMDLINE_PID diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp index 85b5bd3b..adf514e2 100644 --- a/test/ExecutionFramework.cpp +++ b/test/ExecutionFramework.cpp @@ -20,13 +20,15 @@ * Framework for executing contracts and testing them using RPC. */ -#include -#include -#include #include +#include + +#include #include +#include + using namespace std; using namespace dev; using namespace dev::test; @@ -53,6 +55,13 @@ ExecutionFramework::ExecutionFramework() : m_showMessages(dev::test::Options::get().showMessages), m_sender(m_rpc.account(0)) { + if (!dev::test::Options::get().evmVersion.empty()) + { + auto version = solidity::EVMVersion::fromString(dev::test::Options::get().evmVersion); + BOOST_REQUIRE_MESSAGE(version, "Invalid EVM version: " + dev::test::Options::get().evmVersion); + m_evmVersion = *version; + } + m_rpc.test_rewindToBlock(0); } diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h index 8aa99473..a7971b81 100644 --- a/test/ExecutionFramework.h +++ b/test/ExecutionFramework.h @@ -25,6 +25,8 @@ #include #include +#include + #include #include @@ -227,6 +229,7 @@ protected: bytes data; }; + solidity::EVMVersion m_evmVersion; unsigned m_optimizeRuns = 200; bool m_optimize = false; bool m_showMessages = false; diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index c8747a06..fbf2dc90 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -19,8 +19,12 @@ * @date 2014 */ +#include + +#include + #include -#include "TestHelper.h" + using namespace std; using namespace dev::test; @@ -41,6 +45,11 @@ Options::Options() } else if (string(suite.argv[i]) == "--optimize") optimize = true; + else if (string(suite.argv[i]) == "--evm-version") + { + evmVersion = i + 1 < suite.argc ? suite.argv[i + 1] : "INVALID"; + ++i; + } else if (string(suite.argv[i]) == "--show-messages") showMessages = true; else if (string(suite.argv[i]) == "--no-ipc") diff --git a/test/TestHelper.h b/test/TestHelper.h index d25c5cd8..69ac458a 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -19,11 +19,14 @@ #pragma once -#include +#include + #include #include #include +#include + namespace dev { namespace test @@ -33,6 +36,7 @@ struct Options: boost::noncopyable { std::string ipcPath; bool showMessages = false; + std::string evmVersion; bool optimize = false; bool disableIPC = false; bool disableSMT = false; diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h index f562721d..12687dd1 100644 --- a/test/libsolidity/SolidityExecutionFramework.h +++ b/test/libsolidity/SolidityExecutionFramework.h @@ -68,6 +68,7 @@ public: m_compiler.reset(false); m_compiler.addSource("", sourceCode); m_compiler.setLibraries(_libraryAddresses); + m_compiler.setEVMVersion(m_evmVersion); m_compiler.setOptimiserSettings(m_optimize, m_optimizeRuns); if (!m_compiler.compile()) { -- cgit v1.2.3 From a53d6b499d5cc5c45fc096cea6393dc285581f90 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 22 Feb 2018 16:16:27 +0100 Subject: Use EVM version in type checker. --- libsolidity/analysis/TypeChecker.h | 9 ++++++++- libsolidity/interface/CompilerStack.cpp | 2 +- test/libsolidity/Assembly.cpp | 13 ++++++++----- test/libsolidity/SolidityExpressionCompiler.cpp | 2 +- test/libsolidity/SolidityNameAndTypeResolution.cpp | 5 +++++ 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 16796b63..2ba31232 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -22,6 +22,8 @@ #pragma once +#include + #include #include #include @@ -43,7 +45,10 @@ class TypeChecker: private ASTConstVisitor { public: /// @param _errorReporter provides the error logging functionality. - TypeChecker(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} + TypeChecker(EVMVersion _evmVersion, ErrorReporter& _errorReporter): + m_evmVersion(_evmVersion), + m_errorReporter(_errorReporter) + {} /// Performs type checking on the given contract and all of its sub-nodes. /// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings @@ -132,6 +137,8 @@ private: ContractDefinition const* m_scope = nullptr; + EVMVersion m_evmVersion; + /// Flag indicating whether we are currently inside an EmitStatement. bool m_insideEmitStatement = false; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 2f733825..cb1ca3aa 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -205,7 +205,7 @@ bool CompilerStack::analyze() m_contracts[contract->fullyQualifiedName()].contract = contract; } - TypeChecker typeChecker(m_errorReporter); + TypeChecker typeChecker(m_evmVersion, m_errorReporter); for (Source const* source: m_sourceOrder) for (ASTPointer const& node: source->ast->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index 59af6d41..57a4f4e0 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -20,11 +20,9 @@ * Unit tests for Assembly Items from evmasm/Assembly.h */ -#include -#include -#include #include #include + #include #include #include @@ -33,6 +31,11 @@ #include #include +#include + +#include +#include + using namespace std; using namespace dev::eth; @@ -46,7 +49,7 @@ namespace test namespace { -eth::AssemblyItems compileContract(const string& _sourceCode) +eth::AssemblyItems compileContract(string const& _sourceCode) { ErrorList errors; ErrorReporter errorReporter(errors); @@ -69,7 +72,7 @@ eth::AssemblyItems compileContract(const string& _sourceCode) for (ASTPointer const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { - TypeChecker checker(errorReporter); + TypeChecker checker(EVMVersion{}, errorReporter); BOOST_REQUIRE_NO_THROW(checker.checkTypeRequirements(*contract)); if (!Error::containsOnlyWarnings(errorReporter.errors())) return AssemblyItems(); diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index e2a0c3cd..3a5aa941 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -132,7 +132,7 @@ bytes compileFirstExpression( if (ContractDefinition* contract = dynamic_cast(node.get())) { ErrorReporter errorReporter(errors); - TypeChecker typeChecker(errorReporter); + TypeChecker typeChecker(EVMVersion{}, errorReporter); BOOST_REQUIRE(typeChecker.checkTypeRequirements(*contract)); } for (ASTPointer const& node: sourceUnit->nodes()) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 8c2d853c..6849fd07 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -3350,6 +3350,11 @@ BOOST_AUTO_TEST_CASE(dynamic_return_types_not_possible) } } )"; + m_compiler.setEVMVersion(EVMVersion{}); + CHECK_WARNING(sourceCode, "Use of the \"var\" keyword is deprecated"); + m_compiler.setEVMVersion(*EVMVersion::fromString("byzantium")); + CHECK_WARNING(sourceCode, "Use of the \"var\" keyword is deprecated"); + m_compiler.setEVMVersion(*EVMVersion::fromString("homestead")); CHECK_ERROR(sourceCode, TypeError, "Explicit type conversion not allowed from \"inaccessible dynamic type\" to \"bytes storage pointer\"."); } -- cgit v1.2.3 From dc317a44e031d45ebf745b47248bf06bc92d58bf Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 23 Feb 2018 11:42:53 +0100 Subject: Provide EVM version to assembly analysis. --- libsolidity/analysis/ReferencesResolver.cpp | 3 ++- libsolidity/analysis/TypeChecker.cpp | 1 + libsolidity/codegen/CompilerContext.cpp | 1 + libsolidity/inlineasm/AsmAnalysis.cpp | 32 +++++++++++++++++++++-------- libsolidity/inlineasm/AsmAnalysis.h | 5 ++++- libsolidity/interface/AssemblyStack.cpp | 2 +- libsolidity/interface/AssemblyStack.h | 7 +++++-- solc/CommandLineInterface.cpp | 31 ++++++++++++++-------------- solc/CommandLineInterface.h | 2 +- test/ExecutionFramework.cpp | 8 +------- test/TestHelper.cpp | 16 ++++++++++++++- test/TestHelper.h | 5 ++++- test/libjulia/Common.cpp | 9 +++++++- test/libjulia/Parser.cpp | 7 ++++++- test/libsolidity/InlineAssembly.cpp | 6 +++--- 15 files changed, 91 insertions(+), 44 deletions(-) diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 985c44d0..296a39c2 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -278,8 +278,9 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) }; // Will be re-generated later with correct information + // We use the latest EVM version because we will re-run it anyway. assembly::AsmAnalysisInfo analysisInfo; - assembly::AsmAnalyzer(analysisInfo, errorsIgnored, assembly::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations()); + assembly::AsmAnalyzer(analysisInfo, errorsIgnored, EVMVersion(), assembly::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations()); return false; } diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 2914472a..04185b38 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -875,6 +875,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) assembly::AsmAnalyzer analyzer( *_inlineAssembly.annotation().analysisInfo, m_errorReporter, + m_evmVersion, assembly::AsmFlavour::Loose, identifierAccess ); diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 0198a107..ebf0213a 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -329,6 +329,7 @@ void CompilerContext::appendInlineAssembly( analyzerResult = assembly::AsmAnalyzer( analysisInfo, errorReporter, + m_evmVersion, assembly::AsmFlavour::Strict, identifierAccess.resolve ).analyze(*parserResult); diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 1030523a..efa0410d 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -533,19 +533,33 @@ void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _loc void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocation const& _location) { - static set futureInstructions{ - solidity::Instruction::CREATE2, - solidity::Instruction::RETURNDATACOPY, - solidity::Instruction::RETURNDATASIZE, - solidity::Instruction::STATICCALL - }; - if (futureInstructions.count(_instr)) + // We assume that returndatacopy, returndatasize and staticcall are either all available + // or all not available. + solAssert(m_evmVersion.supportsReturndata() == m_evmVersion.hasStaticCall(), ""); + + if (_instr == solidity::Instruction::CREATE2) m_errorReporter.warning( _location, "The \"" + boost::to_lower_copy(instructionInfo(_instr).name) - + "\" instruction is only available after " + - "the Metropolis hard fork. Before that it acts as an invalid instruction." + + "\" instruction is not supported by the VM version \"" + + "" + m_evmVersion.name() + + "\" you are currently compiling for. " + + "It will be interpreted as an invalid instruction on this VM." + ); + else if (( + _instr == solidity::Instruction::RETURNDATACOPY || + _instr == solidity::Instruction::RETURNDATASIZE || + _instr == solidity::Instruction::STATICCALL + ) && !m_evmVersion.supportsReturndata()) + m_errorReporter.warning( + _location, + "The \"" + + boost::to_lower_copy(instructionInfo(_instr).name) + + "\" instruction is only available for Byzantium-compatible VMs. " + + "You are currently compiling for \"" + + m_evmVersion.name() + + "\", where it will be interpreted as an invalid instruction." ); static set experimentalInstructions{ diff --git a/libsolidity/inlineasm/AsmAnalysis.h b/libsolidity/inlineasm/AsmAnalysis.h index 7a81dbf8..867711c7 100644 --- a/libsolidity/inlineasm/AsmAnalysis.h +++ b/libsolidity/inlineasm/AsmAnalysis.h @@ -21,6 +21,7 @@ #pragma once #include +#include #include @@ -54,9 +55,10 @@ public: explicit AsmAnalyzer( AsmAnalysisInfo& _analysisInfo, ErrorReporter& _errorReporter, + EVMVersion _evmVersion, AsmFlavour _flavour = AsmFlavour::Loose, julia::ExternalIdentifierAccess::Resolver const& _resolver = julia::ExternalIdentifierAccess::Resolver() - ): m_resolver(_resolver), m_info(_analysisInfo), m_errorReporter(_errorReporter), m_flavour(_flavour) {} + ): m_resolver(_resolver), m_info(_analysisInfo), m_errorReporter(_errorReporter), m_evmVersion(_evmVersion), m_flavour(_flavour) {} bool analyze(assembly::Block const& _block); @@ -97,6 +99,7 @@ private: std::set m_activeVariables; AsmAnalysisInfo& m_info; ErrorReporter& m_errorReporter; + EVMVersion m_evmVersion; AsmFlavour m_flavour = AsmFlavour::Loose; }; diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp index c9e534c7..7a9fffbf 100644 --- a/libsolidity/interface/AssemblyStack.cpp +++ b/libsolidity/interface/AssemblyStack.cpp @@ -91,7 +91,7 @@ bool AssemblyStack::analyze(assembly::Block const& _block, Scanner const* _scann bool AssemblyStack::analyzeParsed() { m_analysisInfo = make_shared(); - assembly::AsmAnalyzer analyzer(*m_analysisInfo, m_errorReporter, languageToAsmFlavour(m_language)); + assembly::AsmAnalyzer analyzer(*m_analysisInfo, m_errorReporter, m_evmVersion, languageToAsmFlavour(m_language)); m_analysisSuccessful = analyzer.analyze(*m_parserResult); return m_analysisSuccessful; } diff --git a/libsolidity/interface/AssemblyStack.h b/libsolidity/interface/AssemblyStack.h index 6ae7e8d1..720220ab 100644 --- a/libsolidity/interface/AssemblyStack.h +++ b/libsolidity/interface/AssemblyStack.h @@ -22,6 +22,8 @@ #pragma once #include +#include + #include #include @@ -54,8 +56,8 @@ public: enum class Language { JULIA, Assembly, StrictAssembly }; enum class Machine { EVM, EVM15, eWasm }; - explicit AssemblyStack(Language _language = Language::Assembly): - m_language(_language), m_errorReporter(m_errors) + explicit AssemblyStack(EVMVersion _evmVersion = EVMVersion(), Language _language = Language::Assembly): + m_language(_language), m_evmVersion(_evmVersion), m_errorReporter(m_errors) {} /// @returns the scanner used during parsing @@ -82,6 +84,7 @@ private: bool analyzeParsed(); Language m_language = Language::Assembly; + EVMVersion m_evmVersion; std::shared_ptr m_scanner; diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 04d6d1a8..8ccb04ee 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -748,6 +748,19 @@ bool CommandLineInterface::processInput() if (!parseLibraryOption(library)) return false; + EVMVersion evmVersion; + if (m_args.count(g_strEVMVersion)) + { + string versionOptionStr = m_args[g_strEVMVersion].as(); + boost::optional versionOption = EVMVersion::fromString(versionOptionStr); + if (!versionOption) + { + cerr << "Invalid option for --evm-version: " << versionOptionStr << endl; + return false; + } + evmVersion = *versionOption; + } + if (m_args.count(g_argAssemble) || m_args.count(g_argStrictAssembly) || m_args.count(g_argJulia)) { // switch to assembly mode @@ -771,7 +784,7 @@ bool CommandLineInterface::processInput() return false; } } - return assemble(inputLanguage, targetMachine); + return assemble(evmVersion, inputLanguage, targetMachine); } if (m_args.count(g_argLink)) { @@ -782,19 +795,6 @@ bool CommandLineInterface::processInput() m_compiler.reset(new CompilerStack(fileReader)); - EVMVersion evmVersion; - if (m_args.count(g_strEVMVersion)) - { - string versionOptionStr = m_args[g_strEVMVersion].as(); - boost::optional versionOption = EVMVersion::fromString(versionOptionStr); - if (!versionOption) - { - cerr << "Invalid option for --evm-version: " << versionOptionStr << endl; - return false; - } - evmVersion = *versionOption; - } - auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return m_compiler->scanner(_sourceName); }; SourceReferenceFormatter formatter(cerr, scannerFromSourceName); @@ -1081,6 +1081,7 @@ void CommandLineInterface::writeLinkedFiles() } bool CommandLineInterface::assemble( + EVMVersion _evmVersion, AssemblyStack::Language _language, AssemblyStack::Machine _targetMachine ) @@ -1089,7 +1090,7 @@ bool CommandLineInterface::assemble( map assemblyStacks; for (auto const& src: m_sourceCodes) { - auto& stack = assemblyStacks[src.first] = AssemblyStack(_language); + auto& stack = assemblyStacks[src.first] = AssemblyStack(_evmVersion, _language); try { if (!stack.parseAndAnalyze(src.first, src.second)) diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 4768c9d8..81117fdc 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -54,7 +54,7 @@ private: bool link(); void writeLinkedFiles(); - bool assemble(AssemblyStack::Language _language, AssemblyStack::Machine _targetMachine); + bool assemble(EVMVersion _evmVersion, AssemblyStack::Language _language, AssemblyStack::Machine _targetMachine); void outputCompilationResults(); diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp index adf514e2..a24f78fb 100644 --- a/test/ExecutionFramework.cpp +++ b/test/ExecutionFramework.cpp @@ -51,17 +51,11 @@ string getIPCSocketPath() ExecutionFramework::ExecutionFramework() : m_rpc(RPCSession::instance(getIPCSocketPath())), + m_evmVersion(dev::test::Options::get().evmVersion()), m_optimize(dev::test::Options::get().optimize), m_showMessages(dev::test::Options::get().showMessages), m_sender(m_rpc.account(0)) { - if (!dev::test::Options::get().evmVersion.empty()) - { - auto version = solidity::EVMVersion::fromString(dev::test::Options::get().evmVersion); - BOOST_REQUIRE_MESSAGE(version, "Invalid EVM version: " + dev::test::Options::get().evmVersion); - m_evmVersion = *version; - } - m_rpc.test_rewindToBlock(0); } diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index fbf2dc90..e0d4423d 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -47,7 +47,7 @@ Options::Options() optimize = true; else if (string(suite.argv[i]) == "--evm-version") { - evmVersion = i + 1 < suite.argc ? suite.argv[i + 1] : "INVALID"; + evmVersionString = i + 1 < suite.argc ? suite.argv[i + 1] : "INVALID"; ++i; } else if (string(suite.argv[i]) == "--show-messages") @@ -61,3 +61,17 @@ Options::Options() if (auto path = getenv("ETH_TEST_IPC")) ipcPath = path; } + +dev::solidity::EVMVersion Options::evmVersion() const +{ + if (!evmVersionString.empty()) + { + // We do this check as opposed to in the constructor because the BOOST_REQUIRE + // macros cannot yet be used in the constructor. + auto version = solidity::EVMVersion::fromString(evmVersionString); + BOOST_REQUIRE_MESSAGE(version, "Invalid EVM version: " + evmVersionString); + return *version; + } + else + return dev::solidity::EVMVersion(); +} diff --git a/test/TestHelper.h b/test/TestHelper.h index 69ac458a..8c2eec36 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -36,14 +36,17 @@ struct Options: boost::noncopyable { std::string ipcPath; bool showMessages = false; - std::string evmVersion; bool optimize = false; bool disableIPC = false; bool disableSMT = false; + solidity::EVMVersion evmVersion() const; + static Options const& get(); private: + std::string evmVersionString; + Options(); }; diff --git a/test/libjulia/Common.cpp b/test/libjulia/Common.cpp index 7053a68d..d8cd20b6 100644 --- a/test/libjulia/Common.cpp +++ b/test/libjulia/Common.cpp @@ -21,6 +21,8 @@ #include +#include + #include #include @@ -61,7 +63,12 @@ pair, shared_ptr> dev::julia::test: { BOOST_REQUIRE(errorReporter.errors().empty()); auto analysisInfo = make_shared(); - assembly::AsmAnalyzer analyzer(*analysisInfo, errorReporter, flavour); + assembly::AsmAnalyzer analyzer( + *analysisInfo, + errorReporter, + dev::test::Options::get().evmVersion(), + flavour + ); if (analyzer.analyze(*parserResult)) { BOOST_REQUIRE(errorReporter.errors().empty()); diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp index ff9474c1..6476c4d4 100644 --- a/test/libjulia/Parser.cpp +++ b/test/libjulia/Parser.cpp @@ -56,7 +56,12 @@ bool parse(string const& _source, ErrorReporter& errorReporter) if (parserResult) { assembly::AsmAnalysisInfo analysisInfo; - return (assembly::AsmAnalyzer(analysisInfo, errorReporter, assembly::AsmFlavour::IULIA)).analyze(*parserResult); + return (assembly::AsmAnalyzer( + analysisInfo, + errorReporter, + dev::test::Options::get().evmVersion(), + assembly::AsmFlavour::IULIA + )).analyze(*parserResult); } } catch (FatalError const&) diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index ea120657..16ab611a 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -55,7 +55,7 @@ boost::optional parseAndReturnFirstError( AssemblyStack::Machine _machine = AssemblyStack::Machine::EVM ) { - AssemblyStack stack(_language); + AssemblyStack stack(dev::test::Options::get().evmVersion(), _language); bool success = false; try { @@ -117,7 +117,7 @@ Error expectError( void parsePrintCompare(string const& _source, bool _canWarn = false) { - AssemblyStack stack; + AssemblyStack stack(dev::test::Options::get().evmVersion()); BOOST_REQUIRE(stack.parseAndAnalyze("", _source)); if (_canWarn) BOOST_REQUIRE(Error::containsOnlyWarnings(stack.errors())); @@ -567,7 +567,7 @@ BOOST_AUTO_TEST_CASE(print_string_literal_unicode) { string source = "{ let x := \"\\u1bac\" }"; string parsed = "{\n let x := \"\\xe1\\xae\\xac\"\n}"; - AssemblyStack stack; + AssemblyStack stack(dev::test::Options::get().evmVersion()); BOOST_REQUIRE(stack.parseAndAnalyze("", source)); BOOST_REQUIRE(stack.errors().empty()); BOOST_CHECK_EQUAL(stack.print(), parsed); -- cgit v1.2.3 From 739533e9c7534cd3a0b321a3a6be1e041c91e4a2 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 23 Feb 2018 16:41:23 +0100 Subject: Activate byzantium for testing. --- test/RPCSession.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/RPCSession.cpp b/test/RPCSession.cpp index 69c75cee..79ddf754 100644 --- a/test/RPCSession.cpp +++ b/test/RPCSession.cpp @@ -19,7 +19,11 @@ /// @file RPCSession.cpp /// Low-level IPC communication between the test framework and the Ethereum node. -#include "RPCSession.h" +#include + +#include + +#include #include @@ -215,6 +219,9 @@ string RPCSession::personal_newAccount(string const& _password) void RPCSession::test_setChainParams(vector const& _accounts) { + string enableByzantium; + if (test::Options::get().evmVersion() == solidity::EVMVersion::byzantium()) + enableByzantium = "\"byzantiumForkBlock\": \"0x00\","; static string const c_configString = R"( { "sealEngine": "NoProof", @@ -224,6 +231,7 @@ void RPCSession::test_setChainParams(vector const& _accounts) "blockReward": "0x", "allowFutureBlocks": true, "homesteadForkBlock": "0x00", + )" + enableByzantium + R"( "EIP150ForkBlock": "0x00", "EIP158ForkBlock": "0x00" }, -- cgit v1.2.3 From 05cc5f22b204a0c389e1de4feaa44d33492dd053 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 23 Feb 2018 19:29:20 +0100 Subject: Correctly set evm version in tests --- test/libsolidity/ASTJSON.cpp | 21 +++++++++++++++-- test/libsolidity/AnalysisFramework.cpp | 3 +++ test/libsolidity/Assembly.cpp | 6 +++-- test/libsolidity/GasMeter.cpp | 1 + test/libsolidity/Imports.cpp | 27 ++++++++++++++++++++++ test/libsolidity/Metadata.cpp | 4 ++++ test/libsolidity/SolidityABIJSON.cpp | 2 ++ test/libsolidity/SolidityExpressionCompiler.cpp | 4 ++-- test/libsolidity/SolidityNameAndTypeResolution.cpp | 2 ++ test/libsolidity/SolidityNatspecJSON.cpp | 2 ++ test/libsolidity/ViewPureChecker.cpp | 2 ++ 11 files changed, 68 insertions(+), 6 deletions(-) diff --git a/test/libsolidity/ASTJSON.cpp b/test/libsolidity/ASTJSON.cpp index a165f7a9..9bf60b64 100644 --- a/test/libsolidity/ASTJSON.cpp +++ b/test/libsolidity/ASTJSON.cpp @@ -20,12 +20,16 @@ * Tests for the json ast output. */ -#include -#include +#include + #include #include #include +#include + +#include + using namespace std; namespace dev @@ -41,6 +45,7 @@ BOOST_AUTO_TEST_CASE(smoke_test) { CompilerStack c; c.addSource("a", "contract C {}"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map sourceIndices; sourceIndices["a"] = 1; @@ -52,6 +57,7 @@ BOOST_AUTO_TEST_CASE(source_location) { CompilerStack c; c.addSource("a", "contract C { function f() { var x = 2; x++; } }"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map sourceIndices; sourceIndices["a"] = 1; @@ -66,6 +72,7 @@ BOOST_AUTO_TEST_CASE(inheritance_specifier) { CompilerStack c; c.addSource("a", "contract C1 {} contract C2 is C1 {}"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map sourceIndices; sourceIndices["a"] = 1; @@ -81,6 +88,7 @@ BOOST_AUTO_TEST_CASE(using_for_directive) { CompilerStack c; c.addSource("a", "library L {} contract C { using L for uint; }"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map sourceIndices; sourceIndices["a"] = 1; @@ -98,6 +106,7 @@ BOOST_AUTO_TEST_CASE(enum_value) { CompilerStack c; c.addSource("a", "contract C { enum E { A, B } }"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map sourceIndices; sourceIndices["a"] = 1; @@ -115,6 +124,7 @@ BOOST_AUTO_TEST_CASE(modifier_definition) { CompilerStack c; c.addSource("a", "contract C { modifier M(uint i) { _; } function F() M(1) {} }"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map sourceIndices; sourceIndices["a"] = 1; @@ -129,6 +139,7 @@ BOOST_AUTO_TEST_CASE(modifier_invocation) { CompilerStack c; c.addSource("a", "contract C { modifier M(uint i) { _; } function F() M(1) {} }"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map sourceIndices; sourceIndices["a"] = 1; @@ -145,6 +156,7 @@ BOOST_AUTO_TEST_CASE(event_definition) { CompilerStack c; c.addSource("a", "contract C { event E(); }"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map sourceIndices; sourceIndices["a"] = 1; @@ -159,6 +171,7 @@ BOOST_AUTO_TEST_CASE(array_type_name) { CompilerStack c; c.addSource("a", "contract C { uint[] i; }"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map sourceIndices; sourceIndices["a"] = 1; @@ -172,6 +185,7 @@ BOOST_AUTO_TEST_CASE(placeholder_statement) { CompilerStack c; c.addSource("a", "contract C { modifier M { _; } }"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map sourceIndices; sourceIndices["a"] = 1; @@ -185,6 +199,7 @@ BOOST_AUTO_TEST_CASE(non_utf8) { CompilerStack c; c.addSource("a", "contract C { function f() { var x = hex\"ff\"; } }"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map sourceIndices; sourceIndices["a"] = 1; @@ -204,6 +219,7 @@ BOOST_AUTO_TEST_CASE(function_type) "contract C { function f(function() external payable returns (uint) x) " "returns (function() external constant returns (uint)) {} }" ); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map sourceIndices; sourceIndices["a"] = 1; @@ -244,6 +260,7 @@ BOOST_AUTO_TEST_CASE(documentation) " /** Some comment on fn.*/ function fn() public {}" "}" ); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map sourceIndices; sourceIndices["a"] = 0; diff --git a/test/libsolidity/AnalysisFramework.cpp b/test/libsolidity/AnalysisFramework.cpp index a27e3222..7c335a48 100644 --- a/test/libsolidity/AnalysisFramework.cpp +++ b/test/libsolidity/AnalysisFramework.cpp @@ -20,6 +20,8 @@ #include +#include + #include #include @@ -46,6 +48,7 @@ AnalysisFramework::parseAnalyseAndReturnError( { m_compiler.reset(); m_compiler.addSource("", _insertVersionPragma ? "pragma solidity >=0.0;\n" + _source : _source); + m_compiler.setEVMVersion(dev::test::Options::get().evmVersion()); if (!m_compiler.parse()) { BOOST_ERROR("Parsing contract failed in analysis test suite:" + formatErrors()); diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index 57a4f4e0..aff610a4 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -20,6 +20,8 @@ * Unit tests for Assembly Items from evmasm/Assembly.h */ +#include + #include #include @@ -72,7 +74,7 @@ eth::AssemblyItems compileContract(string const& _sourceCode) for (ASTPointer const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { - TypeChecker checker(EVMVersion{}, errorReporter); + TypeChecker checker(dev::test::Options::get().evmVersion(), errorReporter); BOOST_REQUIRE_NO_THROW(checker.checkTypeRequirements(*contract)); if (!Error::containsOnlyWarnings(errorReporter.errors())) return AssemblyItems(); @@ -80,7 +82,7 @@ eth::AssemblyItems compileContract(string const& _sourceCode) for (ASTPointer const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { - Compiler compiler; + Compiler compiler(dev::test::Options::get().evmVersion()); compiler.compileContract(*contract, map{}, bytes()); return compiler.runtimeAssemblyItems(); diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index 9d3409dd..105a0398 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -49,6 +49,7 @@ public: m_compiler.reset(false); m_compiler.addSource("", "pragma solidity >=0.0;\n" + _sourceCode); m_compiler.setOptimiserSettings(dev::test::Options::get().optimize); + m_compiler.setEVMVersion(m_evmVersion); BOOST_REQUIRE_MESSAGE(m_compiler.compile(), "Compiling contract failed"); AssemblyItems const* items = m_compiler.runtimeAssemblyItems(m_compiler.lastContractName()); diff --git a/test/libsolidity/Imports.cpp b/test/libsolidity/Imports.cpp index dc1174f4..bc81b3b1 100644 --- a/test/libsolidity/Imports.cpp +++ b/test/libsolidity/Imports.cpp @@ -21,6 +21,7 @@ */ #include +#include #include #include @@ -44,6 +45,7 @@ BOOST_AUTO_TEST_CASE(smoke_test) { CompilerStack c; c.addSource("a", "contract C {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -52,6 +54,7 @@ BOOST_AUTO_TEST_CASE(regular_import) CompilerStack c; c.addSource("a", "contract C {} pragma solidity >=0.0;"); c.addSource("b", "import \"a\"; contract D is C {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -60,6 +63,7 @@ BOOST_AUTO_TEST_CASE(import_does_not_clutter_importee) CompilerStack c; c.addSource("a", "contract C { D d; } pragma solidity >=0.0;"); c.addSource("b", "import \"a\"; contract D is C {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(!c.compile()); } @@ -69,6 +73,7 @@ BOOST_AUTO_TEST_CASE(import_is_transitive) c.addSource("a", "contract C { } pragma solidity >=0.0;"); c.addSource("b", "import \"a\"; pragma solidity >=0.0;"); c.addSource("c", "import \"b\"; contract D is C {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -77,6 +82,7 @@ BOOST_AUTO_TEST_CASE(circular_import) CompilerStack c; c.addSource("a", "import \"b\"; contract C { D d; } pragma solidity >=0.0;"); c.addSource("b", "import \"a\"; contract D { C c; } pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -86,6 +92,7 @@ BOOST_AUTO_TEST_CASE(relative_import) c.addSource("a", "import \"./dir/b\"; contract A is B {} pragma solidity >=0.0;"); c.addSource("dir/b", "contract B {} pragma solidity >=0.0;"); c.addSource("dir/c", "import \"../a\"; contract C is A {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -94,6 +101,7 @@ BOOST_AUTO_TEST_CASE(relative_import_multiplex) CompilerStack c; c.addSource("a", "contract A {} pragma solidity >=0.0;"); c.addSource("dir/a/b/c", "import \"../../.././a\"; contract B is A {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -102,6 +110,7 @@ BOOST_AUTO_TEST_CASE(simple_alias) CompilerStack c; c.addSource("a", "contract A {} pragma solidity >=0.0;"); c.addSource("dir/a/b/c", "import \"../../.././a\" as x; contract B is x.A { function() { x.A r = x.A(20); } } pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -111,6 +120,7 @@ BOOST_AUTO_TEST_CASE(library_name_clash) c.addSource("a", "library A {} pragma solidity >=0.0;"); c.addSource("b", "library A {} pragma solidity >=0.0;"); c.addSource("c", "import {A} from \"./a\"; import {A} from \"./b\";"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(!c.compile()); } @@ -119,6 +129,7 @@ BOOST_AUTO_TEST_CASE(library_name_clash_with_contract) CompilerStack c; c.addSource("a", "contract A {} pragma solidity >=0.0;"); c.addSource("b", "library A {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -128,6 +139,7 @@ BOOST_AUTO_TEST_CASE(complex_import) c.addSource("a", "contract A {} contract B {} contract C { struct S { uint a; } } pragma solidity >=0.0;"); c.addSource("b", "import \"a\" as x; import {B as b, C as c, C} from \"a\"; " "contract D is b { function f(c.S var1, x.C.S var2, C.S var3) internal {} } pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -136,14 +148,19 @@ BOOST_AUTO_TEST_CASE(name_clash_in_import) CompilerStack c; c.addSource("a", "contract A {} pragma solidity >=0.0;"); c.addSource("b", "import \"a\"; contract A {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(!c.compile()); c.addSource("b", "import \"a\" as A; contract A {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(!c.compile()); c.addSource("b", "import {A as b} from \"a\"; contract b {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(!c.compile()); c.addSource("b", "import {A} from \"a\"; contract A {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(!c.compile()); c.addSource("b", "import {A} from \"a\"; contract B {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -155,6 +172,7 @@ BOOST_AUTO_TEST_CASE(remappings) c.addSource("b", "import \"t/tee.sol\"; contract A is Tee {} pragma solidity >=0.0;"); c.addSource("s_1.4.6/s.sol", "contract S {} pragma solidity >=0.0;"); c.addSource("Tee/tee.sol", "contract Tee {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -166,6 +184,7 @@ BOOST_AUTO_TEST_CASE(context_dependent_remappings) c.addSource("b/b.sol", "import \"s/s.sol\"; contract B is SSeven {} pragma solidity >=0.0;"); c.addSource("s_1.4.6/s.sol", "contract SSix {} pragma solidity >=0.0;"); c.addSource("s_1.4.7/s.sol", "contract SSeven {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -174,6 +193,7 @@ BOOST_AUTO_TEST_CASE(filename_with_period) CompilerStack c; c.addSource("a/a.sol", "import \".b.sol\"; contract A is B {} pragma solidity >=0.0;"); c.addSource("a/.b.sol", "contract B {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(!c.compile()); } @@ -185,6 +205,7 @@ BOOST_AUTO_TEST_CASE(context_dependent_remappings_ensure_default_and_module_pres c.addSource("vendor/bar/bar.sol", "import \"foo/foo.sol\"; contract Bar {Foo1 foo;} pragma solidity >=0.0;"); c.addSource("vendor/foo_1.0.0/foo.sol", "contract Foo1 {} pragma solidity >=0.0;"); c.addSource("vendor/foo_2.0.0/foo.sol", "contract Foo2 {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -196,6 +217,7 @@ BOOST_AUTO_TEST_CASE(context_dependent_remappings_order_independent) c.addSource("a/b/main.sol", "import \"x/y/z/z.sol\"; contract Main is E {} pragma solidity >=0.0;"); c.addSource("d/z.sol", "contract D {} pragma solidity >=0.0;"); c.addSource("e/y/z/z.sol", "contract E {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); CompilerStack d; d.setRemappings(vector{"a/b:x=e", "a:x/y/z=d"}); @@ -203,6 +225,7 @@ BOOST_AUTO_TEST_CASE(context_dependent_remappings_order_independent) d.addSource("a/b/main.sol", "import \"x/y/z/z.sol\"; contract Main is E {} pragma solidity >=0.0;"); d.addSource("d/z.sol", "contract D {} pragma solidity >=0.0;"); d.addSource("e/y/z/z.sol", "contract E {} pragma solidity >=0.0;"); + d.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(d.compile()); } @@ -212,6 +235,7 @@ BOOST_AUTO_TEST_CASE(shadowing_via_import) c.addSource("a", "library A {} pragma solidity >=0.0;"); c.addSource("b", "library A {} pragma solidity >=0.0;"); c.addSource("c", "import {A} from \"./a\"; import {A} from \"./b\";"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(!c.compile()); } @@ -225,6 +249,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_imports) contract C { } )"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); size_t errorCount = 0; for (auto const& e: c.errors()) @@ -251,6 +276,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_multiple_imports) contract C { } )"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); auto numErrors = c.errors().size(); // Sometimes we get the prerelease warning, sometimes not. @@ -274,6 +300,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_alias) pragma solidity >=0.0; import {C as msg} from "B.sol"; )"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); auto numErrors = c.errors().size(); // Sometimes we get the prerelease warning, sometimes not. diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index 47cf1d3d..f1edeeb7 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -46,6 +46,7 @@ BOOST_AUTO_TEST_CASE(metadata_stamp) )"; CompilerStack compilerStack; compilerStack.addSource("", std::string(sourceCode)); + compilerStack.setEVMVersion(dev::test::Options::get().evmVersion()); compilerStack.setOptimiserSettings(dev::test::Options::get().optimize); BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); bytes const& bytecode = compilerStack.runtimeObject("test").bytecode; @@ -72,6 +73,7 @@ BOOST_AUTO_TEST_CASE(metadata_stamp_experimental) )"; CompilerStack compilerStack; compilerStack.addSource("", std::string(sourceCode)); + compilerStack.setEVMVersion(dev::test::Options::get().evmVersion()); compilerStack.setOptimiserSettings(dev::test::Options::get().optimize); BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); bytes const& bytecode = compilerStack.runtimeObject("test").bytecode; @@ -106,6 +108,7 @@ BOOST_AUTO_TEST_CASE(metadata_relevant_sources) } )"; compilerStack.addSource("B", std::string(sourceCode)); + compilerStack.setEVMVersion(dev::test::Options::get().evmVersion()); compilerStack.setOptimiserSettings(dev::test::Options::get().optimize); BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); @@ -144,6 +147,7 @@ BOOST_AUTO_TEST_CASE(metadata_relevant_sources_imports) } )"; compilerStack.addSource("C", std::string(sourceCode)); + compilerStack.setEVMVersion(dev::test::Options::get().evmVersion()); compilerStack.setOptimiserSettings(dev::test::Options::get().optimize); BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index e242508a..0d471b32 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -44,6 +44,8 @@ public: { m_compilerStack.reset(false); m_compilerStack.addSource("", "pragma solidity >=0.0;\n" + _code); + m_compilerStack.setEVMVersion(dev::test::Options::get().evmVersion()); + m_compilerStack.setOptimiserSettings(dev::test::Options::get().optimize); BOOST_REQUIRE_MESSAGE(m_compilerStack.parseAndAnalyze(), "Parsing contract failed"); Json::Value generatedInterface = m_compilerStack.contractABI(m_compilerStack.lastContractName()); diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 3a5aa941..44d3daff 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -132,7 +132,7 @@ bytes compileFirstExpression( if (ContractDefinition* contract = dynamic_cast(node.get())) { ErrorReporter errorReporter(errors); - TypeChecker typeChecker(EVMVersion{}, errorReporter); + TypeChecker typeChecker(dev::test::Options::get().evmVersion(), errorReporter); BOOST_REQUIRE(typeChecker.checkTypeRequirements(*contract)); } for (ASTPointer const& node: sourceUnit->nodes()) @@ -141,7 +141,7 @@ bytes compileFirstExpression( FirstExpressionExtractor extractor(*contract); BOOST_REQUIRE(extractor.expression() != nullptr); - CompilerContext context; + CompilerContext context(dev::test::Options::get().evmVersion()); context.resetVisitedNodes(contract); context.setInheritanceHierarchy(inheritanceHierarchy); unsigned parametersSize = _localVariables.size(); // assume they are all one slot on the stack diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 6849fd07..a5ed4402 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -22,6 +22,8 @@ #include +#include + #include #include diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index e8906bb9..49a725e0 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -47,6 +47,7 @@ public: { m_compilerStack.reset(false); m_compilerStack.addSource("", "pragma solidity >=0.0;\n" + _code); + m_compilerStack.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_REQUIRE_MESSAGE(m_compilerStack.parseAndAnalyze(), "Parsing contract failed"); Json::Value generatedDocumentation; @@ -67,6 +68,7 @@ public: { m_compilerStack.reset(false); m_compilerStack.addSource("", "pragma solidity >=0.0;\n" + _code); + m_compilerStack.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(!m_compilerStack.parseAndAnalyze()); BOOST_REQUIRE(Error::containsErrorOfType(m_compilerStack.errors(), Error::Type::DocstringParsingError)); } diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index 2599ca28..f6b2c62e 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -20,6 +20,8 @@ #include +#include + #include #include -- cgit v1.2.3 From 1e26011d2cdb11b2df089fa97e2ab15ac329a45c Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 23 Feb 2018 19:29:42 +0100 Subject: Returndatasize and staticcall test fixes. --- test/libsolidity/SolidityNameAndTypeResolution.cpp | 15 ++++++--------- test/libsolidity/ViewPureChecker.cpp | 5 ++++- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index a5ed4402..f92cd47c 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -3352,11 +3352,6 @@ BOOST_AUTO_TEST_CASE(dynamic_return_types_not_possible) } } )"; - m_compiler.setEVMVersion(EVMVersion{}); - CHECK_WARNING(sourceCode, "Use of the \"var\" keyword is deprecated"); - m_compiler.setEVMVersion(*EVMVersion::fromString("byzantium")); - CHECK_WARNING(sourceCode, "Use of the \"var\" keyword is deprecated"); - m_compiler.setEVMVersion(*EVMVersion::fromString("homestead")); CHECK_ERROR(sourceCode, TypeError, "Explicit type conversion not allowed from \"inaccessible dynamic type\" to \"bytes storage pointer\"."); } @@ -7088,11 +7083,13 @@ BOOST_AUTO_TEST_CASE(returndatacopy_as_variable) char const* text = R"( contract c { function f() public { uint returndatasize; assembly { returndatasize }}} )"; - CHECK_ALLOW_MULTI(text, (std::vector>{ + vector> expectations(vector>{ {Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"}, - {Error::Type::DeclarationError, "Unbalanced stack"}, - {Error::Type::Warning, "only available after the Metropolis"} - })); + {Error::Type::DeclarationError, "Unbalanced stack"} + }); + if (dev::test::Options::get().evmVersion() == EVMVersion::homestead()) + expectations.emplace_back(make_pair(Error::Type::Warning, std::string("\"returndatasize\" instruction is only available for Byzantium-compatible"))); + CHECK_ALLOW_MULTI(text, expectations); } BOOST_AUTO_TEST_CASE(create2_as_variable) diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index f6b2c62e..35a0c7e5 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -425,7 +425,10 @@ BOOST_AUTO_TEST_CASE(assembly_staticcall) } } )"; - CHECK_WARNING(text, "only available after the Metropolis"); + if (dev::test::Options::get().evmVersion() == EVMVersion::homestead()) + CHECK_WARNING(text, "\"staticcall\" instruction is only available for Byzantium-compatible"); + else + CHECK_SUCCESS_NO_WARNINGS(text); } BOOST_AUTO_TEST_CASE(assembly_jump) -- cgit v1.2.3 From 7b0272ccfbfbe547cea3fea5a941645b1da2cd27 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 23 Feb 2018 19:29:57 +0100 Subject: CREATE2 test fix. --- test/libsolidity/SolidityNameAndTypeResolution.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index f92cd47c..e3cde5d6 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7099,7 +7099,7 @@ BOOST_AUTO_TEST_CASE(create2_as_variable) )"; CHECK_ALLOW_MULTI(text, (std::vector>{ {Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"}, - {Error::Type::Warning, "only available after the Metropolis"}, + {Error::Type::Warning, "The \"create2\" instruction is not supported by the VM version"}, {Error::Type::DeclarationError, "Unbalanced stack"} })); } -- cgit v1.2.3 From 982476f99d085072d25b703a146a6d92cd280714 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 26 Feb 2018 19:53:38 +0100 Subject: Add TangerineWhistle. --- libsolidity/interface/EVMVersion.h | 39 +++++++++++++--------- solc/CommandLineInterface.cpp | 2 +- test/RPCSession.cpp | 16 +++++---- test/libsolidity/SolidityNameAndTypeResolution.cpp | 2 +- test/libsolidity/StandardCompiler.cpp | 4 +++ test/libsolidity/ViewPureChecker.cpp | 2 +- 6 files changed, 40 insertions(+), 25 deletions(-) diff --git a/libsolidity/interface/EVMVersion.h b/libsolidity/interface/EVMVersion.h index 1ddcd218..954a9f8f 100644 --- a/libsolidity/interface/EVMVersion.h +++ b/libsolidity/interface/EVMVersion.h @@ -23,6 +23,7 @@ #include #include +#include namespace dev { @@ -33,28 +34,40 @@ namespace solidity * A version specifier of the EVM we want to compile to. * Defaults to the latest version. */ -class EVMVersion +class EVMVersion: + boost::less_than_comparable, + boost::equality_comparable { public: EVMVersion() {} static EVMVersion homestead() { return {Version::Homestead}; } + static EVMVersion tangerineWhistle() { return {Version::TangerineWhistle}; } + static EVMVersion spuriousDragon() { return {Version::SpuriousDragon}; } static EVMVersion byzantium() { return {Version::Byzantium}; } static boost::optional fromString(std::string const& _version) { - if (_version == "homestead") - return homestead(); - else if (_version == "byzantium") - return byzantium(); - else - return {}; + for (auto const& v: {homestead(), tangerineWhistle(), spuriousDragon(), byzantium()}) + if (_version == v.name()) + return v; + return {}; } bool operator==(EVMVersion const& _other) const { return m_version == _other.m_version; } - bool operator!=(EVMVersion const& _other) const { return !this->operator==(_other); } + bool operator<(EVMVersion const& _other) const { return m_version < _other.m_version; } - std::string name() const { return m_version == Version::Byzantium ? "byzantium" : "homestead"; } + std::string name() const + { + switch (m_version) + { + case Version::Byzantium: return "byzantium"; + case Version::TangerineWhistle: return "tangerineWhistle"; + case Version::SpuriousDragon: return "spuriousDragon"; + case Version::Homestead: return "homestead"; + } + return "INVALID"; + } /// Has the RETURNDATACOPY and RETURNDATASIZE opcodes. bool supportsReturndata() const { return *this >= byzantium(); } @@ -62,14 +75,10 @@ public: /// Whether we have to retain the costs for the call opcode itself (false), /// or whether we can just forward easily all remaining gas (true). - bool canOverchargeGasForCall() const - { - // @TODO when exactly was this introduced? Was together with the call stack fix. - return m_version == Version::Byzantium; - } + bool canOverchargeGasForCall() const { return *this >= tangerineWhistle(); } private: - enum class Version { Homestead, Byzantium }; + enum class Version { Homestead, TangerineWhistle, SpuriousDragon, Byzantium }; EVMVersion(Version _version): m_version(_version) {} diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 8ccb04ee..caa564bc 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -539,7 +539,7 @@ Allowed options)", ( g_strEVMVersion.c_str(), po::value()->value_name("version"), - "Select desired EVM version. Either homestead or byzantium (default)." + "Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon or byzantium (default)." ) (g_argOptimize.c_str(), "Enable bytecode optimizer.") ( diff --git a/test/RPCSession.cpp b/test/RPCSession.cpp index 79ddf754..54871057 100644 --- a/test/RPCSession.cpp +++ b/test/RPCSession.cpp @@ -219,9 +219,13 @@ string RPCSession::personal_newAccount(string const& _password) void RPCSession::test_setChainParams(vector const& _accounts) { - string enableByzantium; - if (test::Options::get().evmVersion() == solidity::EVMVersion::byzantium()) - enableByzantium = "\"byzantiumForkBlock\": \"0x00\","; + string forks; + if (test::Options::get().evmVersion() >= solidity::EVMVersion::tangerineWhistle()) + forks += "\"EIP150ForkBlock\": \"0x00\",\n"; + if (test::Options::get().evmVersion() >= solidity::EVMVersion::spuriousDragon()) + forks += "\"EIP158ForkBlock\": \"0x00\",\n"; + if (test::Options::get().evmVersion() >= solidity::EVMVersion::byzantium()) + forks += "\"byzantiumForkBlock\": \"0x00\",\n"; static string const c_configString = R"( { "sealEngine": "NoProof", @@ -230,10 +234,8 @@ void RPCSession::test_setChainParams(vector const& _accounts) "maximumExtraDataSize": "0x1000000", "blockReward": "0x", "allowFutureBlocks": true, - "homesteadForkBlock": "0x00", - )" + enableByzantium + R"( - "EIP150ForkBlock": "0x00", - "EIP158ForkBlock": "0x00" + )" + forks + R"( + "homesteadForkBlock": "0x00" }, "genesis": { "author": "0000000000000010000000000000000000000000", diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index e3cde5d6..7c260335 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7087,7 +7087,7 @@ BOOST_AUTO_TEST_CASE(returndatacopy_as_variable) {Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"}, {Error::Type::DeclarationError, "Unbalanced stack"} }); - if (dev::test::Options::get().evmVersion() == EVMVersion::homestead()) + if (!dev::test::Options::get().evmVersion().supportsReturndata()) expectations.emplace_back(make_pair(Error::Type::Warning, std::string("\"returndatasize\" instruction is only available for Byzantium-compatible"))); CHECK_ALLOW_MULTI(text, expectations); } diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index df3ece7c..4c8918be 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -746,6 +746,10 @@ BOOST_AUTO_TEST_CASE(evm_version) Json::Value result; result = compile(inputForVersion("\"evmVersion\": \"homestead\",")); BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"homestead\"") != string::npos); + result = compile(inputForVersion("\"evmVersion\": \"tangerineWhistle\",")); + BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"tangerineWhistle\"") != string::npos); + result = compile(inputForVersion("\"evmVersion\": \"spuriousDragon\",")); + BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"spuriousDragon\"") != string::npos); result = compile(inputForVersion("\"evmVersion\": \"byzantium\",")); BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"byzantium\"") != string::npos); // test default diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index 35a0c7e5..e6a5cfd0 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -425,7 +425,7 @@ BOOST_AUTO_TEST_CASE(assembly_staticcall) } } )"; - if (dev::test::Options::get().evmVersion() == EVMVersion::homestead()) + if (!dev::test::Options::get().evmVersion().hasStaticCall()) CHECK_WARNING(text, "\"staticcall\" instruction is only available for Byzantium-compatible"); else CHECK_SUCCESS_NO_WARNINGS(text); -- cgit v1.2.3 From 7171ac0124b9a23be9f631b8d8191aae07a52516 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Feb 2018 14:52:50 +0100 Subject: Simplify error handling in tests.sh --- scripts/tests.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/scripts/tests.sh b/scripts/tests.sh index 69b2b338..9c20815e 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -96,7 +96,6 @@ then progress="" fi -ERROR_CODE=0 # And then run the Solidity unit-tests in the matrix combination of optimizer / no optimizer # and homestead / byzantium VM, # pointing to that IPC endpoint. for optimize in "" "--optimize" @@ -116,9 +115,8 @@ do fi set +e "$REPO_ROOT"/build/test/soltest $progress $log -- "$optimize" --evm-version "$vm" --ipcpath /tmp/test/geth.ipc - THIS_ERR=$? set -e - if [ $? -ne 0 ]; then ERROR_CODE=$?; fi + if [ $? -ne 0 ]; then exit $?; fi done done -- cgit v1.2.3 From 4ce0e7775d86076b2f1d0e1d1390f7a76636d257 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 27 Feb 2018 18:51:12 +0100 Subject: Add constantinople. --- libsolidity/interface/EVMVersion.h | 8 +++++--- solc/CommandLineInterface.cpp | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/libsolidity/interface/EVMVersion.h b/libsolidity/interface/EVMVersion.h index 954a9f8f..738bf203 100644 --- a/libsolidity/interface/EVMVersion.h +++ b/libsolidity/interface/EVMVersion.h @@ -45,6 +45,7 @@ public: static EVMVersion tangerineWhistle() { return {Version::TangerineWhistle}; } static EVMVersion spuriousDragon() { return {Version::SpuriousDragon}; } static EVMVersion byzantium() { return {Version::Byzantium}; } + static EVMVersion constantinople() { return {Version::Constantinople}; } static boost::optional fromString(std::string const& _version) { @@ -61,10 +62,11 @@ public: { switch (m_version) { - case Version::Byzantium: return "byzantium"; + case Version::Homestead: return "homestead"; case Version::TangerineWhistle: return "tangerineWhistle"; case Version::SpuriousDragon: return "spuriousDragon"; - case Version::Homestead: return "homestead"; + case Version::Byzantium: return "byzantium"; + case Version::Constantinople: return "constantinople"; } return "INVALID"; } @@ -78,7 +80,7 @@ public: bool canOverchargeGasForCall() const { return *this >= tangerineWhistle(); } private: - enum class Version { Homestead, TangerineWhistle, SpuriousDragon, Byzantium }; + enum class Version { Homestead, TangerineWhistle, SpuriousDragon, Byzantium, Constantinople }; EVMVersion(Version _version): m_version(_version) {} diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index caa564bc..fd079656 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -539,7 +539,7 @@ Allowed options)", ( g_strEVMVersion.c_str(), po::value()->value_name("version"), - "Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon or byzantium (default)." + "Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon, byzantium (default) or constantinople." ) (g_argOptimize.c_str(), "Enable bytecode optimizer.") ( -- cgit v1.2.3 From 1246917e323eeae635ba7ed6a8a56a1995815bfd Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 27 Feb 2018 18:53:51 +0100 Subject: Explain json-io setting. --- docs/using-the-compiler.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 66e3ac35..df30b6b4 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -101,6 +101,7 @@ Input Description enabled: true, runs: 500 }, + evmVersion: "byzantium", // Version of the EVM to compile for. Affects type checking and code generation. Can be homestead, tangerineWhistle, spuriousDragon, byzantium or constantinople // Metadata settings (optional) metadata: { // Use only literal content and not URLs (false by default) -- cgit v1.2.3 From 7f8e5733391bf2a02a7d1a701591dedddf6341b0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 27 Feb 2018 18:54:19 +0100 Subject: Abort on the first failed end-to-end run. --- scripts/tests.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/tests.sh b/scripts/tests.sh index 9c20815e..60dae2e4 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -113,10 +113,7 @@ do log=--logger=JUNIT,test_suite,$log_directory/noopt_$vm.xml $testargs_no_opt fi fi - set +e "$REPO_ROOT"/build/test/soltest $progress $log -- "$optimize" --evm-version "$vm" --ipcpath /tmp/test/geth.ipc - set -e - if [ $? -ne 0 ]; then exit $?; fi done done -- cgit v1.2.3 From 5a54cd5c708227ad6982b06de7b799ece5065917 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 28 Feb 2018 08:43:18 +0100 Subject: Only warn for shift instructions if not using constantinople --- libsolidity/inlineasm/AsmAnalysis.cpp | 18 +++++++++--------- libsolidity/interface/EVMVersion.h | 1 + test/libsolidity/InlineAssembly.cpp | 6 +++--- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index efa0410d..a7f764a5 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -561,19 +561,19 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio m_evmVersion.name() + "\", where it will be interpreted as an invalid instruction." ); - - static set experimentalInstructions{ - solidity::Instruction::SHL, - solidity::Instruction::SHR, - solidity::Instruction::SAR - }; - if (experimentalInstructions.count(_instr)) + else if (( + _instr == solidity::Instruction::SHL || + _instr == solidity::Instruction::SHR || + _instr == solidity::Instruction::SAR + ) && !m_evmVersion.hasBitwiseShifting()) m_errorReporter.warning( _location, "The \"" + boost::to_lower_copy(instructionInfo(_instr).name) - + "\" instruction is only available after " + - "the Constantinople hard fork. Before that it acts as an invalid instruction." + + "\" instruction is only available for Constantinople-compatible VMs. " + + "You are currently compiling for \"" + + m_evmVersion.name() + + "\", where it will be interpreted as an invalid instruction." ); if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI || _instr == solidity::Instruction::JUMPDEST) diff --git a/libsolidity/interface/EVMVersion.h b/libsolidity/interface/EVMVersion.h index 738bf203..13c4ec94 100644 --- a/libsolidity/interface/EVMVersion.h +++ b/libsolidity/interface/EVMVersion.h @@ -74,6 +74,7 @@ public: /// Has the RETURNDATACOPY and RETURNDATASIZE opcodes. bool supportsReturndata() const { return *this >= byzantium(); } bool hasStaticCall() const { return *this >= byzantium(); } + bool hasBitwiseShifting() const { return *this >= constantinople(); } /// Whether we have to retain the costs for the call opcode itself (false), /// or whether we can just forward easily all remaining gas (true). diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 16ab611a..a4dcc4d5 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -783,9 +783,9 @@ BOOST_AUTO_TEST_CASE(shift) BOOST_AUTO_TEST_CASE(shift_constantinople_warning) { - CHECK_PARSE_WARNING("{ pop(shl(10, 32)) }", Warning, "The \"shl\" instruction is only available after the Constantinople hard fork"); - CHECK_PARSE_WARNING("{ pop(shr(10, 32)) }", Warning, "The \"shr\" instruction is only available after the Constantinople hard fork"); - CHECK_PARSE_WARNING("{ pop(sar(10, 32)) }", Warning, "The \"sar\" instruction is only available after the Constantinople hard fork"); + CHECK_PARSE_WARNING("{ pop(shl(10, 32)) }", Warning, "The \"shl\" instruction is only available for Constantinople-compatible VMs."); + CHECK_PARSE_WARNING("{ pop(shr(10, 32)) }", Warning, "The \"shr\" instruction is only available for Constantinople-compatible VMs."); + CHECK_PARSE_WARNING("{ pop(sar(10, 32)) }", Warning, "The \"sar\" instruction is only available for Constantinople-compatible VMs."); } BOOST_AUTO_TEST_CASE(jump_warning) -- cgit v1.2.3 From 2c086cb90b59116fc300f52c135c465c82db1b1e Mon Sep 17 00:00:00 2001 From: bernard peh Date: Fri, 2 Mar 2018 16:25:52 +1100 Subject: clarify 2300 gas stipend in fallback function section --- docs/contracts.rst | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 967eb2c8..cfd5256a 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -523,16 +523,9 @@ Ether (without data). Additionally, in order to receive Ether, the fallback func must be marked ``payable``. If no such function exists, the contract cannot receive Ether through regular transactions. -In such a context, there is usually very little gas available to the function call (to be precise, 2300 gas), so it is important to make fallback functions as cheap as possible. Note that the gas required by a transaction (as opposed to an internal call) that invokes the fallback function is much higher, because each transaction charges an additional amount of 21000 gas or more for things like signature checking. +It is worth nothing that the "send" or "transfer" function only has a 2300 gas stipend. If the fallback function is called by another contract account using the send/transfer function (eg address.transfer(1 ether)), then the fallback function only has 2300 gas to spend, meaning that it cannot do much other than basic logging. -In particular, the following operations will consume more gas than the stipend provided to a fallback function: - -- Writing to storage -- Creating a contract -- Calling an external function which consumes a large amount of gas -- Sending Ether - -Please ensure you test your fallback function thoroughly to ensure the execution cost is less than 2300 gas before deploying a contract. +Like any function, the fallback function can execute complex operations as long as there is enough gas passed on to it. .. note:: Even though the fallback function cannot have arguments, one can still use ``msg.data`` to retrieve -- cgit v1.2.3 From 454b470ceed799cb22ceaef6ea2b0bbea7165df9 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 2 Mar 2018 10:20:56 +0100 Subject: Suggest expressions of the form '0x1234 * 1 day' instead of hex numbers with unit denominations. --- libsolidity/analysis/TypeChecker.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 4ff0fb8f..cc7492dd 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2041,12 +2041,14 @@ void TypeChecker::endVisit(Literal const& _literal) if (v050) m_errorReporter.fatalTypeError( _literal.location(), - "Hexadecimal numbers cannot be used with unit denominations." + "Hexadecimal numbers cannot be used with unit denominations. " + "You can use an expression of the form '0x1234 * 1 day' instead." ); else m_errorReporter.warning( _literal.location(), - "Hexadecimal numbers with unit denominations are deprecated." + "Hexadecimal numbers with unit denominations are deprecated. " + "You can use an expression of the form '0x1234 * 1 day' instead." ); } if (!_literal.annotation().type) -- cgit v1.2.3 From 0f64648203b6b4631b42fcacf9eb71c3d94c921f Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 2 Mar 2018 10:36:04 +0100 Subject: Parser: Add test case for reserved keywords. --- test/libsolidity/SolidityParser.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index b7097d0f..f03b30e1 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -1164,6 +1164,36 @@ BOOST_AUTO_TEST_CASE(constant_is_keyword) CHECK_PARSE_ERROR(text, "Expected identifier"); } +BOOST_AUTO_TEST_CASE(keyword_is_reserved) +{ + auto keywords = { + "abstract", + "after", + "case", + "catch", + "default", + "final", + "in", + "inline", + "let", + "match", + "null", + "of", + "relocatable", + "static", + "switch", + "try", + "type", + "typeof" + }; + + for (const auto& keyword: keywords) + { + auto text = std::string("contract ") + keyword + " {}"; + CHECK_PARSE_ERROR(text.c_str(), "Expected identifier"); + } +} + BOOST_AUTO_TEST_CASE(var_array) { char const* text = R"( -- cgit v1.2.3 From 66ee9aa2f1d5af5dbabd6699e98935261d49558c Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 2 Mar 2018 12:29:48 +0100 Subject: Use 0.4.21 pragma for documentation that uses "emit". --- docs/introduction-to-smart-contracts.rst | 2 +- docs/solidity-by-example.rst | 6 +++--- docs/structure-of-a-contract.rst | 2 +- docs/types.rst | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 11e07292..56f0fe3e 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -80,7 +80,7 @@ registering with username and password - all you need is an Ethereum keypair. :: - pragma solidity ^0.4.20; // should actually be 0.4.21 + pragma solidity ^0.4.21; contract Coin { // The keyword "public" makes those variables diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index 57556fa5..27fefd49 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -219,7 +219,7 @@ activate themselves. :: - pragma solidity ^0.4.20; // should actually be 0.4.21 + pragma solidity ^0.4.21; contract SimpleAuction { // Parameters of the auction. Times are either @@ -376,7 +376,7 @@ high or low invalid bids. :: - pragma solidity ^0.4.20; // should actually be 0.4.21 + pragma solidity ^0.4.21; contract BlindAuction { struct Bid { @@ -529,7 +529,7 @@ Safe Remote Purchase :: - pragma solidity ^0.4.20; // should actually be 0.4.21 + pragma solidity ^0.4.21; contract Purchase { uint public value; diff --git a/docs/structure-of-a-contract.rst b/docs/structure-of-a-contract.rst index 4a0873df..df40b1d0 100644 --- a/docs/structure-of-a-contract.rst +++ b/docs/structure-of-a-contract.rst @@ -86,7 +86,7 @@ Events are convenience interfaces with the EVM logging facilities. :: - pragma solidity ^0.4.20; // should actually be 0.4.21 + pragma solidity ^0.4.21; contract SimpleAuction { event HighestBidIncreased(address bidder, uint amount); // Event diff --git a/docs/types.rst b/docs/types.rst index 3611bc3e..e704687e 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -470,7 +470,7 @@ Example that shows how to use internal function types:: Another example that uses external function types:: - pragma solidity ^0.4.20; // should actually be 0.4.21 + pragma solidity ^0.4.21; contract Oracle { struct Request { -- cgit v1.2.3 From cde4e3172be3fe87e4e229f28ad96b0844fa4bd4 Mon Sep 17 00:00:00 2001 From: bernard peh Date: Fri, 2 Mar 2018 23:40:17 +1100 Subject: change the language so that it doesn't sound like send and transfer is providing the stipend --- docs/contracts.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index cfd5256a..3df722db 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -523,7 +523,12 @@ Ether (without data). Additionally, in order to receive Ether, the fallback func must be marked ``payable``. If no such function exists, the contract cannot receive Ether through regular transactions. -It is worth nothing that the "send" or "transfer" function only has a 2300 gas stipend. If the fallback function is called by another contract account using the send/transfer function (eg address.transfer(1 ether)), then the fallback function only has 2300 gas to spend, meaning that it cannot do much other than basic logging. +In the worst case, the fallback function can only rely on 2300 gas being available (for example when send or transfer is used), leaving not much room to perform other operations except basic logging. The following operations will consume more gas than the 2300 gas stipend: + +- Writing to storage +- Creating a contract +- Calling an external function which consumes a large amount of gas +- Sending Ether Like any function, the fallback function can execute complex operations as long as there is enough gas passed on to it. -- cgit v1.2.3 From c2730a48931aca8acb5d89ad9e946f16eb9463c3 Mon Sep 17 00:00:00 2001 From: Furkan Ayhan Date: Sun, 4 Mar 2018 13:23:07 +0300 Subject: Correct warning message in Pure Functions doc wrong commit that caused this: (https://github.com/ethereum/solidity/commit/64eaff64200d166bdd48f81bceefec9bc83db72f#diff-754689a291c0a19b500c31eb6c1d30c7R506) --- docs/contracts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 3df722db..12b785d5 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -503,7 +503,7 @@ In addition to the list of state modifying statements explained above, the follo } .. warning:: - Before version 0.4.17 the compiler didn't enforce that ``view`` is not reading the state. + Before version 0.4.17 the compiler didn't enforce that ``pure`` is not reading the state. .. index:: ! fallback function, function;fallback -- cgit v1.2.3 From 5d74b862a390ccf4f2471fb8b9941314b4727c11 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Sun, 4 Mar 2018 14:41:27 +0100 Subject: This z3 option is necessary for good solving performance --- libsolidity/formal/Z3Interface.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libsolidity/formal/Z3Interface.cpp b/libsolidity/formal/Z3Interface.cpp index 769e6edb..125da00d 100644 --- a/libsolidity/formal/Z3Interface.cpp +++ b/libsolidity/formal/Z3Interface.cpp @@ -28,6 +28,7 @@ using namespace dev::solidity::smt; Z3Interface::Z3Interface(): m_solver(m_context) { + z3::set_param("rewriter.pull_cheap_ite", true); } void Z3Interface::reset() -- cgit v1.2.3 From c633c0eacba12e940b12c6ff58b5c6c55dc0122c Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 2 Mar 2018 17:58:27 +0100 Subject: Move msg.gas to global function gasleft(). Closes #2971. --- docs/contracts.rst | 2 +- docs/miscellaneous.rst | 3 +- docs/units-and-global-variables.rst | 3 +- libsolidity/analysis/GlobalContext.cpp | 1 + libsolidity/ast/Types.cpp | 14 ++++++--- libsolidity/ast/Types.h | 3 +- libsolidity/codegen/ExpressionCompiler.cpp | 7 ++++- test/libsolidity/SolidityEndToEndTest.cpp | 19 +++++++++++++ test/libsolidity/SolidityExpressionCompiler.cpp | 33 ++++++++++++++++++++++ test/libsolidity/SolidityNameAndTypeResolution.cpp | 25 ++++++++++++++++ test/libsolidity/ViewPureChecker.cpp | 1 + 11 files changed, 102 insertions(+), 9 deletions(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 967eb2c8..5754b734 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -402,7 +402,7 @@ State variables can be declared as ``constant``. In this case, they have to be assigned from an expression which is a constant at compile time. Any expression that accesses storage, blockchain data (e.g. ``now``, ``this.balance`` or ``block.number``) or -execution data (``msg.gas``) or make calls to external contracts are disallowed. Expressions +execution data (``msg.value``) or make calls to external contracts are disallowed. Expressions that might have a side-effect on memory allocation are allowed, but those that might have a side-effect on other memory objects are not. The built-in functions ``keccak256``, ``sha256``, ``ripemd160``, ``ecrecover``, ``addmod`` and ``mulmod`` diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 70ed6201..7d3d058b 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -315,8 +315,9 @@ Global Variables - ``block.gaslimit`` (``uint``): current block gaslimit - ``block.number`` (``uint``): current block number - ``block.timestamp`` (``uint``): current block timestamp +- ``gasleft() returns (uint256)``: remaining gas - ``msg.data`` (``bytes``): complete calldata -- ``msg.gas`` (``uint``): remaining gas +- ``msg.gas`` (``uint``): remaining gas - deprecated in version 0.4.21 and to be replaced by ``gasleft()`` - ``msg.sender`` (``address``): sender of the message (current call) - ``msg.value`` (``uint``): number of wei sent with the message - ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``) diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index cc4d4446..1b58b1e8 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -58,8 +58,9 @@ Block and Transaction Properties - ``block.gaslimit`` (``uint``): current block gaslimit - ``block.number`` (``uint``): current block number - ``block.timestamp`` (``uint``): current block timestamp as seconds since unix epoch +- ``gasleft() returns (uint256)``: remaining gas - ``msg.data`` (``bytes``): complete calldata -- ``msg.gas`` (``uint``): remaining gas +- ``msg.gas`` (``uint``): remaining gas - deprecated in version 0.4.21 and to be replaced by ``gasleft()`` - ``msg.sender`` (``address``): sender of the message (current call) - ``msg.sig`` (``bytes4``): first four bytes of the calldata (i.e. function identifier) - ``msg.value`` (``uint``): number of wei sent with the message diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index fd39d860..34cb61d8 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -39,6 +39,7 @@ m_magicVariables(vector>{ make_shared("assert", make_shared(strings{"bool"}, strings{}, FunctionType::Kind::Assert, false, StateMutability::Pure)), make_shared("block", make_shared(MagicType::Kind::Block)), make_shared("ecrecover", make_shared(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover, false, StateMutability::Pure)), + make_shared("gasleft", make_shared(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft, false, StateMutability::View)), make_shared("keccak256", make_shared(strings(), strings{"bytes32"}, FunctionType::Kind::SHA3, true, StateMutability::Pure)), make_shared("log0", make_shared(strings{"bytes32"}, strings{}, FunctionType::Kind::Log0)), make_shared("log1", make_shared(strings{"bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log1)), diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 771ae643..bde0dd6e 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -3000,8 +3000,10 @@ bool MagicType::operator==(Type const& _other) const return other.m_kind == m_kind; } -MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const +MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const *_contract) const { + solAssert(_contract, ""); + const bool v050 = _contract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); switch (m_kind) { case Kind::Block: @@ -3014,13 +3016,17 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const {"gaslimit", make_shared(256)} }); case Kind::Message: - return MemberList::MemberMap({ + { + std::vector members = { {"sender", make_shared(160, IntegerType::Modifier::Address)}, - {"gas", make_shared(256)}, {"value", make_shared(256)}, {"data", make_shared(DataLocation::CallData)}, {"sig", make_shared(4)} - }); + }; + if (!v050) + members.emplace_back("gas", make_shared(256)); + return MemberList::MemberMap(members); + } case Kind::Transaction: return MemberList::MemberMap({ {"origin", make_shared(160, IntegerType::Modifier::Address)}, diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 7985521e..c20a025f 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -902,7 +902,8 @@ public: ByteArrayPush, ///< .push() to a dynamically sized byte array in storage ObjectCreation, ///< array creation using new Assert, ///< assert() - Require ///< require() + Require, ///< require() + GasLeft ///< gasleft() }; virtual Category category() const override { return Category::Function; } diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 61920592..d3599367 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -906,6 +906,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << success; break; } + case FunctionType::Kind::GasLeft: + m_context << Instruction::GAS; + break; default: solAssert(false, "Invalid function type."); } @@ -921,6 +924,8 @@ bool ExpressionCompiler::visit(NewExpression const&) bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) { + const bool v050 = m_context.experimentalFeatureActive(ExperimentalFeature::V050); + CompilerContext::LocationSetter locationSetter(m_context, _memberAccess); // Check whether the member is a bound function. ASTString const& member = _memberAccess.memberName(); @@ -1135,7 +1140,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) m_context << Instruction::CALLVALUE; else if (member == "origin") m_context << Instruction::ORIGIN; - else if (member == "gas") + else if (!v050 && member == "gas") m_context << Instruction::GAS; else if (member == "gasprice") m_context << Instruction::GASPRICE; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index c352a2c2..e61afb0e 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2481,6 +2481,25 @@ BOOST_AUTO_TEST_CASE(gas_and_value_basic) BOOST_REQUIRE(callContractFunction("checkState()") == encodeArgs(false, 20 - 5)); } +BOOST_AUTO_TEST_CASE(gas_left) +{ + char const* sourceCode = R"( + contract test { + function getGasLeft() public returns (uint256 val) { return msg.gas; } + } + )"; + compileAndRun(sourceCode); + BOOST_REQUIRE(callContractFunction("getGasLeft()") == encodeArgs(99978604)); + + sourceCode = R"( + contract test { + function getGasLeft() public returns (uint256 val) { return gasleft(); } + } + )"; + compileAndRun(sourceCode); + BOOST_REQUIRE(callContractFunction("getGasLeft()") == encodeArgs(99978604)); +} + BOOST_AUTO_TEST_CASE(value_complex) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index e2a0c3cd..83129ba1 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -515,6 +515,39 @@ BOOST_AUTO_TEST_CASE(blockhash) BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } +BOOST_AUTO_TEST_CASE(gas_left) +{ + char const* sourceCode = R"( + contract test { + function f() returns (uint256 val) { + return msg.gas; + } + } + )"; + bytes code = compileFirstExpression( + sourceCode, {}, {}, + {make_shared("msg", make_shared(MagicType::Kind::Message))} + ); + + bytes expectation({byte(Instruction::GAS)}); + BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); + + sourceCode = R"( + contract test { + function f() returns (uint256 val) { + return gasleft(); + } + } + )"; + code = compileFirstExpression( + sourceCode, {}, {}, + {make_shared("gasleft", make_shared(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft))} + ); + + expectation = bytes({byte(Instruction::GAS)}); + BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index eeefe818..469211d3 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7409,6 +7409,31 @@ BOOST_AUTO_TEST_CASE(builtin_reject_gas) CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup"); } +BOOST_AUTO_TEST_CASE(gas_left) +{ + char const* text = R"( + contract C { + function f() public returns (uint256 val) { return msg.gas; } + } + )"; + CHECK_SUCCESS(text); + + text = R"( + contract C { + function f() public returns (uint256 val) { return gasleft(); } + } + )"; + CHECK_SUCCESS(text); + + text = R"( + pragma experimental "v0.5.0"; + contract C { + function f() public returns (uint256 val) { return msg.gas; } + } + )"; + CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup in msg"); +} + BOOST_AUTO_TEST_CASE(builtin_reject_value) { char const* text = R"( diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index 2599ca28..609a0154 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -109,6 +109,7 @@ BOOST_AUTO_TEST_CASE(environment_access) "block.difficulty", "block.number", "block.gaslimit", + "gasleft()", "msg.gas", "msg.value", "msg.sender", -- cgit v1.2.3 From 6ec4517929e8c0eca022f4771ba217db5d80beed Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 1 Mar 2018 12:06:36 +0100 Subject: Use EVM version in gas meter and optimizer. --- libevmasm/Assembly.cpp | 4 +- libevmasm/Assembly.h | 5 +- libevmasm/ConstantOptimiser.cpp | 11 +- libevmasm/ConstantOptimiser.h | 4 + libevmasm/GasMeter.cpp | 32 ++-- libevmasm/GasMeter.h | 44 +++-- libevmasm/PathGasMeter.cpp | 6 +- libevmasm/PathGasMeter.h | 8 +- liblll/Compiler.cpp | 17 +- liblll/Compiler.h | 9 +- libsolidity/codegen/CompilerContext.h | 2 +- libsolidity/codegen/ContractCompiler.cpp | 4 +- libsolidity/codegen/ContractCompiler.h | 2 +- libsolidity/codegen/ExpressionCompiler.cpp | 2 +- libsolidity/interface/CompilerStack.cpp | 9 +- libsolidity/interface/GasEstimator.cpp | 12 +- libsolidity/interface/GasEstimator.h | 22 ++- lllc/main.cpp | 4 +- solc/CommandLineInterface.cpp | 13 +- solc/CommandLineInterface.h | 5 +- test/contracts/LLL_ENS.cpp | 2 +- test/contracts/LLL_ERC20.cpp | 2 +- test/fuzzer.cpp | 1 + test/libevmasm/Optimiser.cpp | 4 +- test/liblll/Compiler.cpp | 17 +- test/liblll/EndToEndTest.cpp | 276 +++++++++++++++++------------ test/liblll/ExecutionFramework.h | 2 +- test/libsolidity/GasMeter.cpp | 10 +- 28 files changed, 328 insertions(+), 201 deletions(-) diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index b9fedf26..bd4ebf59 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -353,7 +353,7 @@ void Assembly::injectStart(AssemblyItem const& _i) m_items.insert(m_items.begin(), _i); } -Assembly& Assembly::optimise(bool _enable, bool _isCreation, size_t _runs) +Assembly& Assembly::optimise(bool _enable, EVMVersion _evmVersion, bool _isCreation, size_t _runs) { OptimiserSettings settings; settings.isCreation = _isCreation; @@ -365,6 +365,7 @@ Assembly& Assembly::optimise(bool _enable, bool _isCreation, size_t _runs) settings.runCSE = true; settings.runConstantOptimiser = true; } + settings.evmVersion = _evmVersion; settings.expectedExecutionsPerDeployment = _runs; optimise(settings); return *this; @@ -482,6 +483,7 @@ map Assembly::optimiseInternal( ConstantOptimisationMethod::optimiseConstants( _settings.isCreation, _settings.isCreation ? 1 : _settings.expectedExecutionsPerDeployment, + _settings.evmVersion, *this, m_items ); diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index 885192e4..367c6daa 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -23,6 +23,8 @@ #include #include +#include + #include #include #include @@ -107,6 +109,7 @@ public: bool runDeduplicate = false; bool runCSE = false; bool runConstantOptimiser = false; + solidity::EVMVersion evmVersion; /// This specifies an estimate on how often each opcode in this assembly will be executed, /// i.e. use a small value to optimise for size and a large value to optimise for runtime gas usage. size_t expectedExecutionsPerDeployment = 200; @@ -120,7 +123,7 @@ public: /// @a _runs specifes an estimate on how often each opcode in this assembly will be executed, /// i.e. use a small value to optimise for size and a large value to optimise for runtime. /// If @a _enable is not set, will perform some simple peephole optimizations. - Assembly& optimise(bool _enable, bool _isCreation = true, size_t _runs = 200); + Assembly& optimise(bool _enable, EVMVersion _evmVersion, bool _isCreation = true, size_t _runs = 200); /// Create a text representation of the assembly. std::string assemblyString( diff --git a/libevmasm/ConstantOptimiser.cpp b/libevmasm/ConstantOptimiser.cpp index 2efd2dc9..d0b6843c 100644 --- a/libevmasm/ConstantOptimiser.cpp +++ b/libevmasm/ConstantOptimiser.cpp @@ -29,6 +29,7 @@ using namespace dev::eth; unsigned ConstantOptimisationMethod::optimiseConstants( bool _isCreation, size_t _runs, + solidity::EVMVersion _evmVersion, Assembly& _assembly, AssemblyItems& _items ) @@ -48,6 +49,7 @@ unsigned ConstantOptimisationMethod::optimiseConstants( params.multiplicity = it.second; params.isCreation = _isCreation; params.runs = _runs; + params.evmVersion = _evmVersion; LiteralMethod lit(params, item.data()); bigint literalGas = lit.gasNeeded(); CodeCopyMethod copy(params, item.data()); @@ -80,7 +82,12 @@ bigint ConstantOptimisationMethod::simpleRunGas(AssemblyItems const& _items) if (item.type() == Push) gas += GasMeter::runGas(Instruction::PUSH1); else if (item.type() == Operation) - gas += GasMeter::runGas(item.instruction()); + { + if (item.instruction() == Instruction::EXP) + gas += GasCosts::expGas; + else + gas += GasMeter::runGas(item.instruction()); + } return gas; } @@ -286,7 +293,7 @@ bigint ComputeMethod::gasNeeded(AssemblyItems const& _routine) const { size_t numExps = count(_routine.begin(), _routine.end(), Instruction::EXP); return combineGas( - simpleRunGas(_routine) + numExps * (GasCosts::expGas + GasCosts::expByteGas), + simpleRunGas(_routine) + numExps * (GasCosts::expGas + GasCosts::expByteGas(m_params.evmVersion)), // Data gas for routine: Some bytes are zero, but we ignore them. bytesRequired(_routine) * (m_params.isCreation ? GasCosts::txDataNonZeroGas : GasCosts::createDataGas), 0 diff --git a/libevmasm/ConstantOptimiser.h b/libevmasm/ConstantOptimiser.h index c450b0b4..9b60b26b 100644 --- a/libevmasm/ConstantOptimiser.h +++ b/libevmasm/ConstantOptimiser.h @@ -23,6 +23,8 @@ #include +#include + #include #include #include @@ -50,6 +52,7 @@ public: static unsigned optimiseConstants( bool _isCreation, size_t _runs, + solidity::EVMVersion _evmVersion, Assembly& _assembly, AssemblyItems& _items ); @@ -59,6 +62,7 @@ public: bool isCreation; ///< Whether this is called during contract creation or runtime. size_t runs; ///< Estimated number of calls per opcode oven the lifetime of the contract. size_t multiplicity; ///< Number of times the constant appears in the code. + solidity::EVMVersion evmVersion; ///< Version of the EVM }; explicit ConstantOptimisationMethod(Params const& _params, u256 const& _value): diff --git a/libevmasm/GasMeter.cpp b/libevmasm/GasMeter.cpp index 543f1cbc..caa06fc0 100644 --- a/libevmasm/GasMeter.cpp +++ b/libevmasm/GasMeter.cpp @@ -61,7 +61,6 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ case Operation: { ExpressionClasses& classes = m_state->expressionClasses(); - gas = runGas(_item.instruction()); switch (_item.instruction()) { case Instruction::SSTORE: @@ -72,26 +71,29 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ m_state->storageContent().count(slot) && classes.knownNonZero(m_state->storageContent().at(slot)) )) - gas += GasCosts::sstoreResetGas; //@todo take refunds into account + gas = GasCosts::sstoreResetGas; //@todo take refunds into account else - gas += GasCosts::sstoreSetGas; + gas = GasCosts::sstoreSetGas; break; } case Instruction::SLOAD: - gas += GasCosts::sloadGas; + gas = GasCosts::sloadGas(m_evmVersion); break; case Instruction::RETURN: case Instruction::REVERT: + gas = runGas(_item.instruction()); gas += memoryGas(0, -1); break; case Instruction::MLOAD: case Instruction::MSTORE: + gas = runGas(_item.instruction()); gas += memoryGas(classes.find(Instruction::ADD, { m_state->relativeStackElement(0), classes.find(AssemblyItem(32)) })); break; case Instruction::MSTORE8: + gas = runGas(_item.instruction()); gas += memoryGas(classes.find(Instruction::ADD, { m_state->relativeStackElement(0), classes.find(AssemblyItem(1)) @@ -105,10 +107,15 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ case Instruction::CALLDATACOPY: case Instruction::CODECOPY: case Instruction::RETURNDATACOPY: + gas = runGas(_item.instruction()); gas += memoryGas(0, -2); gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-2)); break; + case Instruction::EXTCODESIZE: + gas = GasCosts::extCodeGas(m_evmVersion); + break; case Instruction::EXTCODECOPY: + gas = GasCosts::extCodeGas(m_evmVersion); gas += memoryGas(-1, -3); gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-3)); break; @@ -137,7 +144,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ gas = GasConsumption::infinite(); else { - gas = GasCosts::callGas; + gas = GasCosts::callGas(m_evmVersion); if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(0))) gas += (*value); else @@ -155,7 +162,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ break; } case Instruction::SELFDESTRUCT: - gas = GasCosts::selfdestructGas; + gas = GasCosts::selfdestructGas(m_evmVersion); gas += GasCosts::callNewAccountGas; // We very rarely know whether the address exists. break; case Instruction::CREATE: @@ -172,11 +179,15 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ case Instruction::EXP: gas = GasCosts::expGas; if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1))) - gas += GasCosts::expByteGas * (32 - (h256(*value).firstBitSet() / 8)); + gas += GasCosts::expByteGas(m_evmVersion) * (32 - (h256(*value).firstBitSet() / 8)); else - gas += GasCosts::expByteGas * 32; + gas += GasCosts::expByteGas(m_evmVersion) * 32; + break; + case Instruction::BALANCE: + gas = GasCosts::balanceGas(m_evmVersion); break; default: + gas = runGas(_item.instruction()); break; } break; @@ -241,12 +252,9 @@ unsigned GasMeter::runGas(Instruction _instruction) case Tier::Mid: return GasCosts::tier4Gas; case Tier::High: return GasCosts::tier5Gas; case Tier::Ext: return GasCosts::tier6Gas; - case Tier::Special: return GasCosts::tier7Gas; - case Tier::ExtCode: return GasCosts::extCodeGas; - case Tier::Balance: return GasCosts::balanceGas; default: break; } - assertThrow(false, OptimizerException, "Invalid gas tier."); + assertThrow(false, OptimizerException, "Invalid gas tier for instruction " + instructionInfo(_instruction).name); return 0; } diff --git a/libevmasm/GasMeter.h b/libevmasm/GasMeter.h index 2c3ecf5a..b131802f 100644 --- a/libevmasm/GasMeter.h +++ b/libevmasm/GasMeter.h @@ -21,11 +21,14 @@ #pragma once -#include -#include #include #include +#include + +#include +#include + namespace dev { namespace eth @@ -44,13 +47,25 @@ namespace GasCosts static unsigned const tier5Gas = 10; static unsigned const tier6Gas = 20; static unsigned const tier7Gas = 0; - static unsigned const extCodeGas = 700; - static unsigned const balanceGas = 400; + inline unsigned extCodeGas(EVMVersion _evmVersion) + { + return _evmVersion >= EVMVersion::tangerineWhistle() ? 700 : 20; + } + inline unsigned balanceGas(EVMVersion _evmVersion) + { + return _evmVersion >= EVMVersion::tangerineWhistle() ? 400 : 20; + } static unsigned const expGas = 10; - static unsigned const expByteGas = 50; + inline unsigned expByteGas(EVMVersion _evmVersion) + { + return _evmVersion >= EVMVersion::spuriousDragon() ? 50 : 10; + } static unsigned const keccak256Gas = 30; static unsigned const keccak256WordGas = 6; - static unsigned const sloadGas = 200; + inline unsigned sloadGas(EVMVersion _evmVersion) + { + return _evmVersion >= EVMVersion::tangerineWhistle() ? 200 : 50; + } static unsigned const sstoreSetGas = 20000; static unsigned const sstoreResetGas = 5000; static unsigned const sstoreRefundGas = 15000; @@ -59,11 +74,17 @@ namespace GasCosts static unsigned const logDataGas = 8; static unsigned const logTopicGas = 375; static unsigned const createGas = 32000; - static unsigned const callGas = 700; + inline unsigned callGas(EVMVersion _evmVersion) + { + return _evmVersion >= EVMVersion::tangerineWhistle() ? 700 : 40; + } static unsigned const callStipend = 2300; static unsigned const callValueTransferGas = 9000; static unsigned const callNewAccountGas = 25000; - static unsigned const selfdestructGas = 5000; + inline unsigned selfdestructGas(EVMVersion _evmVersion) + { + return _evmVersion >= EVMVersion::tangerineWhistle() ? 5000 : 0; + } static unsigned const selfdestructRefundGas = 24000; static unsigned const memoryGas = 3; static unsigned const quadCoeffDiv = 512; @@ -100,8 +121,8 @@ public: }; /// Constructs a new gas meter given the current state. - explicit GasMeter(std::shared_ptr const& _state, u256 const& _largestMemoryAccess = 0): - m_state(_state), m_largestMemoryAccess(_largestMemoryAccess) {} + GasMeter(std::shared_ptr const& _state, solidity::EVMVersion _evmVersion, u256 const& _largestMemoryAccess = 0): + m_state(_state), m_evmVersion(_evmVersion), m_largestMemoryAccess(_largestMemoryAccess) {} /// @returns an upper bound on the gas consumed by the given instruction and updates /// the state. @@ -110,6 +131,8 @@ public: u256 const& largestMemoryAccess() const { return m_largestMemoryAccess; } + /// @returns gas costs for simple instructions with constant gas costs (that do not + /// change with EVM versions) static unsigned runGas(Instruction _instruction); private: @@ -123,6 +146,7 @@ private: GasConsumption memoryGas(int _stackPosOffset, int _stackPosSize); std::shared_ptr m_state; + EVMVersion m_evmVersion; /// Largest point where memory was accessed since the creation of this object. u256 m_largestMemoryAccess; }; diff --git a/libevmasm/PathGasMeter.cpp b/libevmasm/PathGasMeter.cpp index c56e2f8b..3fe682b7 100644 --- a/libevmasm/PathGasMeter.cpp +++ b/libevmasm/PathGasMeter.cpp @@ -27,8 +27,8 @@ using namespace std; using namespace dev; using namespace dev::eth; -PathGasMeter::PathGasMeter(AssemblyItems const& _items): - m_items(_items) +PathGasMeter::PathGasMeter(AssemblyItems const& _items, solidity::EVMVersion _evmVersion): + m_items(_items), m_evmVersion(_evmVersion) { for (size_t i = 0; i < m_items.size(); ++i) if (m_items[i].type() == Tag) @@ -59,7 +59,7 @@ GasMeter::GasConsumption PathGasMeter::handleQueueItem() m_queue.pop_back(); shared_ptr state = path->state; - GasMeter meter(state, path->largestMemoryAccess); + GasMeter meter(state, m_evmVersion, path->largestMemoryAccess); ExpressionClasses& classes = state->expressionClasses(); GasMeter::GasConsumption gas = path->gas; size_t index = path->index; diff --git a/libevmasm/PathGasMeter.h b/libevmasm/PathGasMeter.h index 4826eac2..2527d7fb 100644 --- a/libevmasm/PathGasMeter.h +++ b/libevmasm/PathGasMeter.h @@ -21,10 +21,13 @@ #pragma once +#include + +#include + #include #include #include -#include namespace dev { @@ -50,7 +53,7 @@ struct GasPath class PathGasMeter { public: - explicit PathGasMeter(AssemblyItems const& _items); + explicit PathGasMeter(AssemblyItems const& _items, solidity::EVMVersion _evmVersion); GasMeter::GasConsumption estimateMax(size_t _startIndex, std::shared_ptr const& _state); @@ -60,6 +63,7 @@ private: std::vector> m_queue; std::map m_tagPositions; AssemblyItems const& m_items; + solidity::EVMVersion m_evmVersion; }; } diff --git a/liblll/Compiler.cpp b/liblll/Compiler.cpp index 1638f69e..f2c1b0be 100644 --- a/liblll/Compiler.cpp +++ b/liblll/Compiler.cpp @@ -19,17 +19,16 @@ * @date 2014 */ -#include "Compiler.h" -#include "Parser.h" -#include "CompilerState.h" -#include "CodeFragment.h" +#include +#include +#include +#include using namespace std; using namespace dev; using namespace dev::eth; - -bytes dev::eth::compileLLL(string const& _src, bool _opt, vector* _errors, ReadCallback const& _readFile) +bytes dev::eth::compileLLL(string const& _src, dev::solidity::EVMVersion _evmVersion, bool _opt, std::vector* _errors, dev::eth::ReadCallback const& _readFile) { try { @@ -37,7 +36,7 @@ bytes dev::eth::compileLLL(string const& _src, bool _opt, vector* _error cs.populateStandard(); auto assembly = CodeFragment::compile(_src, cs, _readFile).assembly(cs); if (_opt) - assembly = assembly.optimise(true); + assembly = assembly.optimise(true, _evmVersion); bytes ret = assembly.assemble().bytecode; for (auto i: cs.treesToKill) killBigints(i); @@ -67,7 +66,7 @@ bytes dev::eth::compileLLL(string const& _src, bool _opt, vector* _error return bytes(); } -std::string dev::eth::compileLLLToAsm(std::string const& _src, bool _opt, std::vector* _errors, ReadCallback const& _readFile) +std::string dev::eth::compileLLLToAsm(std::string const& _src, EVMVersion _evmVersion, bool _opt, std::vector* _errors, ReadCallback const& _readFile) { try { @@ -75,7 +74,7 @@ std::string dev::eth::compileLLLToAsm(std::string const& _src, bool _opt, std::v cs.populateStandard(); auto assembly = CodeFragment::compile(_src, cs, _readFile).assembly(cs); if (_opt) - assembly = assembly.optimise(true); + assembly = assembly.optimise(true, _evmVersion); string ret = assembly.assemblyString(); for (auto i: cs.treesToKill) killBigints(i); diff --git a/liblll/Compiler.h b/liblll/Compiler.h index c3395b66..06440c17 100644 --- a/liblll/Compiler.h +++ b/liblll/Compiler.h @@ -21,9 +21,12 @@ #pragma once +#include + +#include + #include #include -#include namespace dev { @@ -33,8 +36,8 @@ namespace eth using ReadCallback = std::function; std::string parseLLL(std::string const& _src); -std::string compileLLLToAsm(std::string const& _src, bool _opt = true, std::vector* _errors = nullptr, ReadCallback const& _readFile = ReadCallback()); -bytes compileLLL(std::string const& _src, bool _opt = true, std::vector* _errors = nullptr, ReadCallback const& _readFile = ReadCallback()); +std::string compileLLLToAsm(std::string const& _src, solidity::EVMVersion _evmVersion, bool _opt = true, std::vector* _errors = nullptr, ReadCallback const& _readFile = ReadCallback()); +bytes compileLLL(std::string const& _src, solidity::EVMVersion _evmVersion, bool _opt = true, std::vector* _errors = nullptr, ReadCallback const& _readFile = ReadCallback()); } } diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index e6b2484a..cf626683 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -209,7 +209,7 @@ public: void appendAuxiliaryData(bytes const& _data) { m_asm->appendAuxiliaryDataToEnd(_data); } /// Run optimisation step. - void optimise(bool _fullOptimsation, unsigned _runs = 200) { m_asm->optimise(_fullOptimsation, true, _runs); } + void optimise(bool _fullOptimsation, unsigned _runs = 200) { m_asm->optimise(_fullOptimsation, m_evmVersion, true, _runs); } /// @returns the runtime context if in creation mode and runtime context is set, nullptr otherwise. CompilerContext* runtimeContext() { return m_runtimeContext; } diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index ebb718a5..5a9498f0 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -1059,7 +1059,7 @@ void ContractCompiler::compileExpression(Expression const& _expression, TypePoin CompilerUtils(m_context).convertType(*_expression.annotation().type, *_targetType); } -eth::AssemblyPointer ContractCompiler::cloneRuntime() +eth::AssemblyPointer ContractCompiler::cloneRuntime() const { eth::Assembly a; a << Instruction::CALLDATASIZE; @@ -1070,7 +1070,7 @@ eth::AssemblyPointer ContractCompiler::cloneRuntime() // this is the address which has to be substituted by the linker. //@todo implement as special "marker" AssemblyItem. a << u256("0xcafecafecafecafecafecafecafecafecafecafe"); - a << u256(eth::GasCosts::callGas + 10) << Instruction::GAS << Instruction::SUB; + a << u256(eth::GasCosts::callGas(m_context.evmVersion()) + 10) << Instruction::GAS << Instruction::SUB; a << Instruction::DELEGATECALL; //Propagate error condition (if DELEGATECALL pushes 0 on stack). a << Instruction::ISZERO; diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index 18f31967..8559ea58 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -125,7 +125,7 @@ private: void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer()); /// @returns the runtime assembly for clone contracts. - static eth::AssemblyPointer cloneRuntime(); + eth::AssemblyPointer cloneRuntime() const; bool const m_optimise; /// Pointer to the runtime compiler in case this is a creation compiler. diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 61920592..12881d63 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1756,7 +1756,7 @@ void ExpressionCompiler::appendExternalFunctionCall( { // send all gas except the amount needed to execute "SUB" and "CALL" // @todo this retains too much gas for now, needs to be fine-tuned. - u256 gasNeededByCaller = eth::GasCosts::callGas + 10; + u256 gasNeededByCaller = eth::GasCosts::callGas(m_context.evmVersion()) + 10; if (_functionType.valueSet()) gasNeededByCaller += eth::GasCosts::callValueTransferGas; if (!existenceChecked) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index cb1ca3aa..eacfca9c 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -959,11 +959,12 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const return Json::Value(); using Gas = GasEstimator::GasConsumption; + GasEstimator gasEstimator(m_evmVersion); Json::Value output(Json::objectValue); if (eth::AssemblyItems const* items = assemblyItems(_contractName)) { - Gas executionGas = GasEstimator::functionalEstimation(*items); + Gas executionGas = gasEstimator.functionalEstimation(*items); u256 bytecodeSize(runtimeObject(_contractName).bytecode.size()); Gas codeDepositGas = bytecodeSize * eth::GasCosts::createDataGas; @@ -984,14 +985,14 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const for (auto it: contract.interfaceFunctions()) { string sig = it.second->externalSignature(); - externalFunctions[sig] = gasToJson(GasEstimator::functionalEstimation(*items, sig)); + externalFunctions[sig] = gasToJson(gasEstimator.functionalEstimation(*items, sig)); } if (contract.fallbackFunction()) /// This needs to be set to an invalid signature in order to trigger the fallback, /// without the shortcut (of CALLDATSIZE == 0), and therefore to receive the upper bound. /// An empty string ("") would work to trigger the shortcut only. - externalFunctions[""] = gasToJson(GasEstimator::functionalEstimation(*items, "INVALID")); + externalFunctions[""] = gasToJson(gasEstimator.functionalEstimation(*items, "INVALID")); if (!externalFunctions.empty()) output["external"] = externalFunctions; @@ -1007,7 +1008,7 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const size_t entry = functionEntryPoint(_contractName, *it); GasEstimator::GasConsumption gas = GasEstimator::GasConsumption::infinite(); if (entry > 0) - gas = GasEstimator::functionalEstimation(*items, entry, *it); + gas = gasEstimator.functionalEstimation(*items, entry, *it); /// TODO: This could move into a method shared with externalSignature() FunctionType type(*it); diff --git a/libsolidity/interface/GasEstimator.cpp b/libsolidity/interface/GasEstimator.cpp index 22cc0266..2139395f 100644 --- a/libsolidity/interface/GasEstimator.cpp +++ b/libsolidity/interface/GasEstimator.cpp @@ -40,7 +40,7 @@ using namespace dev::solidity; GasEstimator::ASTGasConsumptionSelfAccumulated GasEstimator::structuralEstimation( AssemblyItems const& _items, vector const& _ast -) +) const { solAssert(std::count(_ast.begin(), _ast.end(), nullptr) == 0, ""); map particularCosts; @@ -49,7 +49,7 @@ GasEstimator::ASTGasConsumptionSelfAccumulated GasEstimator::structuralEstimatio for (BasicBlock const& block: cfg.optimisedBlocks()) { solAssert(!!block.startState, ""); - GasMeter meter(block.startState->copy()); + GasMeter meter(block.startState->copy(), m_evmVersion); auto const end = _items.begin() + block.end; for (auto iter = _items.begin() + block.begin; iter != end; ++iter) particularCosts[iter->location()] += meter.estimateMax(*iter); @@ -127,7 +127,7 @@ map GasEstimator::breakToStatementLeve GasEstimator::GasConsumption GasEstimator::functionalEstimation( AssemblyItems const& _items, string const& _signature -) +) const { auto state = make_shared(); @@ -144,7 +144,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation( }); } - PathGasMeter meter(_items); + PathGasMeter meter(_items, m_evmVersion); return meter.estimateMax(0, state); } @@ -152,7 +152,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation( AssemblyItems const& _items, size_t const& _offset, FunctionDefinition const& _function -) +) const { auto state = make_shared(); @@ -167,7 +167,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation( if (parametersSize > 0) state->feedItem(swapInstruction(parametersSize)); - return PathGasMeter(_items).estimateMax(_offset, state); + return PathGasMeter(_items, m_evmVersion).estimateMax(_offset, state); } set GasEstimator::finestNodesAtLocation( diff --git a/libsolidity/interface/GasEstimator.h b/libsolidity/interface/GasEstimator.h index bf63df96..ea94d988 100644 --- a/libsolidity/interface/GasEstimator.h +++ b/libsolidity/interface/GasEstimator.h @@ -22,11 +22,14 @@ #pragma once +#include + +#include +#include + #include #include #include -#include -#include namespace dev { @@ -44,13 +47,15 @@ public: using ASTGasConsumptionSelfAccumulated = std::map>; + explicit GasEstimator(EVMVersion _evmVersion): m_evmVersion(_evmVersion) {} + /// Estimates the gas consumption for every assembly item in the given assembly and stores /// it by source location. /// @returns a mapping from each AST node to a pair of its particular and syntactically accumulated gas costs. - static ASTGasConsumptionSelfAccumulated structuralEstimation( + ASTGasConsumptionSelfAccumulated structuralEstimation( eth::AssemblyItems const& _items, std::vector const& _ast - ); + ) const; /// @returns a mapping from nodes with non-overlapping source locations to gas consumptions such that /// the following source locations are part of the mapping: /// 1. source locations of statements that do not contain other statements @@ -62,23 +67,24 @@ public: /// @returns the estimated gas consumption by the (public or external) function with the /// given signature. If no signature is given, estimates the maximum gas usage. - static GasConsumption functionalEstimation( + GasConsumption functionalEstimation( eth::AssemblyItems const& _items, std::string const& _signature = "" - ); + ) const; /// @returns the estimated gas consumption by the given function which starts at the given /// offset into the list of assembly items. /// @note this does not work correctly for recursive functions. - static GasConsumption functionalEstimation( + GasConsumption functionalEstimation( eth::AssemblyItems const& _items, size_t const& _offset, FunctionDefinition const& _function - ); + ) const; private: /// @returns the set of AST nodes which are the finest nodes at their location. static std::set finestNodesAtLocation(std::vector const& _roots); + EVMVersion m_evmVersion; }; } diff --git a/lllc/main.cpp b/lllc/main.cpp index 5679bc2b..0ca3ff13 100644 --- a/lllc/main.cpp +++ b/lllc/main.cpp @@ -133,7 +133,7 @@ int main(int argc, char** argv) } else if (mode == Binary || mode == Hex) { - auto bs = compileLLL(src, optimise ? true : false, &errors, readFileAsString); + auto bs = compileLLL(src, EVMVersion{}, optimise ? true : false, &errors, readFileAsString); if (mode == Hex) cout << toHex(bs) << endl; else if (mode == Binary) @@ -145,7 +145,7 @@ int main(int argc, char** argv) } else if (mode == Assembly) { - cout << compileLLLToAsm(src, optimise ? true : false, &errors, readFileAsString) << endl; + cout << compileLLLToAsm(src, EVMVersion{}, optimise ? true : false, &errors, readFileAsString) << endl; } for (auto const& i: errors) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index fd079656..d3d234c3 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -748,7 +748,6 @@ bool CommandLineInterface::processInput() if (!parseLibraryOption(library)) return false; - EVMVersion evmVersion; if (m_args.count(g_strEVMVersion)) { string versionOptionStr = m_args[g_strEVMVersion].as(); @@ -758,7 +757,7 @@ bool CommandLineInterface::processInput() cerr << "Invalid option for --evm-version: " << versionOptionStr << endl; return false; } - evmVersion = *versionOption; + m_evmVersion = *versionOption; } if (m_args.count(g_argAssemble) || m_args.count(g_argStrictAssembly) || m_args.count(g_argJulia)) @@ -784,7 +783,7 @@ bool CommandLineInterface::processInput() return false; } } - return assemble(evmVersion, inputLanguage, targetMachine); + return assemble(inputLanguage, targetMachine); } if (m_args.count(g_argLink)) { @@ -808,8 +807,7 @@ bool CommandLineInterface::processInput() m_compiler->addSource(sourceCode.first, sourceCode.second); if (m_args.count(g_argLibraries)) m_compiler->setLibraries(m_libraries); - if (m_args.count(g_strEVMVersion)) - m_compiler->setEVMVersion(evmVersion); + m_compiler->setEVMVersion(m_evmVersion); // TODO: Perhaps we should not compile unless requested bool optimize = m_args.count(g_argOptimize) > 0; unsigned runs = m_args[g_argOptimizeRuns].as(); @@ -968,7 +966,7 @@ void CommandLineInterface::handleAst(string const& _argStr) // FIXME: shouldn't this be done for every contract? if (m_compiler->runtimeAssemblyItems(m_compiler->lastContractName())) gasCosts = GasEstimator::breakToStatementLevel( - GasEstimator::structuralEstimation(*m_compiler->runtimeAssemblyItems(m_compiler->lastContractName()), asts), + GasEstimator(m_evmVersion).structuralEstimation(*m_compiler->runtimeAssemblyItems(m_compiler->lastContractName()), asts), asts ); @@ -1081,7 +1079,6 @@ void CommandLineInterface::writeLinkedFiles() } bool CommandLineInterface::assemble( - EVMVersion _evmVersion, AssemblyStack::Language _language, AssemblyStack::Machine _targetMachine ) @@ -1090,7 +1087,7 @@ bool CommandLineInterface::assemble( map assemblyStacks; for (auto const& src: m_sourceCodes) { - auto& stack = assemblyStacks[src.first] = AssemblyStack(_evmVersion, _language); + auto& stack = assemblyStacks[src.first] = AssemblyStack(m_evmVersion, _language); try { if (!stack.parseAndAnalyze(src.first, src.second)) diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 81117fdc..303023fc 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -54,7 +55,7 @@ private: bool link(); void writeLinkedFiles(); - bool assemble(EVMVersion _evmVersion, AssemblyStack::Language _language, AssemblyStack::Machine _targetMachine); + bool assemble(AssemblyStack::Language _language, AssemblyStack::Machine _targetMachine); void outputCompilationResults(); @@ -102,6 +103,8 @@ private: std::map m_libraries; /// Solidity compiler stack std::unique_ptr m_compiler; + /// EVM version to use + EVMVersion m_evmVersion; }; } diff --git a/test/contracts/LLL_ENS.cpp b/test/contracts/LLL_ENS.cpp index c5fe8a82..028d58c8 100644 --- a/test/contracts/LLL_ENS.cpp +++ b/test/contracts/LLL_ENS.cpp @@ -345,7 +345,7 @@ protected: if (!s_compiledEns) { vector errors; - s_compiledEns.reset(new bytes(compileLLL(ensCode, dev::test::Options::get().optimize, &errors))); + s_compiledEns.reset(new bytes(compileLLL(ensCode, dev::test::Options::get().evmVersion(), dev::test::Options::get().optimize, &errors))); BOOST_REQUIRE(errors.empty()); } sendMessage(*s_compiledEns, true); diff --git a/test/contracts/LLL_ERC20.cpp b/test/contracts/LLL_ERC20.cpp index 25665d64..60b43e4f 100644 --- a/test/contracts/LLL_ERC20.cpp +++ b/test/contracts/LLL_ERC20.cpp @@ -396,7 +396,7 @@ protected: if (!s_compiledErc20) { vector errors; - s_compiledErc20.reset(new bytes(compileLLL(erc20Code, dev::test::Options::get().optimize, &errors))); + s_compiledErc20.reset(new bytes(compileLLL(erc20Code, dev::test::Options::get().evmVersion(), dev::test::Options::get().optimize, &errors))); BOOST_REQUIRE(errors.empty()); } sendMessage(*s_compiledErc20, true); diff --git a/test/fuzzer.cpp b/test/fuzzer.cpp index 45738baa..71f38b67 100644 --- a/test/fuzzer.cpp +++ b/test/fuzzer.cpp @@ -76,6 +76,7 @@ void testConstantOptimizer() ConstantOptimisationMethod::optimiseConstants( isCreation, runs, + EVMVersion{}, assembly, const_cast(assembly.items()) ); diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index 0ab95b08..e6abcb53 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -20,6 +20,8 @@ * Tests for the Solidity optimizer. */ +#include + #include #include #include @@ -916,7 +918,7 @@ BOOST_AUTO_TEST_CASE(jumpdest_removal_subassemblies) main.append(t1.toSubAssemblyTag(subId)); main.append(u256(8)); - main.optimise(true); + main.optimise(true, dev::test::Options::get().evmVersion()); AssemblyItems expectationMain{ AssemblyItem(PushSubSize, 0), diff --git a/test/liblll/Compiler.cpp b/test/liblll/Compiler.cpp index ace97e15..6c6eae3f 100644 --- a/test/liblll/Compiler.cpp +++ b/test/liblll/Compiler.cpp @@ -20,11 +20,16 @@ * Unit tests for the LLL compiler. */ +#include + +#include + +#include + +#include + #include #include -#include -#include -#include using namespace std; @@ -41,7 +46,7 @@ namespace bool successCompile(string const& _sourceCode) { vector errors; - bytes bytecode = eth::compileLLL(_sourceCode, false, &errors); + bytes bytecode = eth::compileLLL(_sourceCode, dev::test::Options::get().evmVersion(), false, &errors); if (!errors.empty()) return false; if (bytecode.empty()) @@ -353,7 +358,7 @@ BOOST_AUTO_TEST_CASE(valid_opcodes_functional) for (size_t i = 0; i < opcodes_bytecode.size(); i++) { vector errors; - bytes code = eth::compileLLL(opcodes_lll[i], false, &errors); + bytes code = eth::compileLLL(opcodes_lll[i], dev::test::Options::get().evmVersion(), false, &errors); BOOST_REQUIRE_MESSAGE(errors.empty(), opcodes_lll[i]); @@ -641,7 +646,7 @@ BOOST_AUTO_TEST_CASE(valid_opcodes_asm) for (size_t i = 0; i < opcodes_bytecode.size(); i++) { vector errors; - bytes code = eth::compileLLL(opcodes_lll[i], false, &errors); + bytes code = eth::compileLLL(opcodes_lll[i], dev::test::Options::get().evmVersion(), false, &errors); BOOST_REQUIRE_MESSAGE(errors.empty(), opcodes_lll[i]); diff --git a/test/liblll/EndToEndTest.cpp b/test/liblll/EndToEndTest.cpp index 1a5bb490..e5e70cf8 100644 --- a/test/liblll/EndToEndTest.cpp +++ b/test/liblll/EndToEndTest.cpp @@ -20,10 +20,13 @@ * End to end tests for LLL. */ +#include +#include + +#include + #include #include -#include -#include using namespace std; @@ -583,24 +586,34 @@ BOOST_AUTO_TEST_CASE(allgas) BOOST_AUTO_TEST_CASE(send_two_args) { - char const* sourceCode = R"( - (returnlll - (send 0xdead 42)) - )"; - compileAndRun(sourceCode); - callFallbackWithValue(42); - BOOST_CHECK(balanceAt(Address(0xdead)) == 42); + // "send" does not retain enough gas to be able to pay for account creation. + // Disabling for non-tangerineWhistle VMs. + if (dev::test::Options::get().evmVersion().canOverchargeGasForCall()) + { + char const* sourceCode = R"( + (returnlll + (send 0xdead 42)) + )"; + compileAndRun(sourceCode); + callFallbackWithValue(42); + BOOST_CHECK(balanceAt(Address(0xdead)) == 42); + } } BOOST_AUTO_TEST_CASE(send_three_args) { - char const* sourceCode = R"( - (returnlll - (send allgas 0xdead 42)) - )"; - compileAndRun(sourceCode); - callFallbackWithValue(42); - BOOST_CHECK(balanceAt(Address(0xdead)) == 42); + // "send" does not retain enough gas to be able to pay for account creation. + // Disabling for non-tangerineWhistle VMs. + if (dev::test::Options::get().evmVersion().canOverchargeGasForCall()) + { + char const* sourceCode = R"( + (returnlll + (send allgas 0xdead 42)) + )"; + compileAndRun(sourceCode); + callFallbackWithValue(42); + BOOST_CHECK(balanceAt(Address(0xdead)) == 42); + } } // Regression test for edge case that previously failed @@ -708,56 +721,76 @@ BOOST_AUTO_TEST_CASE(msg_four_args) BOOST_AUTO_TEST_CASE(msg_three_args) { - char const* sourceCode = R"( - (returnlll - (seq - (when (= 0 (calldatasize)) - (return (msg (address) 42 0xff))) - (return (callvalue)))) - )"; - compileAndRun(sourceCode); - BOOST_CHECK(callFallbackWithValue(42) == encodeArgs(u256(42))); + // "msg" does not retain enough gas. + // Disabling for non-tangerineWhistle VMs. + if (dev::test::Options::get().evmVersion().canOverchargeGasForCall()) + { + char const* sourceCode = R"( + (returnlll + (seq + (when (= 0 (calldatasize)) + (return (msg (address) 42 0xff))) + (return (callvalue)))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallbackWithValue(42) == encodeArgs(u256(42))); + } } BOOST_AUTO_TEST_CASE(msg_two_args) { - char const* sourceCode = R"( - (returnlll - (seq - (when (= 0 (calldatasize)) - (return (msg (address) 0xff))) - (return 42))) - )"; - compileAndRun(sourceCode); - BOOST_CHECK(callFallback() == encodeArgs(u256(42))); + // "msg" does not retain enough gas. + // Disabling for non-tangerineWhistle VMs. + if (dev::test::Options::get().evmVersion().canOverchargeGasForCall()) + { + char const* sourceCode = R"( + (returnlll + (seq + (when (= 0 (calldatasize)) + (return (msg (address) 0xff))) + (return 42))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs(u256(42))); + } } BOOST_AUTO_TEST_CASE(create_one_arg) { - char const* sourceCode = R"( - (returnlll - (seq - (call allgas - (create (returnlll (return 42))) - 0 0 0 0x00 0x20) - (return 0x00 0x20))) - )"; - compileAndRun(sourceCode); - BOOST_CHECK(callFallback() == encodeArgs(u256(42))); + // "call" does not retain enough gas. + // Disabling for non-tangerineWhistle VMs. + if (dev::test::Options::get().evmVersion().canOverchargeGasForCall()) + { + char const* sourceCode = R"( + (returnlll + (seq + (call allgas + (create (returnlll (return 42))) + 0 0 0 0x00 0x20) + (return 0x00 0x20))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs(u256(42))); + } } BOOST_AUTO_TEST_CASE(create_two_args) { - char const* sourceCode = R"( - (returnlll - (seq - (call allgas - (create 42 (returnlll (return (balance (address))))) - 0 0 0 0x00 0x20) - (return 0x00 0x20))) - )"; - compileAndRun(sourceCode); - BOOST_CHECK(callFallbackWithValue(42) == encodeArgs(u256(42))); + // "call" does not retain enough gas. + // Disabling for non-tangerineWhistle VMs. + if (dev::test::Options::get().evmVersion().canOverchargeGasForCall()) + { + char const* sourceCode = R"( + (returnlll + (seq + (call allgas + (create 42 (returnlll (return (balance (address))))) + 0 0 0 0x00 0x20) + (return 0x00 0x20))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallbackWithValue(42) == encodeArgs(u256(42))); + } } BOOST_AUTO_TEST_CASE(sha3_two_args) @@ -822,77 +855,102 @@ BOOST_AUTO_TEST_CASE(makeperm) // Covers makeperm (implicit), permcount and perm BOOST_AUTO_TEST_CASE(ecrecover) { - char const* sourceCode = R"( - (returnlll - (return - (ecrecover - ; Hash of 'hello world' - 0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad - ; v = 1 + 27 - 0x1c - ; r - 0xdebaaa0cddb321b2dcaaf846d39605de7b97e77ba6106587855b9106cb104215 - ; s - 0x61a22d94fa8b8a687ff9c911c844d1c016d1a685a9166858f9c7c1bc85128aca))) - )"; - compileAndRun(sourceCode); - BOOST_CHECK(callFallback() == encodeArgs(fromHex("0x8743523d96a1b2cbe0c6909653a56da18ed484af"))); + // "ecrecover" does not retain enough gas. + // Disabling for non-tangerineWhistle VMs. + if (dev::test::Options::get().evmVersion().canOverchargeGasForCall()) + { + char const* sourceCode = R"( + (returnlll + (return + (ecrecover + ; Hash of 'hello world' + 0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad + ; v = 1 + 27 + 0x1c + ; r + 0xdebaaa0cddb321b2dcaaf846d39605de7b97e77ba6106587855b9106cb104215 + ; s + 0x61a22d94fa8b8a687ff9c911c844d1c016d1a685a9166858f9c7c1bc85128aca))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs(fromHex("0x8743523d96a1b2cbe0c6909653a56da18ed484af"))); + } } BOOST_AUTO_TEST_CASE(sha256_two_args) { - char const* sourceCode = R"( - (returnlll - (seq - (lit 0x20 "abcdefghijklmnopqrstuvwxyzABCDEF") - (lit 0x40 "GHIJKLMNOPQRSTUVWXYZ0123456789?!") - (sha256 0x20 0x40) - (return 0x00 0x20))) - )"; - compileAndRun(sourceCode); - BOOST_CHECK(callFallback() == encodeArgs( - fromHex("0xcf25a9fe3d86ae228c226c81d2d8c64c687cd6dc4586d10d8e7e4e5b6706d429"))); + // "sha256" does not retain enough gas. + // Disabling for non-tangerineWhistle VMs. + if (dev::test::Options::get().evmVersion().canOverchargeGasForCall()) + { + char const* sourceCode = R"( + (returnlll + (seq + (lit 0x20 "abcdefghijklmnopqrstuvwxyzABCDEF") + (lit 0x40 "GHIJKLMNOPQRSTUVWXYZ0123456789?!") + (sha256 0x20 0x40) + (return 0x00 0x20))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs( + fromHex("0xcf25a9fe3d86ae228c226c81d2d8c64c687cd6dc4586d10d8e7e4e5b6706d429"))); + } } BOOST_AUTO_TEST_CASE(ripemd160_two_args) { - char const* sourceCode = R"( - (returnlll - (seq - (lit 0x20 "abcdefghijklmnopqrstuvwxyzABCDEF") - (lit 0x40 "GHIJKLMNOPQRSTUVWXYZ0123456789?!") - (ripemd160 0x20 0x40) - (return 0x00 0x20))) - )"; - compileAndRun(sourceCode); - BOOST_CHECK(callFallback() == encodeArgs( - fromHex("0x36c6b90a49e17d4c1e1b0e634ec74124d9b207da"))); + // "ripemd160" does not retain enough gas. + // Disabling for non-tangerineWhistle VMs. + if (dev::test::Options::get().evmVersion().canOverchargeGasForCall()) + { + char const* sourceCode = R"( + (returnlll + (seq + (lit 0x20 "abcdefghijklmnopqrstuvwxyzABCDEF") + (lit 0x40 "GHIJKLMNOPQRSTUVWXYZ0123456789?!") + (ripemd160 0x20 0x40) + (return 0x00 0x20))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs( + fromHex("0x36c6b90a49e17d4c1e1b0e634ec74124d9b207da"))); + } } BOOST_AUTO_TEST_CASE(sha256_one_arg) { - char const* sourceCode = R"( - (returnlll - (seq - (sha256 0x6162636465666768696a6b6c6d6e6f707172737475767778797a414243444546) - (return 0x00 0x20))) - )"; - compileAndRun(sourceCode); - BOOST_CHECK(callFallback() == encodeArgs( - fromHex("0xcfd2f1fad75a1978da0a444883db7251414b139f31f5a04704c291fdb0e175e6"))); + // "sha256" does not retain enough gas. + // Disabling for non-tangerineWhistle VMs. + if (dev::test::Options::get().evmVersion().canOverchargeGasForCall()) + { + char const* sourceCode = R"( + (returnlll + (seq + (sha256 0x6162636465666768696a6b6c6d6e6f707172737475767778797a414243444546) + (return 0x00 0x20))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs( + fromHex("0xcfd2f1fad75a1978da0a444883db7251414b139f31f5a04704c291fdb0e175e6"))); + } } BOOST_AUTO_TEST_CASE(ripemd160_one_arg) { - char const* sourceCode = R"( - (returnlll - (seq - (ripemd160 0x6162636465666768696a6b6c6d6e6f707172737475767778797a414243444546) - (return 0x00 0x20))) - )"; - compileAndRun(sourceCode); - BOOST_CHECK(callFallback() == encodeArgs( - fromHex("0xac5ab22e07b0fb80c69b6207902f725e2507e546"))); + // "ripemd160" does not retain enough gas. + // Disabling for non-tangerineWhistle VMs. + if (dev::test::Options::get().evmVersion().canOverchargeGasForCall()) + { + char const* sourceCode = R"( + (returnlll + (seq + (ripemd160 0x6162636465666768696a6b6c6d6e6f707172737475767778797a414243444546) + (return 0x00 0x20))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs( + fromHex("0xac5ab22e07b0fb80c69b6207902f725e2507e546"))); + } } BOOST_AUTO_TEST_CASE(wei_szabo_finney_ether) diff --git a/test/liblll/ExecutionFramework.h b/test/liblll/ExecutionFramework.h index 58e1f0ad..ae5cd988 100644 --- a/test/liblll/ExecutionFramework.h +++ b/test/liblll/ExecutionFramework.h @@ -56,7 +56,7 @@ public: BOOST_REQUIRE(_libraryAddresses.empty()); std::vector errors; - bytes bytecode = eth::compileLLL(_sourceCode, m_optimize, &errors); + bytes bytecode = eth::compileLLL(_sourceCode, dev::test::Options::get().evmVersion(), m_optimize, &errors); if (!errors.empty()) { for (auto const& error: errors) diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index 105a0398..fd2017f9 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -56,7 +56,7 @@ public: ASTNode const& sourceUnit = m_compiler.ast(""); BOOST_REQUIRE(items != nullptr); m_gasCosts = GasEstimator::breakToStatementLevel( - GasEstimator::structuralEstimation(*items, vector({&sourceUnit})), + GasEstimator(dev::test::Options::get().evmVersion()).structuralEstimation(*items, vector({&sourceUnit})), {&sourceUnit} ); } @@ -65,7 +65,7 @@ public: { compileAndRun(_sourceCode); auto state = make_shared(); - PathGasMeter meter(*m_compiler.assemblyItems(m_compiler.lastContractName())); + PathGasMeter meter(*m_compiler.assemblyItems(m_compiler.lastContractName()), dev::test::Options::get().evmVersion()); GasMeter::GasConsumption gas = meter.estimateMax(0, state); u256 bytecodeSize(m_compiler.runtimeObject(m_compiler.lastContractName()).bytecode.size()); // costs for deployment @@ -74,7 +74,7 @@ public: gas += gasForTransaction(m_compiler.object(m_compiler.lastContractName()).bytecode, true); BOOST_REQUIRE(!gas.isInfinite); - BOOST_CHECK(gas.value == m_gasUsed); + BOOST_CHECK_EQUAL(gas.value, m_gasUsed); } /// Compares the gas computed by PathGasMeter for the given signature (but unknown arguments) @@ -91,12 +91,12 @@ public: gas = max(gas, gasForTransaction(hash.asBytes() + arguments, false)); } - gas += GasEstimator::functionalEstimation( + gas += GasEstimator(dev::test::Options::get().evmVersion()).functionalEstimation( *m_compiler.runtimeAssemblyItems(m_compiler.lastContractName()), _sig ); BOOST_REQUIRE(!gas.isInfinite); - BOOST_CHECK(gas.value == m_gasUsed); + BOOST_CHECK_EQUAL(gas.value, m_gasUsed); } static GasMeter::GasConsumption gasForTransaction(bytes const& _data, bool _isCreation) -- cgit v1.2.3 From 477571e19015f1c7806526d58d0ddffd91fe5823 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 26 Feb 2018 19:22:59 +0100 Subject: Do not warn about 0.5.0 experimental pragma. --- Changelog.md | 1 + libsolidity/ast/ExperimentalFeatures.h | 1 + test/libsolidity/SolidityNameAndTypeResolution.cpp | 9 +++------ 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Changelog.md b/Changelog.md index 3c875a8f..04a45f7d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,6 +8,7 @@ Features: * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. * Support and recommend using ``emit EventName();`` to call events explicitly. * Syntax Analyser: Do not warn about experimental features if they do not concern code generation. + * Syntax Analyser: Do not warn about ``pragma experimental "v0.5.0"`` since it does not affect code generation. * Syntax Checker: Mark ``throw`` as an error as experimental 0.5.0 feature. * Syntax Checker: Issue error if no visibility is specified on contract functions as experimental 0.5.0 feature. * Type Checker: disallow combining hex numbers and unit denominations as experimental 0.5.0 feature. diff --git a/libsolidity/ast/ExperimentalFeatures.h b/libsolidity/ast/ExperimentalFeatures.h index 3ecfac7b..a17778b4 100644 --- a/libsolidity/ast/ExperimentalFeatures.h +++ b/libsolidity/ast/ExperimentalFeatures.h @@ -40,6 +40,7 @@ static const std::map ExperimentalFeatureOnlyAnalysis { { ExperimentalFeature::SMTChecker, true }, { ExperimentalFeature::TestOnlyAnalysis, true }, + { ExperimentalFeature::V050, true } }; static const std::map ExperimentalFeatureNames = diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 4fb62821..e503c22b 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -102,7 +102,6 @@ BOOST_AUTO_TEST_CASE(double_variable_declaration_050) )"; CHECK_WARNING_ALLOW_MULTI(text, (vector{ "This declaration shadows an existing declaration.", - "Experimental features", "Unused local variable", "Unused local variable" })); @@ -133,7 +132,6 @@ BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_050) } )"; CHECK_WARNING_ALLOW_MULTI(text, (vector{ - "Experimental features", "Unused local variable", "Unused local variable" })); @@ -164,7 +162,6 @@ BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_activation_050) } )"; CHECK_WARNING_ALLOW_MULTI(text, (vector{ - "Experimental features", "Unused local variable", "Unused local variable" })); @@ -262,7 +259,7 @@ BOOST_AUTO_TEST_CASE(scoping_for) } } )"; - CHECK_WARNING(text, "Experimental features"); + CHECK_SUCCESS_NO_WARNINGS(text); } BOOST_AUTO_TEST_CASE(scoping_for2) @@ -276,7 +273,7 @@ BOOST_AUTO_TEST_CASE(scoping_for2) } } )"; - CHECK_WARNING(text, "Experimental features"); + CHECK_SUCCESS_NO_WARNINGS(text); } BOOST_AUTO_TEST_CASE(scoping_for3) @@ -7674,7 +7671,7 @@ BOOST_AUTO_TEST_CASE(non_external_fallback) function () external { } } )"; - CHECK_WARNING(text, "Experimental features are turned on."); + CHECK_SUCCESS_NO_WARNINGS(text); text = R"( pragma experimental "v0.5.0"; contract C { -- cgit v1.2.3 From 298bdeec4921bd30c055a790976542989710b867 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 5 Mar 2018 13:28:27 +0100 Subject: Remove unstable test case. --- test/libsolidity/SolidityEndToEndTest.cpp | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index e61afb0e..c352a2c2 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2481,25 +2481,6 @@ BOOST_AUTO_TEST_CASE(gas_and_value_basic) BOOST_REQUIRE(callContractFunction("checkState()") == encodeArgs(false, 20 - 5)); } -BOOST_AUTO_TEST_CASE(gas_left) -{ - char const* sourceCode = R"( - contract test { - function getGasLeft() public returns (uint256 val) { return msg.gas; } - } - )"; - compileAndRun(sourceCode); - BOOST_REQUIRE(callContractFunction("getGasLeft()") == encodeArgs(99978604)); - - sourceCode = R"( - contract test { - function getGasLeft() public returns (uint256 val) { return gasleft(); } - } - )"; - compileAndRun(sourceCode); - BOOST_REQUIRE(callContractFunction("getGasLeft()") == encodeArgs(99978604)); -} - BOOST_AUTO_TEST_CASE(value_complex) { char const* sourceCode = R"( -- cgit v1.2.3 From 29fb5fe1c93ec6a5dd474e47f999236d1a8b6f0e Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 5 Mar 2018 13:28:57 +0100 Subject: Add test cases for shadowing gasleft with local functions/variables. --- test/libsolidity/SolidityNameAndTypeResolution.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 469211d3..43c9bc9b 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7434,6 +7434,25 @@ BOOST_AUTO_TEST_CASE(gas_left) CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup in msg"); } +BOOST_AUTO_TEST_CASE(gasleft_as_identifier) +{ + char const* text = R"( + contract C { + function gasleft() public pure returns (bytes32 val) { return "abc"; } + function f() public pure { bytes32 val = gasleft(); assert (val == "abc"); } + } + )"; + CHECK_WARNING(text, "This declaration shadows a builtin symbol."); + + text = R"( + contract C { + uint gasleft; + function f() public { gasleft = 42; } + } + )"; + CHECK_WARNING(text, "This declaration shadows a builtin symbol."); +} + BOOST_AUTO_TEST_CASE(builtin_reject_value) { char const* text = R"( -- cgit v1.2.3 From fd60c1cf86e69931fda47bdd187f444550ca785f Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 20 Sep 2017 14:54:41 +0100 Subject: Warn if using address overloads on contracts --- Changelog.md | 1 + libsolidity/analysis/TypeChecker.cpp | 10 ++ test/libsolidity/SolidityNameAndTypeResolution.cpp | 171 ++++++++++++++++++++- test/libsolidity/ViewPureChecker.cpp | 10 +- 4 files changed, 181 insertions(+), 11 deletions(-) diff --git a/Changelog.md b/Changelog.md index 3c875a8f..67634ea0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,7 @@ Features: * Syntax Analyser: Do not warn about experimental features if they do not concern code generation. * Syntax Checker: Mark ``throw`` as an error as experimental 0.5.0 feature. * Syntax Checker: Issue error if no visibility is specified on contract functions as experimental 0.5.0 feature. + * Syntax Checker: Issue warning when using overloads of ``address`` on contract instances. * Type Checker: disallow combining hex numbers and unit denominations as experimental 0.5.0 feature. Bugfixes: diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 9846a0d0..ac8b2b63 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1831,6 +1831,16 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) if (exprType->category() == Type::Category::Contract) { + // Warn about using address members on contracts + for (auto const& addressMember: IntegerType(160, IntegerType::Modifier::Address).nativeMembers(nullptr)) + if (addressMember.name == memberName && *annotation.type == *addressMember.type) + m_errorReporter.warning( + _memberAccess.location(), + "Using contract member \"" + memberName +"\" inherited from the address type is deprecated." + + " Convert the contract to \"address\" type to access the member." + ); + + // Warn about using send or transfer with a non-payable fallback function. if (auto callType = dynamic_cast(type(_memberAccess).get())) { auto kind = callType->kind(); diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 4fb62821..d4962b6c 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7011,6 +7011,8 @@ BOOST_AUTO_TEST_CASE(callable_crash) BOOST_AUTO_TEST_CASE(error_transfer_non_payable_fallback) { + // This used to be a test for a.transfer to generate a warning + // because A's fallback function is not payable. char const* text = R"( contract A { function() public {} @@ -7024,12 +7026,17 @@ BOOST_AUTO_TEST_CASE(error_transfer_non_payable_fallback) } } )"; - CHECK_ERROR(text, TypeError, "Value transfer to a contract without a payable fallback function."); + CHECK_ALLOW_MULTI(text, (std::vector>{ + {Error::Type::Warning, "Using contract member \"transfer\" inherited from the address type is deprecated"}, + {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} + })); } BOOST_AUTO_TEST_CASE(error_transfer_no_fallback) { - char const* text = R"( + // This used to be a test for a.transfer to generate a warning + // because A does not have a payable fallback function. + std::string text = R"( contract A {} contract B { @@ -7040,12 +7047,17 @@ BOOST_AUTO_TEST_CASE(error_transfer_no_fallback) } } )"; - CHECK_ERROR(text, TypeError, "Value transfer to a contract without a payable fallback function."); + CHECK_ALLOW_MULTI(text, (std::vector>{ + {Error::Type::Warning, "Using contract member \"transfer\" inherited from the address type is deprecated"}, + {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} + })); } BOOST_AUTO_TEST_CASE(error_send_non_payable_fallback) { - char const* text = R"( + // This used to be a test for a.send to generate a warning + // because A does not have a payable fallback function. + std::string text = R"( contract A { function() public {} } @@ -7058,11 +7070,16 @@ BOOST_AUTO_TEST_CASE(error_send_non_payable_fallback) } } )"; - CHECK_ERROR(text, TypeError, "Value transfer to a contract without a payable fallback function."); + CHECK_ALLOW_MULTI(text, (std::vector>{ + {Error::Type::Warning, "Using contract member \"send\" inherited from the address type is deprecated"}, + {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} + })); } BOOST_AUTO_TEST_CASE(does_not_error_transfer_payable_fallback) { + // This used to be a test for a.transfer to generate a warning + // because A does not have a payable fallback function. char const* text = R"( contract A { function() payable public {} @@ -7076,7 +7093,7 @@ BOOST_AUTO_TEST_CASE(does_not_error_transfer_payable_fallback) } } )"; - CHECK_SUCCESS_NO_WARNINGS(text); + CHECK_WARNING(text, "Using contract member \"transfer\" inherited from the address type is deprecated."); } BOOST_AUTO_TEST_CASE(does_not_error_transfer_regular_function) @@ -7992,6 +8009,134 @@ BOOST_AUTO_TEST_CASE(array_length_invalid_expression) CHECK_ERROR(text, TypeError, "Operator / not compatible with types int_const 3 and int_const 0"); } +BOOST_AUTO_TEST_CASE(warn_about_address_members_on_contract) +{ + std::string text = R"( + contract C { + function f() view public { + this.balance; + } + } + )"; + CHECK_WARNING(text, "Using contract member \"balance\" inherited from the address type is deprecated."); + text = R"( + contract C { + function f() view public { + this.transfer; + } + } + )"; + CHECK_ALLOW_MULTI(text, (vector>{ + {Error::Type::Warning, "Using contract member \"transfer\" inherited from the address type is deprecated"}, + {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} + })); + text = R"( + contract C { + function f() view public { + this.send; + } + } + )"; + CHECK_ALLOW_MULTI(text, (vector>{ + {Error::Type::Warning, "Using contract member \"send\" inherited from the address type is deprecated"}, + {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} + })); + text = R"( + contract C { + function f() view public { + this.call; + } + } + )"; + CHECK_WARNING(text, "Using contract member \"call\" inherited from the address type is deprecated."); + text = R"( + contract C { + function f() view public { + this.callcode; + } + } + )"; + CHECK_ALLOW_MULTI(text, (vector>{ + {Error::Type::Warning, "Using contract member \"callcode\" inherited from the address type is deprecated"}, + {Error::Type::Warning, "\"callcode\" has been deprecated in favour of \"delegatecall\""} + })); + text = R"( + contract C { + function f() view public { + this.delegatecall; + } + } + )"; + CHECK_WARNING(text, "Using contract member \"delegatecall\" inherited from the address type is deprecated."); +} + +BOOST_AUTO_TEST_CASE(warn_about_address_members_on_non_this_contract) +{ + std::string text = R"( + contract C { + function f() view public { + C c; + c.balance; + } + } + )"; + CHECK_WARNING(text, "Using contract member \"balance\" inherited from the address type is deprecated"); + text = R"( + contract C { + function f() view public { + C c; + c.transfer; + } + } + )"; + CHECK_ALLOW_MULTI(text, (vector>{ + {Error::Type::Warning, "Using contract member \"transfer\" inherited from the address type is deprecated"}, + {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} + })); + text = R"( + contract C { + function f() view public { + C c; + c.send; + } + } + )"; + CHECK_ALLOW_MULTI(text, (vector>{ + {Error::Type::Warning, "Using contract member \"send\" inherited from the address type is deprecated"}, + {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} + })); + text = R"( + contract C { + function f() pure public { + C c; + c.call; + } + } + )"; + CHECK_WARNING(text, "Using contract member \"call\" inherited from the address type is deprecated"); + text = R"( + contract C { + function f() pure public { + C c; + c.callcode; + } + } + )"; + CHECK_WARNING_ALLOW_MULTI(text, (std::vector{ + "Using contract member \"callcode\" inherited from the address type is deprecated", + "\"callcode\" has been deprecated in favour of \"delegatecall\"" + })); + text = R"( + contract C { + function f() pure public { + C c; + c.delegatecall; + } + } + )"; + CHECK_WARNING(text, "Using contract member \"delegatecall\" inherited from the address type is deprecated"); +} + BOOST_AUTO_TEST_CASE(no_address_members_on_contract) { char const* text = R"( @@ -8050,6 +8195,20 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) CHECK_ERROR(text, TypeError, "Member \"delegatecall\" not found or not visible after argument-dependent lookup in contract"); } +BOOST_AUTO_TEST_CASE(no_warning_for_using_members_that_look_like_address_members) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract C { + function transfer(uint) public; + function f() public { + this.transfer(10); + } + } + )"; + CHECK_WARNING(text, "Experimental features"); +} + BOOST_AUTO_TEST_CASE(emit_events) { char const* text = R"( diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index e6a5cfd0..6e8620f9 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -279,11 +279,11 @@ BOOST_AUTO_TEST_CASE(builtin_functions) string text = R"( contract C { function f() public { - this.transfer(1); - require(this.send(2)); - selfdestruct(this); - require(this.delegatecall()); - require(this.call()); + address(this).transfer(1); + require(address(this).send(2)); + selfdestruct(address(this)); + require(address(this).delegatecall()); + require(address(this).call()); } function g() pure public { bytes32 x = keccak256("abc"); -- cgit v1.2.3 From 1ceb0b04c1ce117366cde88eb8c3bebb1a5e9598 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 5 Mar 2018 15:55:02 +0100 Subject: Assert that address members are not present on contract types in 0.5.0. --- libsolidity/analysis/TypeChecker.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index ac8b2b63..106a2c8e 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1832,13 +1832,17 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) if (exprType->category() == Type::Category::Contract) { // Warn about using address members on contracts + bool v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); for (auto const& addressMember: IntegerType(160, IntegerType::Modifier::Address).nativeMembers(nullptr)) if (addressMember.name == memberName && *annotation.type == *addressMember.type) + { + solAssert(!v050, "Address member still present on contract in v0.5.0."); m_errorReporter.warning( _memberAccess.location(), "Using contract member \"" + memberName +"\" inherited from the address type is deprecated." + " Convert the contract to \"address\" type to access the member." ); + } // Warn about using send or transfer with a non-payable fallback function. if (auto callType = dynamic_cast(type(_memberAccess).get())) -- cgit v1.2.3 From b8589fbe0f4eea7b74ce45c2dbaf1bbf710e7287 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 5 Mar 2018 16:59:33 +0100 Subject: Use msg.value and gasleft() as an example in the docs. --- docs/contracts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 5754b734..58e1dcdc 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -402,7 +402,7 @@ State variables can be declared as ``constant``. In this case, they have to be assigned from an expression which is a constant at compile time. Any expression that accesses storage, blockchain data (e.g. ``now``, ``this.balance`` or ``block.number``) or -execution data (``msg.value``) or make calls to external contracts are disallowed. Expressions +execution data (``msg.value`` or ``gasleft()``) or make calls to external contracts are disallowed. Expressions that might have a side-effect on memory allocation are allowed, but those that might have a side-effect on other memory objects are not. The built-in functions ``keccak256``, ``sha256``, ``ripemd160``, ``ecrecover``, ``addmod`` and ``mulmod`` -- cgit v1.2.3 From 3340053fd95b73facc8a1534a130f58698808f2e Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 5 Mar 2018 17:00:37 +0100 Subject: Style improvements. --- libsolidity/ast/Types.cpp | 4 ++-- libsolidity/codegen/ExpressionCompiler.cpp | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index bde0dd6e..26bde1c4 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -3000,7 +3000,7 @@ bool MagicType::operator==(Type const& _other) const return other.m_kind == m_kind; } -MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const *_contract) const +MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const* _contract) const { solAssert(_contract, ""); const bool v050 = _contract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); @@ -3025,7 +3025,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const *_contra }; if (!v050) members.emplace_back("gas", make_shared(256)); - return MemberList::MemberMap(members); + return members; } case Kind::Transaction: return MemberList::MemberMap({ diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index d3599367..58e2aa62 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -924,7 +924,7 @@ bool ExpressionCompiler::visit(NewExpression const&) bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) { - const bool v050 = m_context.experimentalFeatureActive(ExperimentalFeature::V050); + bool const v050 = m_context.experimentalFeatureActive(ExperimentalFeature::V050); CompilerContext::LocationSetter locationSetter(m_context, _memberAccess); // Check whether the member is a bound function. @@ -1140,8 +1140,11 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) m_context << Instruction::CALLVALUE; else if (member == "origin") m_context << Instruction::ORIGIN; - else if (!v050 && member == "gas") + else if (member == "gas") + { + solAssert(!v050, ""); m_context << Instruction::GAS; + } else if (member == "gasprice") m_context << Instruction::GASPRICE; else if (member == "data") -- cgit v1.2.3 From 2213f9946b34a6e8f5604a6b821d31788dcee08b Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 5 Mar 2018 18:01:42 +0100 Subject: Improved gasleft tests. --- test/libsolidity/SolidityEndToEndTest.cpp | 12 ++++++++++++ test/libsolidity/SolidityNameAndTypeResolution.cpp | 14 +++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index c352a2c2..ebb2f3ff 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -5352,6 +5352,18 @@ BOOST_AUTO_TEST_CASE(super_overload) ABI_CHECK(callContractFunction("h()"), encodeArgs(2)); } +BOOST_AUTO_TEST_CASE(gasleft_shadow_resolution) +{ + char const* sourceCode = R"( + contract C { + function gasleft() returns(uint256) { return 0; } + function f() returns(uint256) { return gasleft(); } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0)); +} + BOOST_AUTO_TEST_CASE(bool_conversion) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 43c9bc9b..f08f695b 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7409,21 +7409,21 @@ BOOST_AUTO_TEST_CASE(builtin_reject_gas) CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup"); } -BOOST_AUTO_TEST_CASE(gas_left) +BOOST_AUTO_TEST_CASE(gasleft) { char const* text = R"( contract C { - function f() public returns (uint256 val) { return msg.gas; } + function f() public view returns (uint256 val) { return msg.gas; } } )"; - CHECK_SUCCESS(text); + CHECK_SUCCESS_NO_WARNINGS(text); text = R"( contract C { - function f() public returns (uint256 val) { return gasleft(); } + function f() public view returns (uint256 val) { return gasleft(); } } )"; - CHECK_SUCCESS(text); + CHECK_SUCCESS_NO_WARNINGS(text); text = R"( pragma experimental "v0.5.0"; @@ -7434,12 +7434,12 @@ BOOST_AUTO_TEST_CASE(gas_left) CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup in msg"); } -BOOST_AUTO_TEST_CASE(gasleft_as_identifier) +BOOST_AUTO_TEST_CASE(gasleft_shadowing) { char const* text = R"( contract C { function gasleft() public pure returns (bytes32 val) { return "abc"; } - function f() public pure { bytes32 val = gasleft(); assert (val == "abc"); } + function f() public pure returns (bytes32 val) { return gasleft(); } } )"; CHECK_WARNING(text, "This declaration shadows a builtin symbol."); -- cgit v1.2.3 From 83fcf007bfdf9c9c8b595369c0542d45943ee095 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 26 Feb 2018 18:25:36 +0100 Subject: Do not retain any gas in external calls (except if EVM version is set to homestead). --- Changelog.md | 1 + libsolidity/codegen/ExpressionCompiler.cpp | 23 +++++++++++++---------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Changelog.md b/Changelog.md index 04a45f7d..529fb181 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ Features: * C99/C++-style scoping rules (instead of JavaScript function scoping) take effect as experimental v0.5.0 feature. * Code Generator: Assert that ``k != 0`` for ``molmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature. + * Code Generator: Do not retain any gas in calls (except if EVM version is set to homestead). * Interface: Provide ability to select target EVM version (homestead or byzantium, with byzantium being the default). * Standard JSON: Reject badly formatted invalid JSON inputs. * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 12881d63..441e4429 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1671,16 +1671,19 @@ void ExpressionCompiler::appendExternalFunctionCall( utils().storeFreeMemoryPointer(); } - // Touch the end of the output area so that we do not pay for memory resize during the call - // (which we would have to subtract from the gas left) - // We could also just use MLOAD; POP right before the gas calculation, but the optimizer - // would remove that, so we use MSTORE here. - if (!_functionType.gasSet() && retSize > 0) + if (!m_context.evmVersion().canOverchargeGasForCall()) { - m_context << u256(0); - utils().fetchFreeMemoryPointer(); - // This touches too much, but that way we save some rounding arithmetics - m_context << u256(retSize) << Instruction::ADD << Instruction::MSTORE; + // Touch the end of the output area so that we do not pay for memory resize during the call + // (which we would have to subtract from the gas left) + // We could also just use MLOAD; POP right before the gas calculation, but the optimizer + // would remove that, so we use MSTORE here. + if (!_functionType.gasSet() && retSize > 0) + { + m_context << u256(0); + utils().fetchFreeMemoryPointer(); + // This touches too much, but that way we save some rounding arithmetics + m_context << u256(retSize) << Instruction::ADD << Instruction::MSTORE; + } } // Copy function identifier to memory. @@ -1749,7 +1752,7 @@ void ExpressionCompiler::appendExternalFunctionCall( if (_functionType.gasSet()) m_context << dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos)); - else if (m_context.experimentalFeatureActive(ExperimentalFeature::V050)) + else if (m_context.evmVersion().canOverchargeGasForCall()) // Send all gas (requires tangerine whistle EVM) m_context << Instruction::GAS; else -- cgit v1.2.3 From 174c46d5afd8be4e6b23804bbdd8245d5a78cc0e Mon Sep 17 00:00:00 2001 From: Anthony Broad-Crawford Date: Fri, 2 Mar 2018 15:30:03 -0600 Subject: Improved messaging when an error spans multiple lines and updated change log file. --- Changelog.md | 1 + libsolidity/interface/SourceReferenceFormatter.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index a28538ed..ce0cc3a2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,7 @@ Features: * Syntax Checker: Mark ``throw`` as an error as experimental 0.5.0 feature. * Syntax Checker: Issue error if no visibility is specified on contract functions as experimental 0.5.0 feature. * Type Checker: disallow combining hex numbers and unit denominations as experimental 0.5.0 feature. + * Improved messaging when error spans multiple lines of a sourcefile Bugfixes: * Assembly: Raise error on oversized number literals in assembly. diff --git a/libsolidity/interface/SourceReferenceFormatter.cpp b/libsolidity/interface/SourceReferenceFormatter.cpp index 9d02c498..0f014372 100644 --- a/libsolidity/interface/SourceReferenceFormatter.cpp +++ b/libsolidity/interface/SourceReferenceFormatter.cpp @@ -79,8 +79,8 @@ void SourceReferenceFormatter::printSourceLocation(SourceLocation const* _locati scanner.lineAtPosition(_location->start) << endl << string(startColumn, ' ') << - "^\n" << - "Spanning multiple lines.\n"; + "^ (Relevant source part starts here and spans across multiple lines)." << + endl; } void SourceReferenceFormatter::printSourceName(SourceLocation const* _location) -- cgit v1.2.3 From 91b13b734d34e6bb5a76fb53624588d035946237 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 5 Mar 2018 23:13:51 +0100 Subject: Use StaticAnalyzer to deprecate msg.gas instead of conditionally removing it in MagicType. --- libsolidity/analysis/StaticAnalyzer.cpp | 19 ++++++++++++++++++- libsolidity/ast/Types.cpp | 14 ++++---------- libsolidity/codegen/ExpressionCompiler.cpp | 5 ----- test/libsolidity/SolidityNameAndTypeResolution.cpp | 4 ++-- 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index bd8ee597..d4de219a 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -139,6 +139,23 @@ bool StaticAnalyzer::visit(ExpressionStatement const& _statement) bool StaticAnalyzer::visit(MemberAccess const& _memberAccess) { + bool const v050 = m_currentContract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); + + if (MagicType const* type = dynamic_cast(_memberAccess.expression().annotation().type.get())) + if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "gas") + { + if (v050) + m_errorReporter.typeError( + _memberAccess.location(), + "\"msg.gas\" has been deprecated in favor of \"gasleft()\"" + ); + else + m_errorReporter.warning( + _memberAccess.location(), + "\"msg.gas\" has been deprecated in favor of \"gasleft()\"" + ); + } + if (m_nonPayablePublic && !m_library) if (MagicType const* type = dynamic_cast(_memberAccess.expression().annotation().type.get())) if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "value") @@ -151,7 +168,7 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess) if (auto const* type = dynamic_cast(_memberAccess.annotation().type.get())) if (type->kind() == FunctionType::Kind::BareCallCode) { - if (m_currentContract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + if (v050) m_errorReporter.typeError( _memberAccess.location(), "\"callcode\" has been deprecated in favour of \"delegatecall\"." diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 26bde1c4..771ae643 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -3000,10 +3000,8 @@ bool MagicType::operator==(Type const& _other) const return other.m_kind == m_kind; } -MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const* _contract) const +MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const { - solAssert(_contract, ""); - const bool v050 = _contract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); switch (m_kind) { case Kind::Block: @@ -3016,17 +3014,13 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const* _contra {"gaslimit", make_shared(256)} }); case Kind::Message: - { - std::vector members = { + return MemberList::MemberMap({ {"sender", make_shared(160, IntegerType::Modifier::Address)}, + {"gas", make_shared(256)}, {"value", make_shared(256)}, {"data", make_shared(DataLocation::CallData)}, {"sig", make_shared(4)} - }; - if (!v050) - members.emplace_back("gas", make_shared(256)); - return members; - } + }); case Kind::Transaction: return MemberList::MemberMap({ {"origin", make_shared(160, IntegerType::Modifier::Address)}, diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 58e2aa62..4c6e9a56 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -924,8 +924,6 @@ bool ExpressionCompiler::visit(NewExpression const&) bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) { - bool const v050 = m_context.experimentalFeatureActive(ExperimentalFeature::V050); - CompilerContext::LocationSetter locationSetter(m_context, _memberAccess); // Check whether the member is a bound function. ASTString const& member = _memberAccess.memberName(); @@ -1141,10 +1139,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) else if (member == "origin") m_context << Instruction::ORIGIN; else if (member == "gas") - { - solAssert(!v050, ""); m_context << Instruction::GAS; - } else if (member == "gasprice") m_context << Instruction::GASPRICE; else if (member == "data") diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index f08f695b..3b0ccd4d 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7416,7 +7416,7 @@ BOOST_AUTO_TEST_CASE(gasleft) function f() public view returns (uint256 val) { return msg.gas; } } )"; - CHECK_SUCCESS_NO_WARNINGS(text); + CHECK_WARNING(text, "\"msg.gas\" has been deprecated in favor of \"gasleft()\""); text = R"( contract C { @@ -7431,7 +7431,7 @@ BOOST_AUTO_TEST_CASE(gasleft) function f() public returns (uint256 val) { return msg.gas; } } )"; - CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup in msg"); + CHECK_ERROR(text, TypeError, "\"msg.gas\" has been deprecated in favor of \"gasleft()\""); } BOOST_AUTO_TEST_CASE(gasleft_shadowing) -- cgit v1.2.3 From d8bfa02deb11007126094a3b05bbf397ba23eed2 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 5 Mar 2018 19:50:09 +0100 Subject: Try new caching keys. --- circle.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/circle.yml b/circle.yml index 1ed09ada..3f9724f4 100644 --- a/circle.yml +++ b/circle.yml @@ -96,14 +96,16 @@ jobs: if [ "$CIRCLE_BRANCH" = release -o -n "$CIRCLE_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi echo -n "$CIRCLE_SHA1" > commit_hash.txt - restore_cache: - key: ccache-{{ arch }}-{{ .Branch }} - key: ccache-{{ arch }} - key: ccache + key: ccache-{{ arch }}-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }} + key: ccache-{{ arch }}-{{ .Branch }}- + key: ccache-{{ arch }}-develop- + key: ccache-{{ arch }}- - run: name: Build command: ./scripts/build.sh RelWithDebInfo - save_cache: - key: ccache-{{ arch }}-{{ .Branch }} + key: ccache-{{ arch }}-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }} + when: always paths: - ~/.ccache - store_artifacts: -- cgit v1.2.3 From 8259ce1e94ee6a4d33ca16f3f4d3616398e2de8c Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 5 Mar 2018 22:49:27 +0100 Subject: Restrict ccache cache size. --- circle.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/circle.yml b/circle.yml index 3f9724f4..990d3939 100644 --- a/circle.yml +++ b/circle.yml @@ -100,9 +100,15 @@ jobs: key: ccache-{{ arch }}-{{ .Branch }}- key: ccache-{{ arch }}-develop- key: ccache-{{ arch }}- + - run: + name: Configure ccache + command: ccache -M 80M && ccache -c && ccache -s && ccache -z - run: name: Build command: ./scripts/build.sh RelWithDebInfo + - run: + name: CCache statistics + command: ccache -s - save_cache: key: ccache-{{ arch }}-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }} when: always -- cgit v1.2.3 From 556fe8a574d6494f68a28267e30808c0448ddcb1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 5 Mar 2018 22:56:35 +0100 Subject: Build with four parallel jobs. --- circle.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 990d3939..45a26fa8 100644 --- a/circle.yml +++ b/circle.yml @@ -105,7 +105,11 @@ jobs: command: ccache -M 80M && ccache -c && ccache -s && ccache -z - run: name: Build - command: ./scripts/build.sh RelWithDebInfo + command: | + mkdir -p build + cd build + cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo + make -j4 - run: name: CCache statistics command: ccache -s -- cgit v1.2.3 From 154fd1d6cbfb7fc954b78e82831c8cdf50a51657 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 6 Mar 2018 00:59:34 +0100 Subject: Document the meaning of sourceIndex "-1" in source mappings. --- docs/miscellaneous.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 7d3d058b..075b6be0 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -192,6 +192,11 @@ These are regular array indices into a list of source files usually called ``"sourceList"``, which is part of the combined-json and the output of the json / npm compiler. +.. note :: + In the case of instructions that are not associated with any particular source file, + the source mapping assigns an integer identifier of ``-1``. This may happen for + bytecode sections stemming from compiler-generated inline assembly statements. + The source mappings inside the AST use the following notation: -- cgit v1.2.3 From 5d486b741fc5352bb7d02a812fb7566f1749f9a7 Mon Sep 17 00:00:00 2001 From: Oleksii Matiiasevych Date: Tue, 6 Mar 2018 14:19:59 +0700 Subject: Revert warning update about `view` modifier --- docs/contracts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index e61667dd..67d4a249 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -473,7 +473,7 @@ The following statements are considered modifying the state: Getter methods are marked ``view``. .. warning:: - Before version 0.4.17 the compiler didn't enforce that ``view`` is not modifying the state. + The compiler does not enforce yet that a ``view`` method is not modifying state. It raises a warning though. .. index:: ! pure function, function;pure -- cgit v1.2.3 From d7fe0bbc7227d7f57043a686bac7bd9bb9e49642 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 6 Mar 2018 11:11:33 +0100 Subject: Increase ccache size. --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 45a26fa8..263cb700 100644 --- a/circle.yml +++ b/circle.yml @@ -102,7 +102,7 @@ jobs: key: ccache-{{ arch }}- - run: name: Configure ccache - command: ccache -M 80M && ccache -c && ccache -s && ccache -z + command: ccache -M 200M && ccache -c && ccache -s && ccache -z - run: name: Build command: | -- cgit v1.2.3 From e3bd3020d13bd898be8c6123b9979c69422e944f Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 6 Mar 2018 11:04:29 +0100 Subject: Use new eth binary for trusty. --- scripts/tests.sh | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/scripts/tests.sh b/scripts/tests.sh index 60dae2e4..bf4ee3d9 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -59,12 +59,15 @@ function download_eth() ETH_PATH="eth" else mkdir -p /tmp/test - ETH_BINARY=eth_byzantium_artful - ETH_HASH="e527dd3e3dc17b983529dd7dcfb74a0d3a5aed4e" if grep -i trusty /etc/lsb-release >/dev/null 2>&1 then - ETH_BINARY=eth_byzantium2 - ETH_HASH="4dc3f208475f622be7c8e53bee720e14cd254c6f" + # built from 1ecff3cac12f0fbbeea3e645f331d5ac026b24d3 at 2018-03-06 + ETH_BINARY=eth_byzantium_trusty + ETH_HASH="5432ea81c150e8a3547615bf597cd6dce9e1e27b" + else + # built from ?? at 2018-02-13 ? + ETH_BINARY=eth_byzantium_artful + ETH_HASH="e527dd3e3dc17b983529dd7dcfb74a0d3a5aed4e" fi wget -q -O /tmp/test/eth https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/$ETH_BINARY test "$(shasum /tmp/test/eth)" = "$ETH_HASH /tmp/test/eth" @@ -121,4 +124,4 @@ wait $CMDLINE_PID pkill "$ETH_PID" || true sleep 4 -pgrep "$ETH_PID" && pkill -9 "$ETH_PID" || true \ No newline at end of file +pgrep "$ETH_PID" && pkill -9 "$ETH_PID" || true -- cgit v1.2.3 From b467116ea85383d5c8492e7f21ea16ce7c70ebc9 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 26 Sep 2017 15:27:03 +0200 Subject: Use STATICCALL for pure function calls if EVM version supports it and 0.5.0 is activated. --- Changelog.md | 3 ++- libsolidity/ast/ExperimentalFeatures.h | 4 ++-- libsolidity/codegen/ExpressionCompiler.cpp | 9 +++++++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index bea9dd5b..4a9f7ff4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,8 +2,9 @@ Features: * C99/C++-style scoping rules (instead of JavaScript function scoping) take effect as experimental v0.5.0 feature. - * Code Generator: Assert that ``k != 0`` for ``molmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature. + * Code Generator: Assert that ``k != 0`` for ``mulmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature. * Code Generator: Do not retain any gas in calls (except if EVM version is set to homestead). + * Code Generator: Use ``STATICCALL`` opcode for calling ``view`` and ``pure`` functions as experimenal 0.5.0 feature. * Interface: Provide ability to select target EVM version (homestead or byzantium, with byzantium being the default). * Standard JSON: Reject badly formatted invalid JSON inputs. * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. diff --git a/libsolidity/ast/ExperimentalFeatures.h b/libsolidity/ast/ExperimentalFeatures.h index a17778b4..30ea7ec5 100644 --- a/libsolidity/ast/ExperimentalFeatures.h +++ b/libsolidity/ast/ExperimentalFeatures.h @@ -29,8 +29,8 @@ namespace solidity enum class ExperimentalFeature { - SMTChecker, ABIEncoderV2, // new ABI encoder that makes use of JULIA + SMTChecker, V050, // v0.5.0 breaking changes Test, TestOnlyAnalysis @@ -45,8 +45,8 @@ static const std::map ExperimentalFeatureOnlyAnalysis static const std::map ExperimentalFeatureNames = { - { "SMTChecker", ExperimentalFeature::SMTChecker }, { "ABIEncoderV2", ExperimentalFeature::ABIEncoderV2 }, + { "SMTChecker", ExperimentalFeature::SMTChecker }, { "v0.5.0", ExperimentalFeature::V050 }, { "__test", ExperimentalFeature::Test }, { "__testOnlyAnalysis", ExperimentalFeature::TestOnlyAnalysis }, diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index d27af7db..7162cb0d 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1610,6 +1610,10 @@ void ExpressionCompiler::appendExternalFunctionCall( bool returnSuccessCondition = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall; bool isCallCode = funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::CallCode; bool isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall; + bool useStaticCall = + _functionType.stateMutability() <= StateMutability::View && + m_context.experimentalFeatureActive(ExperimentalFeature::V050) && + m_context.evmVersion().hasStaticCall(); unsigned retSize = 0; if (returnSuccessCondition) @@ -1738,6 +1742,8 @@ void ExpressionCompiler::appendExternalFunctionCall( // [value,] addr, gas (stack top) if (isDelegateCall) solAssert(!_functionType.valueSet(), "Value set for delegatecall"); + else if (useStaticCall) + solAssert(!_functionType.valueSet(), "Value set for staticcall"); else if (_functionType.valueSet()) m_context << dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos)); else @@ -1769,10 +1775,13 @@ void ExpressionCompiler::appendExternalFunctionCall( gasNeededByCaller += eth::GasCosts::callNewAccountGas; // we never know m_context << gasNeededByCaller << Instruction::GAS << Instruction::SUB; } + // Order is important here, STATICCALL might overlap with DELEGATECALL. if (isDelegateCall) m_context << Instruction::DELEGATECALL; else if (isCallCode) m_context << Instruction::CALLCODE; + else if (useStaticCall) + m_context << Instruction::STATICCALL; else m_context << Instruction::CALL; -- cgit v1.2.3 From a3593df43b8d5639dd756d238cc744b1204cc3e8 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 15 Jan 2018 16:50:00 +0100 Subject: Test for using staticcall for view and pure functions. --- test/libsolidity/SolidityEndToEndTest.cpp | 60 ++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index ebb2f3ff..33cd1419 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -21,13 +21,20 @@ * Unit tests for the solidity expression compiler, testing the behaviour of the code. */ +#include + +#include + +#include +#include + +#include + +#include + #include #include #include -#include -#include -#include -#include using namespace std; using namespace std::placeholders; @@ -10778,6 +10785,51 @@ BOOST_AUTO_TEST_CASE(snark) BOOST_CHECK(callContractFunction("verifyTx()") == encodeArgs(true)); } +BOOST_AUTO_TEST_CASE(staticcall_for_view_and_pure) +{ + char const* sourceCode = R"( + pragma experimental "v0.5.0"; + contract C { + uint x; + function f() public returns (uint) { + x = 3; + return 1; + } + } + interface CView { + function f() view external returns (uint); + } + interface CPure { + function f() pure external returns (uint); + } + contract D { + function f() public returns (uint) { + return (new C()).f(); + } + function fview() public returns (uint) { + return (CView(new C())).f(); + } + function fpure() public returns (uint) { + return (CPure(new C())).f(); + } + } + )"; + compileAndRun(sourceCode, 0, "D"); + // This should work (called via CALL) + ABI_CHECK(callContractFunction("f()"), encodeArgs(1)); + if (dev::test::Options::get().evmVersion().hasStaticCall()) + { + // These should throw (called via STATICCALL) + ABI_CHECK(callContractFunction("fview()"), encodeArgs()); + ABI_CHECK(callContractFunction("fpure()"), encodeArgs()); + } + else + { + ABI_CHECK(callContractFunction("fview()"), encodeArgs(1)); + ABI_CHECK(callContractFunction("fpure()"), encodeArgs(1)); + } +} + BOOST_AUTO_TEST_SUITE_END() } -- cgit v1.2.3 From 3057aeece495276265d9632b97e3faffcb57fe71 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 16 Jan 2018 10:31:13 +0100 Subject: Document STATICCALL usage in experimental 0.5.0. --- docs/contracts.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/contracts.rst b/docs/contracts.rst index 67d4a249..121c4de0 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -472,6 +472,13 @@ The following statements are considered modifying the state: .. note:: Getter methods are marked ``view``. +.. note:: + If invalid explicit type conversions are used, state modifications are possible + even though a ``view`` function was called. + You can switch the compiler to use ``STATICCALL`` when calling such functions and thus + prevent modifications to the state on the level of the EVM by adding + ``pragma experimental "v0.5.0";`` + .. warning:: The compiler does not enforce yet that a ``view`` method is not modifying state. It raises a warning though. @@ -502,6 +509,18 @@ In addition to the list of state modifying statements explained above, the follo } } +.. note:: + If invalid explicit type conversions are used, state modifications are possible + even though a ``pure`` function was called. + You can switch the compiler to use ``STATICCALL`` when calling such functions and thus + prevent modifications to the state on the level of the EVM by adding + ``pragma experimental "v0.5.0";`` + +.. warning:: + It is not possible to prevent functions from reading the state at the level + of the EVM, it is only possible to prevent them from writing to the state + (i.e. only ``view`` can be enforced at the EVM level, ``pure`` can not). + .. warning:: Before version 0.4.17 the compiler didn't enforce that ``pure`` is not reading the state. -- cgit v1.2.3 From fb4d01e2cd9c3d6fa862a5c9fea433522b94d916 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 6 Mar 2018 17:18:57 +0100 Subject: Add gasleft to FunctionType::richIdentifier(). --- libsolidity/ast/Types.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 6a9707f3..c08e0e67 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2344,6 +2344,7 @@ string FunctionType::richIdentifier() const case Kind::Log2: id += "log2"; break; case Kind::Log3: id += "log3"; break; case Kind::Log4: id += "log4"; break; + case Kind::GasLeft: id += "gasleft"; break; case Kind::Event: id += "event"; break; case Kind::SetGas: id += "setgas"; break; case Kind::SetValue: id += "setvalue"; break; -- cgit v1.2.3 From fd1662d1c49776232b491f1871391ad1cd90309a Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Feb 2018 15:18:09 +0100 Subject: Warn about using loose inline assembly features as experimental 0.5.0 feature. --- libsolidity/analysis/ReferencesResolver.cpp | 3 +- libsolidity/analysis/TypeChecker.cpp | 5 ++++ libsolidity/codegen/CompilerContext.cpp | 1 + libsolidity/inlineasm/AsmAnalysis.cpp | 45 ++++++++++++++++++++++++----- libsolidity/inlineasm/AsmAnalysis.h | 17 ++++++++++- libsolidity/interface/AssemblyStack.cpp | 2 +- test/libjulia/Common.cpp | 1 + test/libjulia/Parser.cpp | 1 + 8 files changed, 65 insertions(+), 10 deletions(-) diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 296a39c2..f91eaf6e 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -280,7 +280,8 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) // Will be re-generated later with correct information // We use the latest EVM version because we will re-run it anyway. assembly::AsmAnalysisInfo analysisInfo; - assembly::AsmAnalyzer(analysisInfo, errorsIgnored, EVMVersion(), assembly::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations()); + boost::optional errorTypeForLoose = m_experimental050Mode ? Error::Type::SyntaxError : Error::Type::Warning; + assembly::AsmAnalyzer(analysisInfo, errorsIgnored, EVMVersion(), errorTypeForLoose, assembly::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations()); return false; } diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 1748b518..5d28e776 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -894,10 +894,15 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) }; solAssert(!_inlineAssembly.annotation().analysisInfo, ""); _inlineAssembly.annotation().analysisInfo = make_shared(); + boost::optional errorTypeForLoose = + m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050) ? + Error::Type::SyntaxError : + Error::Type::Warning; assembly::AsmAnalyzer analyzer( *_inlineAssembly.annotation().analysisInfo, m_errorReporter, m_evmVersion, + errorTypeForLoose, assembly::AsmFlavour::Loose, identifierAccess ); diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index ebf0213a..0bf88267 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -330,6 +330,7 @@ void CompilerContext::appendInlineAssembly( analysisInfo, errorReporter, m_evmVersion, + boost::none, assembly::AsmFlavour::Strict, identifierAccess.resolve ).analyze(*parserResult); diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index a7f764a5..abf7ddf2 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -54,7 +54,10 @@ bool AsmAnalyzer::analyze(Block const& _block) bool AsmAnalyzer::operator()(Label const& _label) { - solAssert(m_flavour == AsmFlavour::Loose, ""); + checkLooseFeature( + _label.location, + "The use of labels is deprecated. Please use \"if\", \"switch\", \"for\" or function calls instead." + ); m_info.stackHeightInfo[&_label] = m_stackHeight; warnOnInstructions(solidity::Instruction::JUMPDEST, _label.location); return true; @@ -62,7 +65,10 @@ bool AsmAnalyzer::operator()(Label const& _label) bool AsmAnalyzer::operator()(assembly::Instruction const& _instruction) { - solAssert(m_flavour == AsmFlavour::Loose, ""); + checkLooseFeature( + _instruction.location, + "The use of non-functional instructions is deprecated. Please use functional notation instead." + ); auto const& info = instructionInfo(_instruction.instruction); m_stackHeight += info.ret - info.args; m_info.stackHeightInfo[&_instruction] = m_stackHeight; @@ -170,18 +176,31 @@ bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr) bool AsmAnalyzer::operator()(assembly::ExpressionStatement const& _statement) { - size_t initialStackHeight = m_stackHeight; + int initialStackHeight = m_stackHeight; bool success = boost::apply_visitor(*this, _statement.expression); - if (m_flavour != AsmFlavour::Loose) - if (!expectDeposit(0, initialStackHeight, _statement.location)) + if (m_stackHeight != initialStackHeight && (m_flavour != AsmFlavour::Loose || m_errorTypeForLoose)) + { + Error::Type errorType = m_flavour == AsmFlavour::Loose ? *m_errorTypeForLoose : Error::Type::TypeError; + string msg = + "Top-level expressions are not supposed to return values (this expression returns " + + boost::lexical_cast(m_stackHeight - initialStackHeight) + + " value" + + (m_stackHeight - initialStackHeight == 1 ? "" : "s") + + "). Use ``pop()`` or assign them."; + m_errorReporter.error(errorType, _statement.location, msg); + if (errorType != Error::Type::Warning) success = false; + } m_info.stackHeightInfo[&_statement] = m_stackHeight; return success; } bool AsmAnalyzer::operator()(assembly::StackAssignment const& _assignment) { - solAssert(m_flavour == AsmFlavour::Loose, ""); + checkLooseFeature( + _assignment.location, + "The use of stack assignment is deprecated. Please use assignment in functional notation instead." + ); bool success = checkAssignment(_assignment.variableName, size_t(-1)); m_info.stackHeightInfo[&_assignment] = m_stackHeight; return success; @@ -577,10 +596,22 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio ); if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI || _instr == solidity::Instruction::JUMPDEST) - m_errorReporter.warning( + { + solAssert(m_flavour == AsmFlavour::Loose, ""); + m_errorReporter.error( + m_errorTypeForLoose ? *m_errorTypeForLoose : Error::Type::Warning, _location, "Jump instructions and labels are low-level EVM features that can lead to " "incorrect stack access. Because of that they are discouraged. " "Please consider using \"switch\", \"if\" or \"for\" statements instead." ); + } +} + +void AsmAnalyzer::checkLooseFeature(SourceLocation const& _location, string const& _description) +{ + if (m_flavour != AsmFlavour::Loose) + solAssert(false, _description); + else if (m_errorTypeForLoose) + m_errorReporter.error(*m_errorTypeForLoose, _location, _description); } diff --git a/libsolidity/inlineasm/AsmAnalysis.h b/libsolidity/inlineasm/AsmAnalysis.h index 867711c7..8d2a71f0 100644 --- a/libsolidity/inlineasm/AsmAnalysis.h +++ b/libsolidity/inlineasm/AsmAnalysis.h @@ -30,6 +30,7 @@ #include #include +#include #include #include @@ -56,9 +57,17 @@ public: AsmAnalysisInfo& _analysisInfo, ErrorReporter& _errorReporter, EVMVersion _evmVersion, + boost::optional _errorTypeForLoose, AsmFlavour _flavour = AsmFlavour::Loose, julia::ExternalIdentifierAccess::Resolver const& _resolver = julia::ExternalIdentifierAccess::Resolver() - ): m_resolver(_resolver), m_info(_analysisInfo), m_errorReporter(_errorReporter), m_evmVersion(_evmVersion), m_flavour(_flavour) {} + ): + m_resolver(_resolver), + m_info(_analysisInfo), + m_errorReporter(_errorReporter), + m_evmVersion(_evmVersion), + m_flavour(_flavour), + m_errorTypeForLoose(_errorTypeForLoose) + {} bool analyze(assembly::Block const& _block); @@ -91,6 +100,11 @@ private: void expectValidType(std::string const& type, SourceLocation const& _location); void warnOnInstructions(solidity::Instruction _instr, SourceLocation const& _location); + /// Depending on @a m_flavour and @a m_errorTypeForLoose, throws an internal compiler + /// exception (if the flavour is not Loose), reports an error/warning + /// (if m_errorTypeForLoose is set) or does nothing. + void checkLooseFeature(SourceLocation const& _location, std::string const& _description); + int m_stackHeight = 0; julia::ExternalIdentifierAccess::Resolver m_resolver; Scope* m_currentScope = nullptr; @@ -101,6 +115,7 @@ private: ErrorReporter& m_errorReporter; EVMVersion m_evmVersion; AsmFlavour m_flavour = AsmFlavour::Loose; + boost::optional m_errorTypeForLoose; }; } diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp index 7a9fffbf..7f97336b 100644 --- a/libsolidity/interface/AssemblyStack.cpp +++ b/libsolidity/interface/AssemblyStack.cpp @@ -91,7 +91,7 @@ bool AssemblyStack::analyze(assembly::Block const& _block, Scanner const* _scann bool AssemblyStack::analyzeParsed() { m_analysisInfo = make_shared(); - assembly::AsmAnalyzer analyzer(*m_analysisInfo, m_errorReporter, m_evmVersion, languageToAsmFlavour(m_language)); + assembly::AsmAnalyzer analyzer(*m_analysisInfo, m_errorReporter, m_evmVersion, boost::none, languageToAsmFlavour(m_language)); m_analysisSuccessful = analyzer.analyze(*m_parserResult); return m_analysisSuccessful; } diff --git a/test/libjulia/Common.cpp b/test/libjulia/Common.cpp index d8cd20b6..41f5d320 100644 --- a/test/libjulia/Common.cpp +++ b/test/libjulia/Common.cpp @@ -67,6 +67,7 @@ pair, shared_ptr> dev::julia::test: *analysisInfo, errorReporter, dev::test::Options::get().evmVersion(), + boost::none, flavour ); if (analyzer.analyze(*parserResult)) diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp index 6476c4d4..df905dd6 100644 --- a/test/libjulia/Parser.cpp +++ b/test/libjulia/Parser.cpp @@ -60,6 +60,7 @@ bool parse(string const& _source, ErrorReporter& errorReporter) analysisInfo, errorReporter, dev::test::Options::get().evmVersion(), + boost::none, assembly::AsmFlavour::IULIA )).analyze(*parserResult); } -- cgit v1.2.3 From 84d711fd1d38d36eb6ef2d3c118694a9ad06938e Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Feb 2018 17:25:54 +0100 Subject: Tests for warnings/errors for loose assembly. --- test/libsolidity/SolidityNameAndTypeResolution.cpp | 196 ++++++++++++++++++++- 1 file changed, 192 insertions(+), 4 deletions(-) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 87d62973..71d3e93e 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -5946,10 +5946,11 @@ BOOST_AUTO_TEST_CASE(inline_assembly_unbalanced_negative_stack) BOOST_AUTO_TEST_CASE(inline_assembly_unbalanced_two_stack_load) { char const* text = R"( + pragma experimental "v0.5.0"; contract c { uint8 x; function f() public { - assembly { x pop } + assembly { pop(x) } } } )"; @@ -5959,6 +5960,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_unbalanced_two_stack_load) BOOST_AUTO_TEST_CASE(inline_assembly_in_modifier) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { modifier m { uint a = 1; @@ -5967,7 +5969,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_in_modifier) } _; } - function f() m { + function f() public m { } } )"; @@ -5977,6 +5979,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_in_modifier) BOOST_AUTO_TEST_CASE(inline_assembly_storage) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { uint x = 1; function f() public { @@ -5992,6 +5995,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage) BOOST_AUTO_TEST_CASE(inline_assembly_storage_in_modifiers) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { uint x = 1; modifier m { @@ -6000,7 +6004,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage_in_modifiers) } _; } - function f() m { + function f() public m { } } )"; @@ -6010,6 +6014,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage_in_modifiers) BOOST_AUTO_TEST_CASE(inline_assembly_constant_assign) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { uint constant x = 1; function f() public { @@ -6025,6 +6030,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_constant_assign) BOOST_AUTO_TEST_CASE(inline_assembly_constant_access) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { uint constant x = 1; function f() public { @@ -6040,6 +6046,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_constant_access) BOOST_AUTO_TEST_CASE(inline_assembly_local_variable_access_out_of_functions) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { function f() public { uint a; @@ -6055,6 +6062,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_local_variable_access_out_of_functions) BOOST_AUTO_TEST_CASE(inline_assembly_local_variable_access_out_of_functions_storage_ptr) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { uint[] r; function f() public { @@ -6071,6 +6079,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_local_variable_access_out_of_functions_stor BOOST_AUTO_TEST_CASE(inline_assembly_storage_variable_access_out_of_functions) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { uint a; function f() pure public { @@ -6101,6 +6110,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_constant_variable_via_offset) BOOST_AUTO_TEST_CASE(inline_assembly_calldata_variables) { char const* text = R"( + pragma experimental "v0.5.0"; contract C { function f(bytes bytesAsCalldata) external { assembly { @@ -6112,6 +6122,182 @@ BOOST_AUTO_TEST_CASE(inline_assembly_calldata_variables) CHECK_ERROR(text, TypeError, "Call data elements cannot be accessed directly."); } +BOOST_AUTO_TEST_CASE(inline_assembly_050_literals_on_stack) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract C { + function f() pure public { + assembly { + 1 + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector>{ + {Error::Type::SyntaxError, "are not supposed to return"}, + {Error::Type::DeclarationError, "Unbalanced stack"}, + })); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_literals_on_stack) +{ + char const* text = R"( + contract C { + function f() pure public { + assembly { + 1 + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector>{ + {Error::Type::Warning, "are not supposed to return"}, + {Error::Type::DeclarationError, "Unbalanced stack"}, + })); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_050_bare_instructions) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract C { + function f() view public { + assembly { + address + pop + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector>{ + {Error::Type::SyntaxError, "The use of non-functional"}, + {Error::Type::SyntaxError, "The use of non-functional"} + })); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_bare_instructions) +{ + char const* text = R"( + contract C { + function f() view public { + assembly { + address + pop + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector>{ + {Error::Type::Warning, "The use of non-functional"}, + {Error::Type::Warning, "The use of non-functional"} + })); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_050_labels) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract C { + function f() pure public { + assembly { + label: + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector>{ + {Error::Type::SyntaxError, "Jump instructions and labels are low-level"}, + {Error::Type::SyntaxError, "The use of labels is deprecated"} + })); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_labels) +{ + char const* text = R"( + contract C { + function f() pure public { + assembly { + label: + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector>{ + {Error::Type::Warning, "Jump instructions and labels are low-level"}, + {Error::Type::Warning, "The use of labels is deprecated"} + })); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_050_jump) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract C { + function f() pure public { + assembly { + jump(2) + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector>{ + {Error::Type::SyntaxError, "Jump instructions and labels are low-level"} + })); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_jump) +{ + char const* text = R"( + contract C { + function f() pure public { + assembly { + jump(2) + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector>{ + {Error::Type::TypeError, "Function declared as pure"}, + {Error::Type::Warning, "Jump instructions and labels are low-level"} + })); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_050_leave_items_on_stack) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract C { + function f() pure public { + assembly { + mload(0) + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector>{ + {Error::Type::SyntaxError, "are not supposed to return"}, + {Error::Type::DeclarationError, "Unbalanced stack"}, + })); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_leave_items_on_stack) +{ + char const* text = R"( + contract C { + function f() pure public { + assembly { + mload(0) + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector>{ + {Error::Type::Warning, "are not supposed to return"}, + {Error::Type::DeclarationError, "Unbalanced stack"}, + })); +} + BOOST_AUTO_TEST_CASE(invalid_mobile_type) { char const* text = R"( @@ -7157,6 +7343,7 @@ BOOST_AUTO_TEST_CASE(returndatacopy_as_variable) )"; vector> expectations(vector>{ {Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"}, + {Error::Type::Warning, "The use of non-functional instructions is deprecated."}, {Error::Type::DeclarationError, "Unbalanced stack"} }); if (!dev::test::Options::get().evmVersion().supportsReturndata()) @@ -7172,7 +7359,8 @@ BOOST_AUTO_TEST_CASE(create2_as_variable) CHECK_ALLOW_MULTI(text, (std::vector>{ {Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"}, {Error::Type::Warning, "The \"create2\" instruction is not supported by the VM version"}, - {Error::Type::DeclarationError, "Unbalanced stack"} + {Error::Type::DeclarationError, "Unbalanced stack"}, + {Error::Type::Warning, "not supposed to return values"} })); } -- cgit v1.2.3 From af7c64c1fc56221736320582dd9644470f157fa3 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Feb 2018 15:47:50 +0100 Subject: Changelog entry. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index c643c943..b4ba572f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Features: * Code Generator: Assert that ``k != 0`` for ``mulmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature. * Code Generator: Do not retain any gas in calls (except if EVM version is set to homestead). * Code Generator: Use ``STATICCALL`` opcode for calling ``view`` and ``pure`` functions as experimenal 0.5.0 feature. + * Inline Assembly: Enforce strict mode as experimental 0.5.0 feature. * Interface: Provide ability to select target EVM version (homestead or byzantium, with byzantium being the default). * Standard JSON: Reject badly formatted invalid JSON inputs. * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. -- cgit v1.2.3 From b4d38c5491fbde31dd36471ff442334cf906e180 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 6 Mar 2018 18:09:54 +0100 Subject: Use double quotes for suggestion about hex literals and denominations. --- libsolidity/analysis/TypeChecker.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 1748b518..6f55941f 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2065,13 +2065,13 @@ void TypeChecker::endVisit(Literal const& _literal) m_errorReporter.fatalTypeError( _literal.location(), "Hexadecimal numbers cannot be used with unit denominations. " - "You can use an expression of the form '0x1234 * 1 day' instead." + "You can use an expression of the form \"0x1234 * 1 day\" instead." ); else m_errorReporter.warning( _literal.location(), "Hexadecimal numbers with unit denominations are deprecated. " - "You can use an expression of the form '0x1234 * 1 day' instead." + "You can use an expression of the form \"0x1234 * 1 day\" instead." ); } if (!_literal.annotation().type) -- cgit v1.2.3 From 7566787cd247f9a84c1adb5ced9356b9ada0fa8e Mon Sep 17 00:00:00 2001 From: ankit raj Date: Wed, 7 Mar 2018 00:09:53 +0530 Subject: Some words on Remix --- docs/index.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index 184d0e69..f53b0fc4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -19,7 +19,9 @@ crowdfunding, blind auctions, multi-signature wallets and more. .. note:: The best way to try out Solidity right now is using `Remix `_ - (it can take a while to load, please be patient). + (it can take a while to load, please be patient). Remix is a web browser + based IDE that allows you to write Solidity smart contracts, then deploy + and run the smart contracts. .. warning:: Since software is written by humans, it can have bugs. Thus, also -- cgit v1.2.3 From ea48d8ade34910838fb9c07ad5494d17935d250b Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 6 Mar 2018 20:15:43 +0100 Subject: Fix test. --- test/libsolidity/SolidityNameAndTypeResolution.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 85ca3968..997b610e 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -8491,7 +8491,7 @@ BOOST_AUTO_TEST_CASE(no_warning_for_using_members_that_look_like_address_members } } )"; - CHECK_WARNING(text, "Experimental features"); + CHECK_SUCCESS_NO_WARNINGS(text); } BOOST_AUTO_TEST_CASE(emit_events) -- cgit v1.2.3 From cd2e1e079a6a74418a2f30c8169cdf17b61044ee Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 6 Mar 2018 16:20:55 +0100 Subject: Clarify that the experimental flag is not set for "experimental 0.5.0" --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 2792c5e7..fe4bd7fd 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,7 +11,7 @@ Features: * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. * Support and recommend using ``emit EventName();`` to call events explicitly. * Syntax Analyser: Do not warn about experimental features if they do not concern code generation. - * Syntax Analyser: Do not warn about ``pragma experimental "v0.5.0"`` since it does not affect code generation. + * Syntax Analyser: Do not warn about ``pragma experimental "v0.5.0"`` and do not set the experimental flag in the bytecode for this. * Syntax Checker: Mark ``throw`` as an error as experimental 0.5.0 feature. * Syntax Checker: Issue error if no visibility is specified on contract functions as experimental 0.5.0 feature. * Syntax Checker: Issue warning when using overloads of ``address`` on contract instances. -- cgit v1.2.3 From cbd8644f2d17d786870fcf94a798ec9e932690db Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 7 Mar 2018 18:15:25 +0100 Subject: Update changelog for release. --- Changelog.md | 8 ++++---- docs/bugs_by_version.json | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index fe4bd7fd..98528893 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,22 +1,22 @@ -### 0.4.21 (unreleased) +### 0.4.21 (2018-03-07) Features: - * C99/C++-style scoping rules (instead of JavaScript function scoping) take effect as experimental v0.5.0 feature. * Code Generator: Assert that ``k != 0`` for ``mulmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature. * Code Generator: Do not retain any gas in calls (except if EVM version is set to homestead). * Code Generator: Use ``STATICCALL`` opcode for calling ``view`` and ``pure`` functions as experimenal 0.5.0 feature. + * General: C99/C++-style scoping rules (instead of JavaScript function scoping) take effect as experimental v0.5.0 feature. + * General: Improved messaging when error spans multiple lines of a sourcefile + * General: Support and recommend using ``emit EventName();`` to call events explicitly. * Inline Assembly: Enforce strict mode as experimental 0.5.0 feature. * Interface: Provide ability to select target EVM version (homestead or byzantium, with byzantium being the default). * Standard JSON: Reject badly formatted invalid JSON inputs. * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. - * Support and recommend using ``emit EventName();`` to call events explicitly. * Syntax Analyser: Do not warn about experimental features if they do not concern code generation. * Syntax Analyser: Do not warn about ``pragma experimental "v0.5.0"`` and do not set the experimental flag in the bytecode for this. * Syntax Checker: Mark ``throw`` as an error as experimental 0.5.0 feature. * Syntax Checker: Issue error if no visibility is specified on contract functions as experimental 0.5.0 feature. * Syntax Checker: Issue warning when using overloads of ``address`` on contract instances. * Type Checker: disallow combining hex numbers and unit denominations as experimental 0.5.0 feature. - * Improved messaging when error spans multiple lines of a sourcefile Bugfixes: * Assembly: Raise error on oversized number literals in assembly. diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 5a4c9e29..4c976a32 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -418,6 +418,10 @@ "bugs": [], "released": "2018-02-14" }, + "0.4.21": { + "bugs": [], + "released": "2018-03-07" + }, "0.4.3": { "bugs": [ "ZeroFunctionSelector", -- cgit v1.2.3