diff options
-rw-r--r-- | Changelog.md | 21 | ||||
-rw-r--r-- | docs/control-structures.rst | 3 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 7 | ||||
-rw-r--r-- | libsolidity/grammar.txt | 6 | ||||
-rw-r--r-- | libsolidity/parsing/Token.h | 2 | ||||
-rw-r--r-- | solc/CommandLineInterface.cpp | 40 | ||||
-rw-r--r-- | test/libsolidity/SolidityEndToEndTest.cpp | 20 |
7 files changed, 69 insertions, 30 deletions
diff --git a/Changelog.md b/Changelog.md index 27f88cd2..2d9c6c31 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,13 +7,16 @@ enforce some safety features. The most important change is Breaking Changes: * Source files have to specify the compiler version they are - compatible with using e.g. `pragma solidity ^0.4.0;` or - `pragma solidity >=0.4.0 <0.4.8;` - * Contracts that want to receive Ether have to implement a fallback - function (contracts now throw if no fallback function is defined - and no function matches the signature). - * Failing contract creation through "new" throws now. - * Throw on division / modulus by zero + compatible with using e.g. ``pragma solidity ^0.4.0;`` or + ``pragma solidity >=0.4.0 <0.4.8;`` + * Functions that want to receive Ether have to specify the + new ``payable`` modifier (otherwise they throw). + * Contracts that want to receive Ether with a plain "send" + have to implement a fallback function with the ``payable`` + modifier. Contracts now throw if no payable fallback + function is defined and no function matches the signature. + * Failing contract creation through "new" throws. + * Division / modulus by zero throws * Function call throws if target contract does not have code * Modifiers are required to contain ``_`` (use ``if (false) _`` as a workaround if needed). * Modifiers: return does not skip part in modifier after ``_`` @@ -25,7 +28,7 @@ Breaking Changes: * Moved (and reworked) standard library contracts from inside the compiler to github.com/ethereum/solidity/std (``import "std";`` or ``import owned;`` do not work anymore). * Confusing and undocumented keyword "after" was removed. - * New reserved words: hex, payable, abstract, static, interface + * New reserved words: abstract, hex, interface, payable, pure, static, view Features: @@ -40,8 +43,10 @@ Bugfixes: * JSON AST: nodes were added at wrong parent * Why3 translator: crash fix for exponentiation + * Commandline Interface: linking libraries with underscores in their name. * Type Checker: Fallback function cannot return data anymore. * Code Generator: Fix crash when sha3() was used on unsupported types. + * Code Generator: Manually set gas stipend for .send(0). Lots of changes to the documentation mainly by voluntary external contributors. diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 0e430b6b..e6dfe4f6 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -523,7 +523,8 @@ The opcodes ``pushi`` and ``jumpdest`` cannot be used directly. +-------------------------+------+-----------------------------------------------------------------+ | 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 1 on error (out of gas) | +| | | 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 | diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 1cc4a50d..96ca4296 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -568,12 +568,17 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) break; case Location::Send: _functionCall.expression().accept(*this); - m_context << u256(0); // do not send gas (there still is the stipend) + // Provide the gas stipend manually at first because we may send zero ether. + // Will be zeroed if we send more than zero ether. + m_context << u256(eth::GasCosts::callStipend); arguments.front()->accept(*this); utils().convertType( *arguments.front()->annotation().type, *function.parameterTypes().front(), true ); + // gas <- gas * !value + m_context << Instruction::SWAP1 << Instruction::DUP2; + m_context << Instruction::ISZERO << Instruction::MUL << Instruction::SWAP1; appendExternalFunctionCall( FunctionType( TypePointers{}, diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt index 86df3db0..755cf281 100644 --- a/libsolidity/grammar.txt +++ b/libsolidity/grammar.txt @@ -43,9 +43,9 @@ StorageLocation = 'memory' | 'storage' Block = '{' Statement* '}' Statement = IfStatement | WhileStatement | ForStatement | Block | ( PlaceholderStatement | Continue | Break | Return | - Throw | SimpleStatement | ExpressionStatement ) ';' + Throw | SimpleStatement ) ';' -ExpressionStatement = Expression | VariableDefinition +ExpressionStatement = Expression IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )? WhileStatement = 'while' '(' Expression ')' Statement PlaceholderStatement = '_' @@ -79,7 +79,7 @@ Expression = PrimaryExpression = Identifier | BooleanLiteral | NumberLiteral | StringLiteral -FunctionCall = Identifier '(' Expression? ( ',' Expression )* ')' +FunctionCall = ( PrimaryExpression | NewExpression | TypeName ) ( ( '.' Identifier ) | ( '[' Expression ']' ) )* '(' Expression? ( ',' Expression )* ')' NewExpression = 'new' Identifier MemberAccess = Expression '.' Identifier IndexAccess = Expression '[' Expression? ']' diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index cc85b610..2bf7419e 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -230,12 +230,14 @@ namespace solidity K(Let, "let", 0) \ K(Match, "match", 0) \ K(Of, "of", 0) \ + K(Pure, "pure", 0) \ K(Relocatable, "relocatable", 0) \ K(Static, "static", 0) \ K(Switch, "switch", 0) \ K(Try, "try", 0) \ K(Type, "type", 0) \ K(TypeOf, "typeof", 0) \ + K(View, "view", 0) \ /* Illegal token - not able to scan. */ \ T(Illegal, "ILLEGAL", 0) \ \ diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index fbef56f0..f0a34632 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -778,37 +778,43 @@ void CommandLineInterface::actOnInput() bool CommandLineInterface::link() { + // Map from how the libraries will be named inside the bytecode to their addresses. + map<string, h160> librariesReplacements; + int const placeholderSize = 40; // 20 bytes or 40 hex characters + for (auto const& library: m_libraries) + { + string const& name = library.first; + // Library placeholders are 40 hex digits (20 bytes) that start and end with '__'. + // This leaves 36 characters for the library name, while too short library names are + // padded on the right with '_' and too long names are truncated. + string replacement = "__"; + for (size_t i = 0; i < placeholderSize - 4; ++i) + replacement.push_back(i < name.size() ? name[i] : '_'); + replacement += "__"; + librariesReplacements[replacement] = library.second; + } for (auto& src: m_sourceCodes) { auto end = src.second.end(); for (auto it = src.second.begin(); it != end;) { while (it != end && *it != '_') ++it; - auto insertStart = it; - while (it != end && *it == '_') ++it; - auto nameStart = it; - while (it != end && *it != '_') ++it; - auto nameEnd = it; - while (it != end && *it == '_') ++it; - auto insertEnd = it; - - if (insertStart == end) - break; - - if (insertEnd - insertStart != 40) + if (it == end) break; + if (end - it < placeholderSize) { - cerr << "Error in binary object file " << src.first << " at position " << (insertStart - src.second.begin()) << endl; + cerr << "Error in binary object file " << src.first << " at position " << (end - src.second.begin()) << endl; return false; } - string name(nameStart, nameEnd); - if (m_libraries.count(name)) + string name(it, it + placeholderSize); + if (librariesReplacements.count(name)) { - string hexStr(toHex(m_libraries.at(name).asBytes())); - copy(hexStr.begin(), hexStr.end(), insertStart); + string hexStr(toHex(librariesReplacements.at(name).asBytes())); + copy(hexStr.begin(), hexStr.end(), it); } else cerr << "Reference \"" << name << "\" in file \"" << src.first << "\" still unresolved." << endl; + it += placeholderSize; } } return true; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 1ecd7a2c..3c85d8a8 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -4625,6 +4625,26 @@ BOOST_AUTO_TEST_CASE(failing_send) BOOST_REQUIRE(callContractFunction("callHelper(address)", c_helperAddress) == encodeArgs(true, 20)); } +BOOST_AUTO_TEST_CASE(send_zero_ether) +{ + // Sending zero ether to a contract should still invoke the fallback function + // (it previously did not because the gas stipend was not provided by the EVM) + char const* sourceCode = R"( + contract Receiver { + function () payable { + } + } + contract Main { + function s() returns (bool) { + var r = new Receiver(); + return r.send(0); + } + } + )"; + compileAndRun(sourceCode, 20, "Main"); + BOOST_REQUIRE(callContractFunction("s()") == encodeArgs(true)); +} + BOOST_AUTO_TEST_CASE(reusing_memory) { // Invoke some features that use memory and test that they do not interfere with each other. |