From e5514becb89c945f59fd440696d0bb3122edbe99 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 7 Mar 2016 16:55:53 +0100 Subject: [PATCH 01/14] BREAKING: Implement delegatecall and make default for library calls. --- libsolidity/ast/Types.cpp | 47 ++++++++-------- libsolidity/ast/Types.h | 4 +- libsolidity/codegen/Compiler.cpp | 10 ++-- libsolidity/codegen/Compiler.h | 2 +- libsolidity/codegen/ExpressionCompiler.cpp | 29 ++++++---- libsolidity/interface/CompilerStack.h | 2 +- solc/jsonCompiler.cpp | 2 +- test/libsolidity/GasMeter.cpp | 2 +- test/libsolidity/SolidityEndToEndTest.cpp | 56 +++++++++++++++++++ .../SolidityNameAndTypeResolution.cpp | 13 +++++ 10 files changed, 122 insertions(+), 45 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index bca83d5989c8..0696b9083b1f 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -333,6 +333,7 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons {"balance", make_shared(256)}, {"call", make_shared(strings(), strings{"bool"}, FunctionType::Location::Bare, true)}, {"callcode", make_shared(strings(), strings{"bool"}, FunctionType::Location::BareCallCode, true)}, + {"delegatecall", make_shared(strings(), strings{"bool"}, FunctionType::Location::BareDelegateCall, true)}, {"send", make_shared(strings{"uint"}, strings{"bool"}, FunctionType::Location::Send)} }; else @@ -1561,9 +1562,9 @@ unsigned FunctionType::sizeOnStack() const } unsigned size = 0; - if (location == Location::External || location == Location::CallCode) + if (location == Location::External || location == Location::CallCode || location == Location::DelegateCall) size = 2; - else if (location == Location::Bare || location == Location::BareCallCode) + else if (location == Location::Bare || location == Location::BareCallCode || location == Location::BareDelegateCall) size = 1; else if (location == Location::Internal) size = 1; @@ -1619,9 +1620,11 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con case Location::RIPEMD160: case Location::Bare: case Location::BareCallCode: + case Location::BareDelegateCall: { - MemberList::MemberMap members{ - { + MemberList::MemberMap members; + if (m_location != Location::BareDelegateCall) + members.push_back(MemberList::Member( "value", make_shared( parseElementaryTypeVector({"uint"}), @@ -1634,25 +1637,22 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con m_gasSet, m_valueSet ) - } - }; + )); if (m_location != Location::Creation) - members.push_back( - MemberList::Member( - "gas", - make_shared( - parseElementaryTypeVector({"uint"}), - TypePointers{copyAndSetGasOrValue(true, false)}, - strings(), - strings(), - Location::SetGas, - false, - nullptr, - m_gasSet, - m_valueSet - ) + members.push_back(MemberList::Member( + "gas", + make_shared( + parseElementaryTypeVector({"uint"}), + TypePointers{copyAndSetGasOrValue(true, false)}, + strings(), + strings(), + Location::SetGas, + false, + nullptr, + m_gasSet, + m_valueSet ) - ); + )); return members; } default: @@ -1700,6 +1700,7 @@ bool FunctionType::isBareCall() const { case Location::Bare: case Location::BareCallCode: + case Location::BareDelegateCall: case Location::ECRecover: case Location::SHA256: case Location::RIPEMD160: @@ -1785,7 +1786,7 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound) returnParameterTypes, m_parameterNames, returnParameterNames, - _inLibrary ? Location::CallCode : m_location, + _inLibrary ? Location::DelegateCall : m_location, m_arbitraryParameters, m_declaration, m_gasSet, @@ -1884,7 +1885,7 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current for (auto const& it: contract.interfaceFunctions()) members.push_back(MemberList::Member( it.second->declaration().name(), - it.second->asMemberFunction(true), // use callcode + it.second->asMemberFunction(true), &it.second->declaration() )); if (isBase) diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index b4a2d5739713..189dd10a5afc 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -732,8 +732,10 @@ class FunctionType: public Type Internal, ///< stack-call using plain JUMP External, ///< external call using CALL CallCode, ///< extercnal call using CALLCODE, i.e. not exchanging the storage + DelegateCall, ///< extercnal call using DELEGATECALL, i.e. not exchanging the storage Bare, ///< CALL without function hash BareCallCode, ///< CALLCODE without function hash + BareDelegateCall, ///< DELEGATECALL without function hash Creation, ///< external call using CREATE Send, ///< CALL, but without data and gas SHA3, ///< SHA3 @@ -869,7 +871,7 @@ class FunctionType: public Type /// removed and the location of reference types is changed from CallData to Memory. /// This is needed if external functions are called on other contracts, as they cannot return /// dynamic values. - /// @param _inLibrary if true, uses CallCode as location. + /// @param _inLibrary if true, uses DelegateCall as location. /// @param _bound if true, the argumenst are placed as `arg1.functionName(arg2, ..., argn)`. FunctionTypePointer asMemberFunction(bool _inLibrary, bool _bound = false) const; diff --git a/libsolidity/codegen/Compiler.cpp b/libsolidity/codegen/Compiler.cpp index 18803b715d14..c7eb71a8f252 100644 --- a/libsolidity/codegen/Compiler.cpp +++ b/libsolidity/codegen/Compiler.cpp @@ -773,15 +773,13 @@ eth::Assembly Compiler::cloneRuntime() a << u256(0) << eth::Instruction::DUP1 << eth::Instruction::CALLDATACOPY; //@todo adjust for larger return values, make this dynamic. a << u256(0x20) << u256(0) << eth::Instruction::CALLDATASIZE; - // unfortunately, we have to send the value again, so that CALLVALUE returns the correct value - // in the callcoded contract. - a << u256(0) << eth::Instruction::CALLVALUE; + a << u256(0); // this is the address which has to be substituted by the linker. //@todo implement as special "marker" AssemblyItem. a << u256("0xcafecafecafecafecafecafecafecafecafecafe"); - a << u256(schedule.callGas + schedule.callValueTransferGas + 10) << eth::Instruction::GAS << eth::Instruction::SUB; - a << eth::Instruction::CALLCODE; - //Propagate error condition (if CALLCODE pushes 0 on stack). + a << u256(schedule.callGas + 10) << eth::Instruction::GAS << eth::Instruction::SUB; + a << eth::Instruction::DELEGATECALL; + //Propagate error condition (if DELEGATECALL pushes 0 on stack). a << eth::Instruction::ISZERO; a.appendJumpI(a.errorTag()); //@todo adjust for larger return values, make this dynamic. diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h index 9d069f7c2f4a..fa33bd3068ab 100644 --- a/libsolidity/codegen/Compiler.h +++ b/libsolidity/codegen/Compiler.h @@ -44,7 +44,7 @@ class Compiler: private ASTConstVisitor ContractDefinition const& _contract, std::map const& _contracts ); - /// Compiles a contract that uses CALLCODE to call into a pre-deployed version of the given + /// Compiles a contract that uses DELEGATECALL to call into a pre-deployed version of the given /// contract at runtime, but contains the full creation-time code. void compileClone( ContractDefinition const& _contract, diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 58db07b1f924..e0b2b5f60158 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -465,8 +465,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) { FunctionType const& function = *functionType; if (function.bound()) - // Only callcode functions can be bound, this might be lifted later. - solAssert(function.location() == Location::CallCode, ""); + // Only delegatecall functions can be bound, this might be lifted later. + solAssert(function.location() == Location::DelegateCall, ""); switch (function.location()) { case Location::Internal: @@ -492,8 +492,10 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } case Location::External: case Location::CallCode: + case Location::DelegateCall: case Location::Bare: case Location::BareCallCode: + case Location::BareDelegateCall: _functionCall.expression().accept(*this); appendExternalFunctionCall(function, arguments); break; @@ -875,7 +877,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) ); m_context << eth::Instruction::BALANCE; } - else if ((set{"send", "call", "callcode"}).count(member)) + else if ((set{"send", "call", "callcode", "delegatecall"}).count(member)) utils().convertType( *_memberAccess.expression().annotation().type, IntegerType(0, IntegerType::Modifier::Address), @@ -1356,6 +1358,7 @@ void ExpressionCompiler::appendExternalFunctionCall( FunctionKind funKind = _functionType.location(); bool returnSuccessCondition = funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode; bool isCallCode = funKind == FunctionKind::BareCallCode || funKind == FunctionKind::CallCode; + bool isDelegateCall = funKind == FunctionKind::BareDelegateCall || funKind == FunctionKind::DelegateCall; unsigned retSize = 0; if (returnSuccessCondition) @@ -1371,13 +1374,13 @@ void ExpressionCompiler::appendExternalFunctionCall( TypePointers argumentTypes; TypePointers parameterTypes = _functionType.parameterTypes(); bool manualFunctionId = - (funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode) && + (funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode || funKind == FunctionKind::BareDelegateCall) && !_arguments.empty() && _arguments.front()->annotation().type->mobileType()->calldataEncodedSize(false) == CompilerUtils::dataStartOffset; if (manualFunctionId) { - // If we have a BareCall or BareCallCode and the first type has exactly 4 bytes, use it as + // If we have a Bare* and the first type has exactly 4 bytes, use it as // function identifier. _arguments.front()->accept(*this); utils().convertType( @@ -1416,7 +1419,7 @@ void ExpressionCompiler::appendExternalFunctionCall( parameterTypes, _functionType.padArguments(), _functionType.takesArbitraryParameters(), - isCallCode + isCallCode || isDelegateCall ); // Stack now: @@ -1435,8 +1438,10 @@ void ExpressionCompiler::appendExternalFunctionCall( m_context << eth::Instruction::DUP2; // CALL arguments: outSize, outOff, inSize, inOff (already present up to here) - // value, addr, gas (stack top) - if (_functionType.valueSet()) + // [value,] addr, gas (stack top) + if (isDelegateCall) + solAssert(!_functionType.valueSet(), "Value set for delegatecall"); + else if (_functionType.valueSet()) m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos)); else m_context << u256(0); @@ -1446,20 +1451,22 @@ void ExpressionCompiler::appendExternalFunctionCall( m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos)); else { - eth::EVMSchedule schedule;// TODO: Make relevant to current suppose context. + eth::EVMSchedule schedule; // 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 = schedule.callGas + 10; if (_functionType.valueSet()) gasNeededByCaller += schedule.callValueTransferGas; - if (!isCallCode) + if (!isCallCode && !isDelegateCall) gasNeededByCaller += schedule.callNewAccountGas; // we never know m_context << gasNeededByCaller << eth::Instruction::GAS << eth::Instruction::SUB; } - if (isCallCode) + if (isDelegateCall) + m_context << eth::Instruction::DELEGATECALL; + else if (isCallCode) m_context << eth::Instruction::CALLCODE; else m_context << eth::Instruction::CALL; diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 517d0055703f..1d13a23c3de1 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -124,7 +124,7 @@ class CompilerStack: boost::noncopyable eth::LinkerObject const& object(std::string const& _contractName = "") const; /// @returns the runtime object for the contract. eth::LinkerObject const& runtimeObject(std::string const& _contractName = "") const; - /// @returns the bytecode of a contract that uses an already deployed contract via CALLCODE. + /// @returns the bytecode of a contract that uses an already deployed contract via DELEGATECALL. /// The returned bytes will contain a sequence of 20 bytes of the format "XXX...XXX" which have to /// substituted by the actual address. Note that this sequence starts end ends in three X /// characters but can contain anything in between. diff --git a/solc/jsonCompiler.cpp b/solc/jsonCompiler.cpp index 987f8dff4b64..b5efa94d49c6 100644 --- a/solc/jsonCompiler.cpp +++ b/solc/jsonCompiler.cpp @@ -72,7 +72,7 @@ Json::Value gasToJson(GasEstimator::GasConsumption const& _gas) Json::Value estimateGas(CompilerStack const& _compiler, string const& _contract) { - eth::EVMSchedule schedule;// TODO: make relevant to supposed context. + eth::EVMSchedule schedule; Json::Value gasEstimates(Json::objectValue); using Gas = GasEstimator::GasConsumption; if (!_compiler.assemblyItems(_contract) && !_compiler.runtimeAssemblyItems(_contract)) diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index 25df9e4de6b4..9f947af3bc3a 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -59,7 +59,7 @@ class GasMeterTestFramework: public ExecutionFramework void testCreationTimeGas(string const& _sourceCode) { - EVMSchedule schedule;// TODO: make relevant to supposed context. + EVMSchedule schedule; compileAndRun(_sourceCode); auto state = make_shared(); diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index b54393e4c123..388f5a69500d 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2890,6 +2890,62 @@ BOOST_AUTO_TEST_CASE(generic_callcode) BOOST_CHECK_EQUAL(m_state.balance(c_senderAddress), 50); } +BOOST_AUTO_TEST_CASE(generic_delegatecall) +{ + char const* sourceCode = R"**( + contract receiver { + uint public received; + address public sender; + uint public value; + function receive(uint256 x) { received = x; sender = msg.sender; value = msg.value; } + } + contract sender { + uint public received; + address public sender; + uint public value; + function doSend(address rec) + { + bytes4 signature = bytes4(bytes32(sha3("receive(uint256)"))); + rec.delegatecall(signature, 23); + } + } + )**"; + compileAndRun(sourceCode, 0, "receiver"); + u160 const c_receiverAddress = m_contractAddress; + compileAndRun(sourceCode, 50, "sender"); + u160 const c_senderAddress = m_contractAddress; + BOOST_CHECK(m_sender != c_senderAddress); // just for sanity + BOOST_CHECK(callContractFunctionWithValue("doSend(address)", 11, c_receiverAddress) == encodeArgs()); + BOOST_CHECK(callContractFunction("received()") == encodeArgs(u256(23))); + BOOST_CHECK(callContractFunction("sender()") == encodeArgs(u160(m_sender))); + BOOST_CHECK(callContractFunction("value()") == encodeArgs(u256(11))); + m_contractAddress = c_receiverAddress; + BOOST_CHECK(callContractFunction("received()") == encodeArgs(u256(0))); + BOOST_CHECK(callContractFunction("sender()") == encodeArgs(u256(0))); + BOOST_CHECK(callContractFunction("value()") == encodeArgs(u256(0))); + BOOST_CHECK(m_state.storage(c_receiverAddress).empty()); + BOOST_CHECK(!m_state.storage(c_senderAddress).empty()); + BOOST_CHECK_EQUAL(m_state.balance(c_receiverAddress), 0); + BOOST_CHECK_EQUAL(m_state.balance(c_senderAddress), 50 + 11); +} + +BOOST_AUTO_TEST_CASE(library_call_in_homestead) +{ + char const* sourceCode = R"( + library Lib { function m() returns (address) { return msg.sender; } } + contract Test { + address public sender; + function f() { + sender = Lib.m(); + } + } + )"; + compileAndRun(sourceCode, 0, "Lib"); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + BOOST_CHECK(callContractFunction("f()") == encodeArgs()); + BOOST_CHECK(callContractFunction("sender()") == encodeArgs(u160(m_sender))); +} + BOOST_AUTO_TEST_CASE(store_bytes) { // this test just checks that the copy loop does not mess up the stack diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 5f6c05356119..b2e46b7dd51f 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -3232,6 +3232,19 @@ BOOST_AUTO_TEST_CASE(int10abc_is_identifier) BOOST_CHECK(success(text)); } +BOOST_AUTO_TEST_CASE(library_functions_do_not_have_value) +{ + char const* text = R"( + library L { function l() {} } + contract test { + function f() { + L.l.value; + } + } + )"; + BOOST_CHECK(!success(text)); +} + BOOST_AUTO_TEST_SUITE_END() } From 29b74be413ea2661f2fa181bff89bef2371deb51 Mon Sep 17 00:00:00 2001 From: RJ Catalano Date: Mon, 7 Mar 2016 10:12:03 -0600 Subject: [PATCH 02/14] fixed keyword added in for token type --- libsolidity/parsing/Token.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index a64eded56262..59b5e520e8f6 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -198,8 +198,10 @@ namespace solidity K(String, "string", 0) \ K(Address, "address", 0) \ K(Bool, "bool", 0) \ - K(Real, "real", 0) \ - K(UReal, "ureal", 0) \ + K(Fixed, "fixed", 0) \ + T(FixedMxN, "fixedMxN", 0) \ + K(UFixed, "ufixed", 0) \ + T(UFixedMxN, "ufixedMxN", 0) \ T(TypesEnd, NULL, 0) /* used as type enum end marker */ \ \ /* Literals */ \ From 953e92b6f53ffee90cac3a79828a668cf1a55435 Mon Sep 17 00:00:00 2001 From: RJ Catalano Date: Mon, 7 Mar 2016 12:29:41 -0600 Subject: [PATCH 03/14] added from identifier or keyword handling of fixed types --- libsolidity/parsing/Token.cpp | 34 ++++++++++++++++++++++++++++++++-- libsolidity/parsing/Token.h | 9 +++++---- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/libsolidity/parsing/Token.cpp b/libsolidity/parsing/Token.cpp index 78a905597dc7..9e8bc503bc0d 100644 --- a/libsolidity/parsing/Token.cpp +++ b/libsolidity/parsing/Token.cpp @@ -66,6 +66,13 @@ void ElementaryTypeNameToken::assertDetails(Token::Value _baseType, unsigned con "No elementary type " + string(Token::toString(_baseType)) + to_string(_first) + "." ); } + else if (_baseType == Token::UFixedMxN || _baseType == Token::FixedMxN) + { + solAssert( + _first + _second <= 256 && _first % 8 == 0 && _second % 8 == 0, + "No elementary type " + string(Token::toString(_baseType)) + to_string(_first) + "x" + to_string(_second) + "." + ); + } m_token = _baseType; m_firstNumber = _first; m_secondNumber = _second; @@ -101,7 +108,7 @@ char const Token::m_tokenType[] = { TOKEN_LIST(KT, KK) }; -unsigned Token::extractM(string const& _literal) +unsigned Token::extractUnsigned(string const& _literal) { try { @@ -112,6 +119,10 @@ unsigned Token::extractM(string const& _literal) { return 0; } + catch (invalid_argument& e) + { + return 1; + } } tuple Token::fromIdentifierOrKeyword(string const& _literal) { @@ -120,7 +131,7 @@ tuple Token::fromIdentifierOrKeywo { string baseType(_literal.begin(), positionM); auto positionX = find_if_not(positionM, _literal.end(), ::isdigit); - unsigned short m = extractM(string(positionM, positionX)); + unsigned short m = extractUnsigned(string(positionM, positionX)); Token::Value keyword = keywordByName(baseType); if (keyword == Token::Bytes) { @@ -137,6 +148,25 @@ tuple Token::fromIdentifierOrKeywo return make_tuple(Token::IntM, m, 0); } } + else if (keyword == Token::UFixed || keyword == Token::Fixed) + { + auto positionN = find_if_not(positionX + 1, _literal.end(), ::isdigit); + unsigned short n = extractUnsigned(string(positionX + 1, positionN)); + if ( + 0 < m + n && + m + n <= 256 && + m % 8 == 0 && + n % 8 == 0 && + positionN == _literal.end() && + *positionX == 'x' + ) + { + if (keyword == Token::UFixed) + return make_tuple(Token::UFixed, m, n); + else + return make_tuple(Token::Fixed, m, n); + } + } return make_tuple(Token::Identifier, 0, 0); } return make_tuple(keywordByName(_literal), 0, 0); diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index 59b5e520e8f6..c81e42100cea 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -305,10 +305,11 @@ class Token static std::tuple fromIdentifierOrKeyword(std::string const& _literal); private: - // extractM provides a safe way to extract numbers, - // if out_of_range error is thrown, they returns 0s, therefore securing - // the variable's identity as an identifier. - static unsigned extractM(std::string const& _literal); + // extractUnsigned provides a safe way to extract numbers, + // the variable's identity as an identifier. If an invalid conversion + // error is thrown (usually in the case of grabbing N from a fixed type) + // then a 1 is thrown to purposely ensure that it will declare itself as an identifier + static unsigned extractUnsigned(std::string const& _literal); // @returns the keyword with name @a _name or Token::Identifier of no such keyword exists. static Token::Value keywordByName(std::string const& _name); static char const* const m_name[NUM_TOKENS]; From 93114949a3fa03db39d747b7f97969e167d3748b Mon Sep 17 00:00:00 2001 From: RJ Catalano Date: Mon, 7 Mar 2016 12:31:44 -0600 Subject: [PATCH 04/14] needed this one string of notation --- libsolidity/parsing/Token.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index c81e42100cea..e415ed4fb75b 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -306,6 +306,7 @@ class Token private: // extractUnsigned provides a safe way to extract numbers, + // if out_of_range error is thrown, they returns 0s, therefore securing // the variable's identity as an identifier. If an invalid conversion // error is thrown (usually in the case of grabbing N from a fixed type) // then a 1 is thrown to purposely ensure that it will declare itself as an identifier From 2738f4066a3786bea5d5c0b49709fa99547b8eff Mon Sep 17 00:00:00 2001 From: RJ Catalano Date: Mon, 7 Mar 2016 13:55:48 -0600 Subject: [PATCH 05/14] changed 1 to 0 in invalid argument catch block --- libsolidity/parsing/Token.cpp | 2 +- libsolidity/parsing/Token.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libsolidity/parsing/Token.cpp b/libsolidity/parsing/Token.cpp index 9e8bc503bc0d..cbb46f0d83c4 100644 --- a/libsolidity/parsing/Token.cpp +++ b/libsolidity/parsing/Token.cpp @@ -121,7 +121,7 @@ unsigned Token::extractUnsigned(string const& _literal) } catch (invalid_argument& e) { - return 1; + return 0; } } tuple Token::fromIdentifierOrKeyword(string const& _literal) diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index e415ed4fb75b..ce6eed181d2b 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -309,7 +309,7 @@ class Token // if out_of_range error is thrown, they returns 0s, therefore securing // the variable's identity as an identifier. If an invalid conversion // error is thrown (usually in the case of grabbing N from a fixed type) - // then a 1 is thrown to purposely ensure that it will declare itself as an identifier + // then a 0 is thrown to purposely ensure that it will declare itself as an identifier static unsigned extractUnsigned(std::string const& _literal); // @returns the keyword with name @a _name or Token::Identifier of no such keyword exists. static Token::Value keywordByName(std::string const& _name); From 9f5c3977fb0c1a4607a955811ea3cda6e0547b66 Mon Sep 17 00:00:00 2001 From: RJ Catalano Date: Mon, 7 Mar 2016 15:36:25 -0600 Subject: [PATCH 06/14] changed extractUnsigned to handle iterators rather than a string --- libsolidity/parsing/Token.cpp | 8 ++++---- libsolidity/parsing/Token.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libsolidity/parsing/Token.cpp b/libsolidity/parsing/Token.cpp index cbb46f0d83c4..332d27f7aa29 100644 --- a/libsolidity/parsing/Token.cpp +++ b/libsolidity/parsing/Token.cpp @@ -108,11 +108,11 @@ char const Token::m_tokenType[] = { TOKEN_LIST(KT, KK) }; -unsigned Token::extractUnsigned(string const& _literal) +unsigned Token::extractUnsigned(string::const_iterator const& _begin, string::const_iterator const& _end) { try { - unsigned short m = stoi(_literal); + unsigned short m = stoi(string(_begin, _end)); return m; } catch(out_of_range& e) @@ -131,7 +131,7 @@ tuple Token::fromIdentifierOrKeywo { string baseType(_literal.begin(), positionM); auto positionX = find_if_not(positionM, _literal.end(), ::isdigit); - unsigned short m = extractUnsigned(string(positionM, positionX)); + unsigned short m = extractUnsigned(positionM, positionX); Token::Value keyword = keywordByName(baseType); if (keyword == Token::Bytes) { @@ -151,7 +151,7 @@ tuple Token::fromIdentifierOrKeywo else if (keyword == Token::UFixed || keyword == Token::Fixed) { auto positionN = find_if_not(positionX + 1, _literal.end(), ::isdigit); - unsigned short n = extractUnsigned(string(positionX + 1, positionN)); + unsigned short n = extractUnsigned(positionX + 1, positionN); if ( 0 < m + n && m + n <= 256 && diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index ce6eed181d2b..fea80ef77934 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -310,7 +310,7 @@ class Token // the variable's identity as an identifier. If an invalid conversion // error is thrown (usually in the case of grabbing N from a fixed type) // then a 0 is thrown to purposely ensure that it will declare itself as an identifier - static unsigned extractUnsigned(std::string const& _literal); + static unsigned extractUnsigned(std::string::const_iterator const& _begin, std::string::const_iterator const& _end); // @returns the keyword with name @a _name or Token::Identifier of no such keyword exists. static Token::Value keywordByName(std::string const& _name); static char const* const m_name[NUM_TOKENS]; From 67793f1aedab8cae956e8df0eec5e6a99f06b87b Mon Sep 17 00:00:00 2001 From: RJ Catalano Date: Tue, 8 Mar 2016 11:23:32 -0600 Subject: [PATCH 07/14] changed documentation and using lexical cast --- libsolidity/parsing/Token.cpp | 8 ++------ libsolidity/parsing/Token.h | 6 +----- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/libsolidity/parsing/Token.cpp b/libsolidity/parsing/Token.cpp index 332d27f7aa29..4f04ea7ab336 100644 --- a/libsolidity/parsing/Token.cpp +++ b/libsolidity/parsing/Token.cpp @@ -112,14 +112,10 @@ unsigned Token::extractUnsigned(string::const_iterator const& _begin, string::co { try { - unsigned short m = stoi(string(_begin, _end)); + unsigned short m = boost::lexical_cast(string(_begin, _end)); return m; } - catch(out_of_range& e) - { - return 0; - } - catch (invalid_argument& e) + catch(const boost::bad_lexical_cast &) { return 0; } diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index fea80ef77934..0e5b594c5c3c 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -305,11 +305,7 @@ class Token static std::tuple fromIdentifierOrKeyword(std::string const& _literal); private: - // extractUnsigned provides a safe way to extract numbers, - // if out_of_range error is thrown, they returns 0s, therefore securing - // the variable's identity as an identifier. If an invalid conversion - // error is thrown (usually in the case of grabbing N from a fixed type) - // then a 0 is thrown to purposely ensure that it will declare itself as an identifier + // @returns 0 on error (invalid digit or number too large) static unsigned extractUnsigned(std::string::const_iterator const& _begin, std::string::const_iterator const& _end); // @returns the keyword with name @a _name or Token::Identifier of no such keyword exists. static Token::Value keywordByName(std::string const& _name); From 58e07151e3fd8503bc0418dba452a822f03ff260 Mon Sep 17 00:00:00 2001 From: LianaHus Date: Wed, 9 Mar 2016 17:23:05 +0100 Subject: [PATCH 08/14] - inline and assembly keywords added - some style fixes --- libsolidity/ast/Types.h | 2 +- libsolidity/formal/Why3Translator.h | 2 +- libsolidity/interface/CompilerStack.h | 8 ++++---- libsolidity/interface/InterfaceHandler.h | 2 +- libsolidity/parsing/Parser.h | 4 ++-- libsolidity/parsing/Scanner.h | 6 +++--- libsolidity/parsing/Token.h | 8 +++++--- 7 files changed, 17 insertions(+), 15 deletions(-) diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 189dd10a5afc..1d65aeb62b0e 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -826,7 +826,7 @@ class FunctionType: public Type /// @returns TypePointer of a new FunctionType object. All input/return parameters are an /// appropriate external types (i.e. the interfaceType()s) of input/return parameters of /// current function. - /// Returns an empty shared pointer if one of the input/return parameters does not have an + /// @returns an empty shared pointer if one of the input/return parameters does not have an /// external type. FunctionTypePointer interfaceFunctionType() const; diff --git a/libsolidity/formal/Why3Translator.h b/libsolidity/formal/Why3Translator.h index f4315a7a6bdb..588b6d808f04 100644 --- a/libsolidity/formal/Why3Translator.h +++ b/libsolidity/formal/Why3Translator.h @@ -52,7 +52,7 @@ class Why3Translator: private ASTConstVisitor std::string translation() const; private: - /// Returns an error. + /// Creates an error and adds it to errors list. void error(ASTNode const& _node, std::string const& _description); /// Reports a fatal error and throws. void fatalError(ASTNode const& _node, std::string const& _description); diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 1d13a23c3de1..c7f98184b4fb 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -101,7 +101,7 @@ class CompilerStack: boost::noncopyable /// Sets the given source code as the only source unit apart from standard sources and parses it. /// @returns false on error. bool parse(std::string const& _sourceCode); - /// Returns a list of the contract names in the sources. + /// @returns a list of the contract names in the sources. std::vector contractNames() const; std::string defaultContractName() const; @@ -144,13 +144,13 @@ class CompilerStack: boost::noncopyable /// Prerequisite: Successful compilation. Json::Value streamAssembly(std::ostream& _outStream, std::string const& _contractName = "", StringMap _sourceCodes = StringMap(), bool _inJsonFormat = false) const; - /// Returns a string representing the contract interface in JSON. + /// @returns a string representing the contract interface in JSON. /// Prerequisite: Successful call to parse or compile. std::string const& interface(std::string const& _contractName = "") const; - /// Returns a string representing the contract interface in Solidity. + /// @returns a string representing the contract interface in Solidity. /// Prerequisite: Successful call to parse or compile. std::string const& solidityInterface(std::string const& _contractName = "") const; - /// Returns a string representing the contract's documentation in JSON. + /// @returns a string representing the contract's documentation in JSON. /// Prerequisite: Successful call to parse or compile. /// @param type The type of the documentation to get. /// Can be one of 4 types defined at @c DocumentationType diff --git a/libsolidity/interface/InterfaceHandler.h b/libsolidity/interface/InterfaceHandler.h index 30b8f5200a33..3e0a16601d08 100644 --- a/libsolidity/interface/InterfaceHandler.h +++ b/libsolidity/interface/InterfaceHandler.h @@ -85,7 +85,7 @@ class InterfaceHandler static std::string devDocumentation(ContractDefinition const& _contractDef); private: - /// Returns concatenation of all content under the given tag name. + /// @returns concatenation of all content under the given tag name. static std::string extractDoc(std::multimap const& _tags, std::string const& _name); }; diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 9db3b3c4e10b..a093cc5bfc75 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -124,12 +124,12 @@ class Parser: public ParserBase /// For source code of the form "a[][8]" ("IndexAccessStructure"), this is not possible to /// decide with constant look-ahead. LookAheadInfo peekStatementType() const; - /// Returns a typename parsed in look-ahead fashion from something like "a.b[8][2**70]". + /// @returns a typename parsed in look-ahead fashion from something like "a.b[8][2**70]". ASTPointer typeNameIndexAccessStructure( std::vector> const& _path, std::vector, SourceLocation>> const& _indices ); - /// Returns an expression parsed in look-ahead fashion from something like "a.b[8][2**70]". + /// @returns an expression parsed in look-ahead fashion from something like "a.b[8][2**70]". ASTPointer expressionFromIndexAccessStructure( std::vector> const& _path, std::vector, SourceLocation>> const& _indices diff --git a/libsolidity/parsing/Scanner.h b/libsolidity/parsing/Scanner.h index 8dde922da13b..cffcec8e2986 100644 --- a/libsolidity/parsing/Scanner.h +++ b/libsolidity/parsing/Scanner.h @@ -108,13 +108,13 @@ class Scanner /// Resets scanner to the start of input. void reset(); - /// Returns the next token and advances input + /// @returns the next token and advances input Token::Value next(); ///@{ ///@name Information about the current token - /// Returns the current token + /// @returns the current token Token::Value currentToken() { return m_currentToken.token; @@ -138,7 +138,7 @@ class Scanner ///@{ ///@name Information about the next token - /// Returns the next token without advancing input. + /// @returns the next token without advancing input. Token::Value peekNextToken() const { return m_nextToken.token; } SourceLocation peekLocation() const { return m_nextToken.location; } std::string const& peekLiteral() const { return m_nextToken.literal; } diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index 0e5b594c5c3c..ab240b2a18ca 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -143,6 +143,7 @@ namespace solidity \ /* Keywords */ \ K(Anonymous, "anonymous", 0) \ + K(Assembly, "assembly", 0) \ K(Break, "break", 0) \ K(Const, "constant", 0) \ K(Continue, "continue", 0) \ @@ -220,6 +221,7 @@ namespace solidity K(Case, "case", 0) \ K(Catch, "catch", 0) \ K(Final, "final", 0) \ + K(Inline, "inline", 0) \ K(Let, "let", 0) \ K(Match, "match", 0) \ K(Of, "of", 0) \ @@ -251,7 +253,7 @@ class Token }; #undef T - // Returns a string corresponding to the C++ token name + // @returns a string corresponding to the C++ token name // (e.g. "LT" for the token LT). static char const* name(Value tok) { @@ -285,7 +287,7 @@ class Token static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == SubEther; } static bool isTimeSubdenomination(Value op) { return op == SubSecond || op == SubMinute || op == SubHour || op == SubDay || op == SubWeek || op == SubYear; } - // Returns a string corresponding to the JS token string + // @returns a string corresponding to the JS token string // (.e., "<" for the token LT) or NULL if the token doesn't // have a (unique) string (e.g. an IDENTIFIER). static char const* toString(Value tok) @@ -294,7 +296,7 @@ class Token return m_string[tok]; } - // Returns the precedence > 0 for binary and compare + // @returns the precedence > 0 for binary and compare // operators; returns 0 otherwise. static int precedence(Value tok) { From b8bcb706e946ca05f55139e668fe90297a382a0a Mon Sep 17 00:00:00 2001 From: RJ Catalano Date: Wed, 9 Mar 2016 17:02:37 -0600 Subject: [PATCH 09/14] stylistic change, and got lexical cast to work with an iterator range --- libsolidity/parsing/Token.cpp | 9 +++++---- libsolidity/parsing/Token.h | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/libsolidity/parsing/Token.cpp b/libsolidity/parsing/Token.cpp index 4f04ea7ab336..4634c510b16b 100644 --- a/libsolidity/parsing/Token.cpp +++ b/libsolidity/parsing/Token.cpp @@ -42,6 +42,7 @@ #include #include +#include using namespace std; @@ -108,11 +109,11 @@ char const Token::m_tokenType[] = { TOKEN_LIST(KT, KK) }; -unsigned Token::extractUnsigned(string::const_iterator const& _begin, string::const_iterator const& _end) +unsigned Token::extractUnsigned(string::const_iterator _begin, string::const_iterator _end) { try { - unsigned short m = boost::lexical_cast(string(_begin, _end)); + unsigned short m = boost::lexical_cast(boost::make_iterator_range(_begin, _end)); return m; } catch(const boost::bad_lexical_cast &) @@ -146,8 +147,8 @@ tuple Token::fromIdentifierOrKeywo } else if (keyword == Token::UFixed || keyword == Token::Fixed) { - auto positionN = find_if_not(positionX + 1, _literal.end(), ::isdigit); - unsigned short n = extractUnsigned(positionX + 1, positionN); + auto positionN = find_if_not(positionX++, _literal.end(), ::isdigit); + unsigned short n = extractUnsigned(positionX++, positionN); if ( 0 < m + n && m + n <= 256 && diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index ab240b2a18ca..64a3447ad1cb 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -308,7 +308,7 @@ class Token private: // @returns 0 on error (invalid digit or number too large) - static unsigned extractUnsigned(std::string::const_iterator const& _begin, std::string::const_iterator const& _end); + static unsigned extractUnsigned(std::string::const_iterator _begin, std::string::const_iterator _end); // @returns the keyword with name @a _name or Token::Identifier of no such keyword exists. static Token::Value keywordByName(std::string const& _name); static char const* const m_name[NUM_TOKENS]; From d0bb87ae88602f4afc091d9cda1be352258a6df9 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 10 Mar 2016 15:56:25 +0100 Subject: [PATCH 10/14] Documentation for delegatecall. --- docs/contracts.rst | 43 +++++------------------- docs/control-structures.rst | 2 +- docs/introduction-to-smart-contracts.rst | 12 +++---- docs/types.rst | 8 ++--- 4 files changed, 19 insertions(+), 46 deletions(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index b2358af59024..2ab038497224 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -679,7 +679,7 @@ Such contracts cannot be compiled (even if they contain implemented functions al If a contract inherits from an abstract contract and does not implement all non-implemented functions by overriding, it will itself be abstract. -.. index:: ! library, callcode +.. index:: ! library, callcode, delegatecall .. _libraries: @@ -688,7 +688,8 @@ Libraries ************ Libraries are similar to contracts, but their purpose is that they are deployed -only once at a specific address and their code is reused using the `CALLCODE` +only once at a specific address and their code is reused using the `DELEGATECALL` +(`CALLCODE` until homestead) feature of the EVM. This means that if library functions are called, their code is executed in the context of the calling contract, i.e. `this` points to the calling contract and especially the storage from the calling contract can be @@ -755,12 +756,12 @@ reference parameters, can have multiple storage reference parameters and in any position. The calls to `Set.contains`, `Set.insert` and `Set.remove` -are all compiled as calls (`CALLCODE`s) to an external +are all compiled as calls (`DELEGATECALL`s) to an external contract/library. If you use libraries, take care that an -actual external function call is performed, so `msg.sender` -does not point to the original sender anymore but to the the -calling contract and also `msg.value` contains the funds -sent during the call to the library function. +actual external function call is performed. +`msg.sender`, `msg.value` and `this` will retain their values +in this call, though (prior to Homestead, `msg.sender` and +`msg.value` changed, though). As the compiler cannot know where the library will be deployed at, these addresses have to be filled into the @@ -780,34 +781,6 @@ Restrictions for libraries in comparison to contracts: (these might be lifted at a later point) -Common pitfalls for libraries -============================= - -.. index:: msg;sender - -The value of `msg.sender` -------------------------- - -The value for `msg.sender` will be that of the contract which is calling the library function. - -For example, if A calls contract B which internally calls library C, then within the function call of library C, `msg.sender` will be the address of contract B. - -The reason for this is that the expression `LibraryName.functionName()` -performs an external function call using `CALLCODE`, which maps to a real EVM -call just like `otherContract.functionName()` or `this.functionName()`. This -call extends the call depth by one (limited to 1024), stores the caller (the -current contract) as `msg.sender`, and then executes the library contract's -code against the current contracts storage. This execution occurs in a -completely new memory context meaning that memory types will be copied and -cannot be passed by reference. - -Transferring Ether -------------------------- - -It is *in principle* possible to transfer ether using -`LibraryName.functionName.value(x)()`, but as `CALLCODE` is used, the Ether -will just end up at the current contract. - .. index:: ! using for, library .. _using-for: diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 989e14bacf60..9f0f5b5d259e 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -145,7 +145,7 @@ Assigning *to* a state variable always creates an independent copy. On the other Exceptions ========== -There are some cases where exceptions are thrown automatically (see below). You can use the `throw` instruction to throw an exception manually. The effect of an exception is that the currently executing call is stopped and reverted (i.e. all changes to the state and balances are undone) and the exception is also "bubbled up" through Solidity function calls (exceptions are `send` and the low-level functions `call` and `callcode`, those return `false` in case of an exception). +There are some cases where exceptions are thrown automatically (see below). You can use the `throw` instruction to throw an exception manually. The effect of an exception is that the currently executing call is stopped and reverted (i.e. all changes to the state and balances are undone) and the exception is also "bubbled up" through Solidity function calls (exceptions are `send` and the low-level functions `call`, `delegatecall` and `callcode`, those return `false` in case of an exception). Catching exceptions is not yet possible. diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 711e7082654d..22dbbcb70fe7 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -406,15 +406,15 @@ a location in the caller's memory preallocated by the caller. Calls are **limited** to a depth of 1024, which means that for more complex operations, loops should be preferred over recursive calls. -.. index:: callcode, library +.. index:: delegatecall, callcode, library -Callcode and Libraries -====================== +Delegatecall / Callcode and Libraries +===================================== -There exists a special variant of a message call, named **callcode** +There exists a special variant of a message call, named **delegatecall** which is identical to a message call apart from the fact that the code at the target address is executed in the context of the calling -contract. +contract and `msg.sender` and `msg.value` do not change their values. This means that a contract can dynamically load code from a different address at runtime. Storage, current address and balance still @@ -461,4 +461,4 @@ The remaining Ether stored at that address is sent to a designated target and then the storage and code is removed. Note that even if a contract's code does not contain the `SELFDESTRUCT` -opcode, it can still perform that operation using callcode. +opcode, it can still perform that operation using delegatecall or callcode. diff --git a/docs/types.rst b/docs/types.rst index a09a490d937c..13e2a23e87ab 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -54,7 +54,7 @@ Operators: Division always truncates (it just maps to the DIV opcode of the EVM), but it does not truncate if both operators are :ref:`literals` (or literal expressions). -.. index:: address, balance, send, call, callcode +.. index:: address, balance, send, call, callcode, delegatecall Address ------- @@ -82,7 +82,7 @@ and to send Ether (in units of wei) to an address using the `send` function: .. note:: If `x` is a contract address, its code (more specifically: its fallback function, if present) will be executed together with the `send` call (this is a limitation of the EVM and cannot be prevented). If that execution runs out of gas or fails in any way, the Ether transfer will be reverted. In this case, `send` returns `false`. -* `call` and `callcode` +* `call`, `callcode` and `delegatecall` Furthermore, to interface with contracts that do not adhere to the ABI, the function `call` is provided which takes an arbitrary number of arguments of any type. These arguments are padded to 32 bytes and concatenated. One exception is the case where the first argument is encoded to exactly four bytes. In this case, it is not padded to allow the use of function signatures here. @@ -95,9 +95,9 @@ the function `call` is provided which takes an arbitrary number of arguments of `call` returns a boolean indicating whether the invoked function terminated (`true`) or caused an EVM exception (`false`). It is not possible to access the actual data returned (for this we would need to know the encoding and size in advance). -In a similar way, the function `callcode` can be used: The difference is that only the code of the given address is used, all other aspects (storage, balance, ...) are taken from the current contract. The purpose of `callcode` is to use library code which is stored in another contract. The user has to ensure that the layout of storage in both contracts is suitable for callcode to be used. +In a similar way, the function `delegatecall` can be used: The difference is that only the code of the given address is used, all other aspects (storage, balance, ...) are taken from the current contract. The purpose of `delegatecall` is to use library code which is stored in another contract. The user has to ensure that the layout of storage in both contracts is suitable for delegatecall to be used. Prior to homestead, only a limited variant called `callcode` was available that did not provide access to the original `msg.sender` and `msg.value` values. -Both `call` and `callcode` are very low-level functions and should only be used as a *last resort* as they break the type-safety of Solidity. +All three functions `call`, `delegatecall` and `callcode` are very low-level functions and should only be used as a *last resort* as they break the type-safety of Solidity. .. note:: All contracts inherit the members of address, so it is possible to query the balance of the From d0054a8d294901a0107e37d19ab6cb858d3a72f1 Mon Sep 17 00:00:00 2001 From: RJ Catalano Date: Thu, 10 Mar 2016 13:25:14 -0600 Subject: [PATCH 11/14] added keyword type and some tests, changes in lexical cast --- libsolidity/parsing/Token.cpp | 16 ++++++++-------- libsolidity/parsing/Token.h | 7 ++++--- .../SolidityNameAndTypeResolution.cpp | 13 +++++++++++++ 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/libsolidity/parsing/Token.cpp b/libsolidity/parsing/Token.cpp index 4634c510b16b..158ce516d09c 100644 --- a/libsolidity/parsing/Token.cpp +++ b/libsolidity/parsing/Token.cpp @@ -109,26 +109,26 @@ char const Token::m_tokenType[] = { TOKEN_LIST(KT, KK) }; -unsigned Token::extractUnsigned(string::const_iterator _begin, string::const_iterator _end) +int Token::parseSize(string::const_iterator _begin, string::const_iterator _end) { try { - unsigned short m = boost::lexical_cast(boost::make_iterator_range(_begin, _end)); + unsigned int m = boost::lexical_cast(boost::make_iterator_range(_begin, _end)); return m; } - catch(const boost::bad_lexical_cast &) + catch(boost::bad_lexical_cast const&) { - return 0; + return -1; } } -tuple Token::fromIdentifierOrKeyword(string const& _literal) +tuple Token::fromIdentifierOrKeyword(string const& _literal) { auto positionM = find_if(_literal.begin(), _literal.end(), ::isdigit); if (positionM != _literal.end()) { string baseType(_literal.begin(), positionM); auto positionX = find_if_not(positionM, _literal.end(), ::isdigit); - unsigned short m = extractUnsigned(positionM, positionX); + int m = parseSize(positionM, positionX); Token::Value keyword = keywordByName(baseType); if (keyword == Token::Bytes) { @@ -147,8 +147,8 @@ tuple Token::fromIdentifierOrKeywo } else if (keyword == Token::UFixed || keyword == Token::Fixed) { - auto positionN = find_if_not(positionX++, _literal.end(), ::isdigit); - unsigned short n = extractUnsigned(positionX++, positionN); + auto positionN = find_if_not(++positionX, _literal.end(), ::isdigit); + int n = parseSize(++positionX, positionN); if ( 0 < m + n && m + n <= 256 && diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index 64a3447ad1cb..b8ad74718adc 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -231,6 +231,7 @@ namespace solidity K(Type, "type", 0) \ K(TypeOf, "typeof", 0) \ K(Using, "using", 0) \ + T(Timestamp, "timestamp", 0) \ /* Illegal token - not able to scan. */ \ T(Illegal, "ILLEGAL", 0) \ \ @@ -304,11 +305,11 @@ class Token return m_precedence[tok]; } - static std::tuple fromIdentifierOrKeyword(std::string const& _literal); + static std::tuple fromIdentifierOrKeyword(std::string const& _literal); private: - // @returns 0 on error (invalid digit or number too large) - static unsigned extractUnsigned(std::string::const_iterator _begin, std::string::const_iterator _end); + // @returns -1 on error (invalid digit or number too large) + static int parseSize(std::string::const_iterator _begin, std::string::const_iterator _end); // @returns the keyword with name @a _name or Token::Identifier of no such keyword exists. static Token::Value keywordByName(std::string const& _name); static char const* const m_name[NUM_TOKENS]; diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index b2e46b7dd51f..bf43224f7bd5 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -3232,6 +3232,19 @@ BOOST_AUTO_TEST_CASE(int10abc_is_identifier) BOOST_CHECK(success(text)); } +BOOST_AUTO_TEST_CASE(invalid_fixed_types) +{ + char const* text = R"( + contract test { + function f() { + fixed0x7 a = .3; + fixed99999999999999999999999999999999999999x7 b = 9.5; + } + } + )"; + BOOST_CHECK(!success(text)); +} + BOOST_AUTO_TEST_CASE(library_functions_do_not_have_value) { char const* text = R"( From 299fef0c797c045888af5241334247390169bef1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 11 Mar 2016 14:41:00 +0100 Subject: [PATCH 12/14] Do not allow value for delegatecall functions. --- libsolidity/ast/Types.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 0696b9083b1f..4dc1eb131d75 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1623,7 +1623,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con case Location::BareDelegateCall: { MemberList::MemberMap members; - if (m_location != Location::BareDelegateCall) + if (m_location != Location::BareDelegateCall && m_location != Location::DelegateCall) members.push_back(MemberList::Member( "value", make_shared( From 9b00290d74790bb7204449bc96a0ac3660581912 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 11 Mar 2016 17:42:55 +0100 Subject: [PATCH 13/14] Remove timestamp again and some fixes for ufixed parsing. --- libsolidity/parsing/Token.cpp | 32 ++++++++++++++++++-------------- libsolidity/parsing/Token.h | 1 - 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/libsolidity/parsing/Token.cpp b/libsolidity/parsing/Token.cpp index 158ce516d09c..3812a83fdbfb 100644 --- a/libsolidity/parsing/Token.cpp +++ b/libsolidity/parsing/Token.cpp @@ -147,21 +147,25 @@ tuple Token::fromIdentifierOrKeyword(s } else if (keyword == Token::UFixed || keyword == Token::Fixed) { - auto positionN = find_if_not(++positionX, _literal.end(), ::isdigit); - int n = parseSize(++positionX, positionN); if ( - 0 < m + n && - m + n <= 256 && - m % 8 == 0 && - n % 8 == 0 && - positionN == _literal.end() && - *positionX == 'x' - ) - { - if (keyword == Token::UFixed) - return make_tuple(Token::UFixed, m, n); - else - return make_tuple(Token::Fixed, m, n); + positionM < positionX && + positionX < _literal.end() && + *positionX == 'x' && + all_of(positionX + 1, _literal.end(), ::isdigit) + ) { + int n = parseSize(positionX + 1, _literal.end()); + if ( + 0 < m && m < 256 && + 0 < n && n < 256 && + m + n <= 256 && + m % 8 == 0 && + n % 8 == 0 + ) { + if (keyword == Token::UFixed) + return make_tuple(Token::UFixed, m, n); + else + return make_tuple(Token::Fixed, m, n); + } } } return make_tuple(Token::Identifier, 0, 0); diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index b8ad74718adc..31646f8dd4b9 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -231,7 +231,6 @@ namespace solidity K(Type, "type", 0) \ K(TypeOf, "typeof", 0) \ K(Using, "using", 0) \ - T(Timestamp, "timestamp", 0) \ /* Illegal token - not able to scan. */ \ T(Illegal, "ILLEGAL", 0) \ \ From 1bf87c6c2b90406d8a59aa928a9fe43a169e157a Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 11 Mar 2016 17:48:07 +0100 Subject: [PATCH 14/14] Set version to 0.3.0 --- CMakeLists.txt | 2 +- test/libsolidity/SolidityEndToEndTest.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a856b8830dce..a02b779e8123 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.2.2") +set(PROJECT_VERSION "0.3.0") project(solidity VERSION ${PROJECT_VERSION}) # Let's find our dependencies diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 388f5a69500d..56f074154c2c 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -5903,7 +5903,7 @@ BOOST_AUTO_TEST_CASE(version_stamp_for_libraries) bytes runtimeCode = compileAndRun(sourceCode, 0, "lib"); BOOST_CHECK(runtimeCode.size() >= 8); BOOST_CHECK_EQUAL(runtimeCode[0], int(eth::Instruction::PUSH6)); // might change once we switch to 1.x.x - BOOST_CHECK_EQUAL(runtimeCode[1], 2); // might change once we switch away from x.2.x + BOOST_CHECK_EQUAL(runtimeCode[1], 3); // might change once we switch away from x.3.x BOOST_CHECK_EQUAL(runtimeCode[7], int(eth::Instruction::POP)); }