From df42afec64684ef9c2fd0eef69dd1e2ebaf28980 Mon Sep 17 00:00:00 2001 From: wanderer Date: Mon, 17 Nov 2014 15:27:30 -0500 Subject: [PATCH 01/14] added filler.json --- test/recursiveCreateFiller.json | 35 +++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 test/recursiveCreateFiller.json diff --git a/test/recursiveCreateFiller.json b/test/recursiveCreateFiller.json new file mode 100644 index 000000000..0d18fb7a5 --- /dev/null +++ b/test/recursiveCreateFiller.json @@ -0,0 +1,35 @@ +{ + "recursiveCreate": { + "env": { + "previousHash": "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber": "0", + "currentGasLimit": "10000000", + "currentDifficulty": "256", + "currentTimestamp": 1, + "currentCoinbase": "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre": { + "095e7baea6a6c7c4c2dfeb977efac326af552d87": { + "balance": "20000000", + "nonce": 0, + "code": "{(CODECOPY 0 0 32)(CREATE 0 0 32)}", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "1000000000000000000", + "nonce": 0, + "code": "", + "storage": {} + } + }, + "transaction": { + "nonce": "0", + "gasPrice": "1", + "gasLimit": "465224", + "to": "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value": "100000", + "secretKey": "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data": "" + } + } +} From 62c49018edd95587e1358be3c5ec22cad1320407 Mon Sep 17 00:00:00 2001 From: Christoph Jentzsch Date: Mon, 17 Nov 2014 22:23:09 +0100 Subject: [PATCH 02/14] Check validity of signature values r,s,v --- libdevcore/FixedHash.h | 1 + libdevcrypto/Common.cpp | 9 +++++++++ libdevcrypto/Common.h | 10 +++++++++- libethereum/State.cpp | 4 ++++ libethereum/Transaction.cpp | 4 +++- 5 files changed, 26 insertions(+), 2 deletions(-) diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h index 0e387ab8a..6c42aa501 100644 --- a/libdevcore/FixedHash.h +++ b/libdevcore/FixedHash.h @@ -83,6 +83,7 @@ public: bool operator==(FixedHash const& _c) const { return m_data == _c.m_data; } bool operator!=(FixedHash const& _c) const { return m_data != _c.m_data; } bool operator<(FixedHash const& _c) const { return m_data < _c.m_data; } + bool operator>=(FixedHash const& _c) const { return m_data >= _c.m_data; } // The obvious binary operators. FixedHash& operator^=(FixedHash const& _c) { for (unsigned i = 0; i < N; ++i) m_data[i] ^= _c.m_data[i]; return *this; } diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp index fa5a544a1..f4803bd52 100644 --- a/libdevcrypto/Common.cpp +++ b/libdevcrypto/Common.cpp @@ -33,6 +33,15 @@ using namespace dev::crypto; static Secp256k1 s_secp256k1; +bool dev::SignatureStruct::isValid() +{ + if (this->v > 1 || + this->r >= h256("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141") || + this->s >= h256("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f")) + return false; + return true; +} + Public dev::toPublic(Secret const& _secret) { Public p; diff --git a/libdevcrypto/Common.h b/libdevcrypto/Common.h index 2ec332d8d..96631fcf9 100644 --- a/libdevcrypto/Common.h +++ b/libdevcrypto/Common.h @@ -43,7 +43,15 @@ using Public = h512; /// @NOTE This is not endian-specific; it's just a bunch of bytes. using Signature = h520; -struct SignatureStruct { h256 r; h256 s; byte v; }; +struct SignatureStruct +{ + /// @returns true if r,s,v values are valid, otherwise false + bool isValid(); + + h256 r; + h256 s; + byte v; +}; /// An Ethereum address: 20 bytes. /// @NOTE This is not endian-specific; it's just a bunch of bytes. diff --git a/libethereum/State.cpp b/libethereum/State.cpp index ee93fdc55..f657c2699 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -55,6 +55,10 @@ void ecrecoverCode(bytesConstRef _in, bytesRef _out) memcpy(&in, _in.data(), min(_in.size(), sizeof(in))); + SignatureStruct sig{in.r, in.s, (byte)((int)(u256)in.v - 27)}; + if (!sig.isValid() || in.v > 28) + return; + byte pubkey[65]; int pubkeylen = 65; secp256k1_start(); diff --git a/libethereum/Transaction.cpp b/libethereum/Transaction.cpp index d94a31425..1edfe3927 100644 --- a/libethereum/Transaction.cpp +++ b/libethereum/Transaction.cpp @@ -82,7 +82,9 @@ Address Transaction::sender() const void Transaction::sign(Secret _priv) { auto sig = dev::sign(_priv, sha3(WithoutSignature)); - m_vrs = *(SignatureStruct const*)&sig; + SignatureStruct sigStruct = *(SignatureStruct const*)&sig; + if (sigStruct.isValid()) + m_vrs = sigStruct; } void Transaction::streamRLP(RLPStream& _s, IncludeSignature _sig) const From 0c101d89f81a890372d96521289a8b921e0b2f13 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 18 Nov 2014 18:50:40 +0100 Subject: [PATCH 03/14] solidity scanner takes triple slash doc comments into account - Conditionally scanning for the documentation comments and gettings their contents. - Adding tests for this functionality of the scanner --- libsolidity/Scanner.cpp | 36 +++++++++++++++++++++++++------- libsolidity/Scanner.h | 19 ++++++++++++----- libsolidity/Token.h | 1 + test/solidityScanner.cpp | 44 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 12 deletions(-) diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index b13e52d7e..382d07a99 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -102,13 +102,13 @@ int HexValue(char c) } } // end anonymous namespace -void Scanner::reset(CharStream const& _source) +void Scanner::reset(CharStream const& _source, bool _skipDocumentationComments) { m_source = _source; m_char = m_source.get(); skipWhitespace(); - scanToken(); - next(); + scanToken(_skipDocumentationComments); + next(_skipDocumentationComments); } @@ -134,10 +134,10 @@ bool Scanner::scanHexByte(char& o_scannedByte) // Ensure that tokens can be stored in a byte. BOOST_STATIC_ASSERT(Token::NUM_TOKENS <= 0x100); -Token::Value Scanner::next() +Token::Value Scanner::next(bool _skipDocumentationComments) { m_current_token = m_next_token; - scanToken(); + scanToken(_skipDocumentationComments); return m_current_token.token; } @@ -171,6 +171,21 @@ Token::Value Scanner::skipSingleLineComment() return Token::WHITESPACE; } +// For the moment this function simply consumes a single line triple slash doc comment +Token::Value Scanner::scanDocumentationComment() +{ + LiteralScope literal(this); + advance(); //consume the last '/' + while (!isSourcePastEndOfInput() && !IsLineTerminator(m_char)) + { + char c = m_char; + advance(); + addLiteralChar(c); + } + literal.Complete(); + return Token::COMMENT_LITERAL; +} + Token::Value Scanner::skipMultiLineComment() { if (asserts(m_char == '*')) @@ -194,7 +209,7 @@ Token::Value Scanner::skipMultiLineComment() return Token::ILLEGAL; } -void Scanner::scanToken() +void Scanner::scanToken(bool _skipDocumentationComments) { m_next_token.literal.clear(); Token::Value token; @@ -297,7 +312,14 @@ void Scanner::scanToken() // / // /* /= advance(); if (m_char == '/') - token = skipSingleLineComment(); + { + if (!advance()) /* double slash comment directly before EOS */ + token = Token::WHITESPACE; + else if (!_skipDocumentationComments) + token = scanDocumentationComment(); + else + token = skipSingleLineComment(); + } else if (m_char == '*') token = skipMultiLineComment(); else if (m_char == '=') diff --git a/libsolidity/Scanner.h b/libsolidity/Scanner.h index 997365f3c..fd48d5698 100644 --- a/libsolidity/Scanner.h +++ b/libsolidity/Scanner.h @@ -111,19 +111,27 @@ public: }; Scanner() { reset(CharStream()); } - explicit Scanner(CharStream const& _source) { reset(_source); } + explicit Scanner(CharStream const& _source, bool _skipDocumentationComments = true) + { + reset(_source, _skipDocumentationComments); + } /// Resets the scanner as if newly constructed with _input as input. - void reset(CharStream const& _source); + void reset(CharStream const& _source, bool _skipDocumentationComments = true); /// Returns the next token and advances input. - Token::Value next(); + Token::Value next(bool _skipDocumentationComments = true); ///@{ ///@name Information about the current token /// Returns the current token - Token::Value getCurrentToken() { return m_current_token.token; } + Token::Value getCurrentToken(bool _skipDocumentationComments = true) + { + if (!_skipDocumentationComments) + next(_skipDocumentationComments); + return m_current_token.token; + } Location getCurrentLocation() const { return m_current_token.location; } std::string const& getCurrentLiteral() const { return m_current_token.literal; } ///@} @@ -172,7 +180,7 @@ private: bool scanHexByte(char& o_scannedByte); /// Scans a single JavaScript token. - void scanToken(); + void scanToken(bool _skipDocumentationComments = true); /// Skips all whitespace and @returns true if something was skipped. bool skipWhitespace(); @@ -184,6 +192,7 @@ private: Token::Value scanIdentifierOrKeyword(); Token::Value scanString(); + Token::Value scanDocumentationComment(); /// Scans an escape-sequence which is part of a string and adds the /// decoded character to the current literal. Returns true if a pattern diff --git a/libsolidity/Token.h b/libsolidity/Token.h index 67971c3d0..39a84bcc2 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -281,6 +281,7 @@ namespace solidity K(FALSE_LITERAL, "false", 0) \ T(NUMBER, NULL, 0) \ T(STRING_LITERAL, NULL, 0) \ + T(COMMENT_LITERAL, NULL, 0) \ \ /* Identifiers (not keywords or future reserved words). */ \ T(IDENTIFIER, NULL, 0) \ diff --git a/test/solidityScanner.cpp b/test/solidityScanner.cpp index d714699a0..28f52d3af 100644 --- a/test/solidityScanner.cpp +++ b/test/solidityScanner.cpp @@ -153,6 +153,50 @@ BOOST_AUTO_TEST_CASE(ambiguities) BOOST_CHECK_EQUAL(scanner.next(), Token::SHL); } +BOOST_AUTO_TEST_CASE(documentation_comments_parsed_begin) +{ + Scanner scanner(CharStream("/// Send $(value / 1000) chocolates to the user"), false); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::COMMENT_LITERAL); + BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), " Send $(value / 1000) chocolates to the user"); +} + +BOOST_AUTO_TEST_CASE(documentation_comments_skipped_begin) +{ + Scanner scanner(CharStream("/// Send $(value / 1000) chocolates to the user")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(documentation_comments_parsed) +{ + Scanner scanner(CharStream("some other tokens /// Send $(value / 1000) chocolates to the user")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.next(false), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.next(false), Token::COMMENT_LITERAL); + BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), " Send $(value / 1000) chocolates to the user"); +} + +BOOST_AUTO_TEST_CASE(documentation_comments_skipped) +{ + Scanner scanner(CharStream("some other tokens /// Send $(value / 1000) chocolates to the user")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(comment_before_eos) +{ + Scanner scanner(CharStream("//")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(documentation_comment_before_eos) +{ + Scanner scanner(CharStream("///"), false); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::COMMENT_LITERAL); + BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), ""); +} BOOST_AUTO_TEST_SUITE_END() From 0788e326ced4e1deeb0514133bcc67dd7bb71c25 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 19 Nov 2014 02:02:30 +0100 Subject: [PATCH 04/14] fixing typo and alignment --- libsolidity/Scanner.h | 2 +- libsolidity/Token.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libsolidity/Scanner.h b/libsolidity/Scanner.h index fd48d5698..402f1aea8 100644 --- a/libsolidity/Scanner.h +++ b/libsolidity/Scanner.h @@ -179,7 +179,7 @@ private: bool scanHexByte(char& o_scannedByte); - /// Scans a single JavaScript token. + /// Scans a single Solidity token. void scanToken(bool _skipDocumentationComments = true); /// Skips all whitespace and @returns true if something was skipped. diff --git a/libsolidity/Token.h b/libsolidity/Token.h index 39a84bcc2..524487521 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -281,7 +281,7 @@ namespace solidity K(FALSE_LITERAL, "false", 0) \ T(NUMBER, NULL, 0) \ T(STRING_LITERAL, NULL, 0) \ - T(COMMENT_LITERAL, NULL, 0) \ + T(COMMENT_LITERAL, NULL, 0) \ \ /* Identifiers (not keywords or future reserved words). */ \ T(IDENTIFIER, NULL, 0) \ From 732e5c2b0fed646e0d7e7a03af2da8eb0238f7f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 19 Nov 2014 13:13:19 +0100 Subject: [PATCH 05/14] In VM tests, check only if an exception occurred if an exception expected (no post state and output checking) --- test/TestHelper.cpp | 1 + test/vm.cpp | 77 ++++++++++++++++++++++++++------------------- 2 files changed, 45 insertions(+), 33 deletions(-) diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index 1b13f9e82..114399a49 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -374,6 +374,7 @@ void executeTests(const string& _name, const string& _testPathAppendix, std::fun { BOOST_ERROR("Failed test with Exception: " << _e.what()); } + break; } } diff --git a/test/vm.cpp b/test/vm.cpp index cacbf94cc..bdbe8155d 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -300,6 +300,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) VM vm(fev.gas); u256 gas; + bool vmExceptionOccured = false; try { output = vm.go(fev, fev.simpleTrace()).toVector(); @@ -308,7 +309,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) catch (VMException const& _e) { cnote << "VM did throw an exception: " << diagnostic_information(_e); - gas = 0; + vmExceptionOccured = true; } catch (Exception const& _e) { @@ -342,48 +343,58 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) { o["env"] = mValue(fev.exportEnv()); o["exec"] = mValue(fev.exportExec()); - o["post"] = mValue(fev.exportState()); - o["callcreates"] = fev.exportCallCreates(); - o["out"] = "0x" + toHex(output); - fev.push(o, "gas", gas); + if (!vmExceptionOccured) + { + o["post"] = mValue(fev.exportState()); + o["callcreates"] = fev.exportCallCreates(); + o["out"] = "0x" + toHex(output); + fev.push(o, "gas", gas); + } } else { - BOOST_REQUIRE(o.count("post") > 0); - BOOST_REQUIRE(o.count("callcreates") > 0); - BOOST_REQUIRE(o.count("out") > 0); - BOOST_REQUIRE(o.count("gas") > 0); + if (o.count("post") > 0) // No exceptions expected + { + BOOST_CHECK(!vmExceptionOccured); - dev::test::FakeExtVM test; - test.importState(o["post"].get_obj()); - test.importCallCreates(o["callcreates"].get_array()); + BOOST_REQUIRE(o.count("post") > 0); + BOOST_REQUIRE(o.count("callcreates") > 0); + BOOST_REQUIRE(o.count("out") > 0); + BOOST_REQUIRE(o.count("gas") > 0); - checkOutput(output, o); + dev::test::FakeExtVM test; + test.importState(o["post"].get_obj()); + test.importCallCreates(o["callcreates"].get_array()); - BOOST_CHECK_EQUAL(toInt(o["gas"]), gas); + checkOutput(output, o); - auto& expectedAddrs = test.addresses; - auto& resultAddrs = fev.addresses; - for (auto&& expectedPair : expectedAddrs) - { - auto& expectedAddr = expectedPair.first; - auto resultAddrIt = resultAddrs.find(expectedAddr); - if (resultAddrIt == resultAddrs.end()) - BOOST_ERROR("Missing expected address " << expectedAddr); - else - { - auto& expectedState = expectedPair.second; - auto& resultState = resultAddrIt->second; - BOOST_CHECK_MESSAGE(std::get<0>(expectedState) == std::get<0>(resultState), expectedAddr << ": incorrect balance " << std::get<0>(resultState) << ", expected " << std::get<0>(expectedState)); - BOOST_CHECK_MESSAGE(std::get<1>(expectedState) == std::get<1>(resultState), expectedAddr << ": incorrect txCount " << std::get<1>(resultState) << ", expected " << std::get<1>(expectedState)); - BOOST_CHECK_MESSAGE(std::get<3>(expectedState) == std::get<3>(resultState), expectedAddr << ": incorrect code"); + BOOST_CHECK_EQUAL(toInt(o["gas"]), gas); - checkStorage(std::get<2>(expectedState), std::get<2>(resultState), expectedAddr); + auto& expectedAddrs = test.addresses; + auto& resultAddrs = fev.addresses; + for (auto&& expectedPair : expectedAddrs) + { + auto& expectedAddr = expectedPair.first; + auto resultAddrIt = resultAddrs.find(expectedAddr); + if (resultAddrIt == resultAddrs.end()) + BOOST_ERROR("Missing expected address " << expectedAddr); + else + { + auto& expectedState = expectedPair.second; + auto& resultState = resultAddrIt->second; + BOOST_CHECK_MESSAGE(std::get<0>(expectedState) == std::get<0>(resultState), expectedAddr << ": incorrect balance " << std::get<0>(resultState) << ", expected " << std::get<0>(expectedState)); + BOOST_CHECK_MESSAGE(std::get<1>(expectedState) == std::get<1>(resultState), expectedAddr << ": incorrect txCount " << std::get<1>(resultState) << ", expected " << std::get<1>(expectedState)); + BOOST_CHECK_MESSAGE(std::get<3>(expectedState) == std::get<3>(resultState), expectedAddr << ": incorrect code"); + + checkStorage(std::get<2>(expectedState), std::get<2>(resultState), expectedAddr); + } } - } - checkAddresses, bytes> > >(test.addresses, fev.addresses); - BOOST_CHECK(test.callcreates == fev.callcreates); + checkAddresses, bytes> > >(test.addresses, fev.addresses); + BOOST_CHECK(test.callcreates == fev.callcreates); + } + else // Exception expected + BOOST_CHECK(vmExceptionOccured); } } } From c7972ba8509ffe7b21365555ce500ecbceef3197 Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 19 Nov 2014 10:24:22 +0100 Subject: [PATCH 06/14] Special handling for constructor. --- libsolidity/Compiler.cpp | 93 ++++++++++++++++++++++------------- libsolidity/Compiler.h | 13 +++-- test/solidityEndToEndTest.cpp | 22 +++++++++ 3 files changed, 90 insertions(+), 38 deletions(-) diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index eed886783..a3865bc30 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -43,42 +43,59 @@ void Compiler::compileContract(ContractDefinition& _contract) { m_context = CompilerContext(); // clear it just in case - //@todo constructor - for (ASTPointer const& function: _contract.getDefinedFunctions()) - m_context.addFunction(*function); - //@todo sort them? - for (ASTPointer const& variable: _contract.getStateVariables()) - m_context.addStateVariable(*variable); + if (function->getName() != _contract.getName()) // don't add the constructor here + m_context.addFunction(*function); + registerStateVariables(_contract); - appendFunctionSelector(_contract.getDefinedFunctions()); + appendFunctionSelector(_contract); for (ASTPointer const& function: _contract.getDefinedFunctions()) - function->accept(*this); + if (function->getName() != _contract.getName()) // don't add the constructor here + function->accept(*this); - packIntoContractCreator(); + packIntoContractCreator(_contract); } -void Compiler::packIntoContractCreator() +void Compiler::packIntoContractCreator(ContractDefinition const& _contract) { - CompilerContext creatorContext; - eth::AssemblyItem sub = creatorContext.addSubroutine(m_context.getAssembly()); + CompilerContext runtimeContext; + swap(m_context, runtimeContext); + + registerStateVariables(_contract); + + FunctionDefinition* constructor = nullptr; + for (ASTPointer const& function: _contract.getDefinedFunctions()) + if (function->getName() == _contract.getName()) + { + constructor = function.get(); + break; + } + if (constructor) + { + eth::AssemblyItem returnTag = m_context.pushNewTag(); + m_context.addFunction(*constructor); // note that it cannot be called due to syntactic reasons + //@todo copy constructor arguments from calldata to memory prior to this + //@todo calling other functions inside the constructor should either trigger a parse error + //or we should copy them here (register them above and call "accept") - detecting which + // functions are referenced / called needs to be done in a recursive way. + appendCalldataUnpacker(*constructor, true); + m_context.appendJumpTo(m_context.getFunctionEntryLabel(*constructor)); + constructor->accept(*this); + m_context << returnTag; + } + + eth::AssemblyItem sub = m_context.addSubroutine(runtimeContext.getAssembly()); // stack contains sub size - creatorContext << eth::Instruction::DUP1 << sub << u256(0) << eth::Instruction::CODECOPY; - creatorContext << u256(0) << eth::Instruction::RETURN; - swap(m_context, creatorContext); + m_context << eth::Instruction::DUP1 << sub << u256(0) << eth::Instruction::CODECOPY; + m_context << u256(0) << eth::Instruction::RETURN; } -void Compiler::appendFunctionSelector(vector> const& _functions) +void Compiler::appendFunctionSelector(ContractDefinition const& _contract) { - // sort all public functions and store them together with a tag for their argument decoding section - map> publicFunctions; - for (ASTPointer const& f: _functions) - if (f->isPublic()) - publicFunctions.insert(make_pair(f->getName(), make_pair(f.get(), m_context.newTag()))); + vector interfaceFunctions = _contract.getInterfaceFunctions(); + vector callDataUnpackerEntryPoints; - //@todo remove constructor - - if (publicFunctions.size() > 255) + if (interfaceFunctions.size() > 255) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("More than 255 public functions for contract.")); // retrieve the first byte of the call data, which determines the called function @@ -90,21 +107,20 @@ void Compiler::appendFunctionSelector(vector> con << eth::dupInstruction(2); // stack here: 1 0 0, stack top will be counted up until it matches funid - for (pair> const& f: publicFunctions) + for (unsigned funid = 0; funid < interfaceFunctions.size(); ++funid) { - eth::AssemblyItem const& callDataUnpackerEntry = f.second.second; + callDataUnpackerEntryPoints.push_back(m_context.newTag()); m_context << eth::dupInstruction(2) << eth::dupInstruction(2) << eth::Instruction::EQ; - m_context.appendConditionalJumpTo(callDataUnpackerEntry); + m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.back()); m_context << eth::dupInstruction(4) << eth::Instruction::ADD; //@todo avoid the last ADD (or remove it in the optimizer) } m_context << eth::Instruction::STOP; // function not found - for (pair> const& f: publicFunctions) + for (unsigned funid = 0; funid < interfaceFunctions.size(); ++funid) { - FunctionDefinition const& function = *f.second.first; - eth::AssemblyItem const& callDataUnpackerEntry = f.second.second; - m_context << callDataUnpackerEntry; + FunctionDefinition const& function = *interfaceFunctions[funid]; + m_context << callDataUnpackerEntryPoints[funid]; eth::AssemblyItem returnTag = m_context.pushNewTag(); appendCalldataUnpacker(function); m_context.appendJumpTo(m_context.getFunctionEntryLabel(function)); @@ -113,10 +129,11 @@ void Compiler::appendFunctionSelector(vector> con } } -void Compiler::appendCalldataUnpacker(FunctionDefinition const& _function) +unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, bool _fromMemory) { // We do not check the calldata size, everything is zero-padded. unsigned dataOffset = 1; + eth::Instruction load = _fromMemory ? eth::Instruction::MLOAD : eth::Instruction::CALLDATALOAD; //@todo this can be done more efficiently, saving some CALLDATALOAD calls for (ASTPointer const& var: _function.getParameters()) @@ -127,12 +144,13 @@ void Compiler::appendCalldataUnpacker(FunctionDefinition const& _function) << errinfo_sourceLocation(var->getLocation()) << errinfo_comment("Type " + var->getType()->toString() + " not yet supported.")); if (numBytes == 32) - m_context << u256(dataOffset) << eth::Instruction::CALLDATALOAD; + m_context << u256(dataOffset) << load; else m_context << (u256(1) << ((32 - numBytes) * 8)) << u256(dataOffset) - << eth::Instruction::CALLDATALOAD << eth::Instruction::DIV; + << load << eth::Instruction::DIV; dataOffset += numBytes; } + return dataOffset; } void Compiler::appendReturnValuePacker(FunctionDefinition const& _function) @@ -158,6 +176,13 @@ void Compiler::appendReturnValuePacker(FunctionDefinition const& _function) m_context << u256(dataOffset) << u256(0) << eth::Instruction::RETURN; } +void Compiler::registerStateVariables(ContractDefinition const& _contract) +{ + //@todo sort them? + for (ASTPointer const& variable: _contract.getStateVariables()) + m_context.addStateVariable(*variable); +} + bool Compiler::visit(FunctionDefinition& _function) { //@todo to simplify this, the calling convention could by changed such that diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h index d931f5359..3887d3b5b 100644 --- a/libsolidity/Compiler.h +++ b/libsolidity/Compiler.h @@ -40,12 +40,17 @@ public: static bytes compile(ContractDefinition& _contract, bool _optimize); private: - /// Creates a new compiler context / assembly and packs the current code into the data part. - void packIntoContractCreator(); - void appendFunctionSelector(std::vector > const& _functions); - void appendCalldataUnpacker(FunctionDefinition const& _function); + /// Creates a new compiler context / assembly, packs the current code into the data part and + /// adds the constructor code. + void packIntoContractCreator(ContractDefinition const& _contract); + void appendFunctionSelector(ContractDefinition const& _contract); + /// Creates code that unpacks the arguments for the given function, from memory if + /// @a _fromMemory is true, otherwise from call data. @returns the size of the data in bytes. + unsigned appendCalldataUnpacker(FunctionDefinition const& _function, bool _fromMemory = false); void appendReturnValuePacker(FunctionDefinition const& _function); + void registerStateVariables(ContractDefinition const& _contract); + virtual bool visit(FunctionDefinition& _function) override; virtual bool visit(IfStatement& _ifStatement) override; virtual bool visit(WhileStatement& _whileStatement) override; diff --git a/test/solidityEndToEndTest.cpp b/test/solidityEndToEndTest.cpp index 617cbabc9..4e68103ac 100644 --- a/test/solidityEndToEndTest.cpp +++ b/test/solidityEndToEndTest.cpp @@ -700,6 +700,28 @@ BOOST_AUTO_TEST_CASE(structs) BOOST_CHECK(callContractFunction(0) == bytes({0x01})); } +BOOST_AUTO_TEST_CASE(constructor) +{ + char const* sourceCode = "contract test {\n" + " mapping(uint => uint) data;\n" + " function test() {\n" + " data[7] = 8;\n" + " }\n" + " function get(uint key) returns (uint value) {\n" + " return data[key];" + " }\n" + "}\n"; + compileAndRun(sourceCode); + map data; + data[7] = 8; + auto get = [&](u256 const& _x) -> u256 + { + return data[_x]; + }; + testSolidityAgainstCpp(0, get, u256(6)); + testSolidityAgainstCpp(0, get, u256(7)); +} + BOOST_AUTO_TEST_SUITE_END() } From e866cedc40b0e61c0201aa5b3b48fd71b8e0fef7 Mon Sep 17 00:00:00 2001 From: Christoph Jentzsch Date: Wed, 19 Nov 2014 15:20:43 +0100 Subject: [PATCH 07/14] Fix memNeed overflow --- libevm/VM.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libevm/VM.h b/libevm/VM.h index 487c8cd1a..16df0af64 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -92,7 +92,7 @@ private: // INLINE: template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc const& _onOp, uint64_t _steps) { - auto memNeed = [](dev::u256 _offset, dev::u256 _size) { return _size ? _offset + _size : 0; }; + auto memNeed = [](dev::u256 _offset, dev::u256 _size) { return _size ? (bigint)_offset + _size : (bigint)0; }; if (m_jumpDests.empty()) { From be1d8881af0271644395f94f7988f627edf4c960 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 19 Nov 2014 16:21:42 +0100 Subject: [PATCH 08/14] documentation comments are now always skipped but saved as special tokens at the Scanner --- libsolidity/Scanner.cpp | 31 ++++++++++++++++++++++--------- libsolidity/Scanner.h | 34 +++++++++++++++++++++------------- test/solidityScanner.cpp | 25 +++++-------------------- 3 files changed, 48 insertions(+), 42 deletions(-) diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index 382d07a99..cddc687a9 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -102,13 +102,14 @@ int HexValue(char c) } } // end anonymous namespace -void Scanner::reset(CharStream const& _source, bool _skipDocumentationComments) +void Scanner::reset(CharStream const& _source) { + bool found_doc_comment; m_source = _source; m_char = m_source.get(); skipWhitespace(); - scanToken(_skipDocumentationComments); - next(_skipDocumentationComments); + found_doc_comment = scanToken(); + next(found_doc_comment); } @@ -134,10 +135,11 @@ bool Scanner::scanHexByte(char& o_scannedByte) // Ensure that tokens can be stored in a byte. BOOST_STATIC_ASSERT(Token::NUM_TOKENS <= 0x100); -Token::Value Scanner::next(bool _skipDocumentationComments) +Token::Value Scanner::next(bool _change_skipped_comment) { m_current_token = m_next_token; - scanToken(_skipDocumentationComments); + if (scanToken() || _change_skipped_comment) + m_skipped_comment = m_next_skipped_comment; return m_current_token.token; } @@ -180,7 +182,7 @@ Token::Value Scanner::scanDocumentationComment() { char c = m_char; advance(); - addLiteralChar(c); + addCommentLiteralChar(c); } literal.Complete(); return Token::COMMENT_LITERAL; @@ -209,8 +211,9 @@ Token::Value Scanner::skipMultiLineComment() return Token::ILLEGAL; } -void Scanner::scanToken(bool _skipDocumentationComments) +bool Scanner::scanToken() { + bool found_doc_comment = false; m_next_token.literal.clear(); Token::Value token; do @@ -315,8 +318,16 @@ void Scanner::scanToken(bool _skipDocumentationComments) { if (!advance()) /* double slash comment directly before EOS */ token = Token::WHITESPACE; - else if (!_skipDocumentationComments) - token = scanDocumentationComment(); + else if (m_char == '/') + { + Token::Value comment; + m_next_skipped_comment.location.start = getSourcePos(); + comment = scanDocumentationComment(); + m_next_skipped_comment.location.end = getSourcePos(); + m_next_skipped_comment.token = comment; + token = Token::WHITESPACE; + found_doc_comment = true; + } else token = skipSingleLineComment(); } @@ -411,6 +422,8 @@ void Scanner::scanToken(bool _skipDocumentationComments) while (token == Token::WHITESPACE); m_next_token.location.end = getSourcePos(); m_next_token.token = token; + + return found_doc_comment; } bool Scanner::scanEscape() diff --git a/libsolidity/Scanner.h b/libsolidity/Scanner.h index 402f1aea8..23007fe12 100644 --- a/libsolidity/Scanner.h +++ b/libsolidity/Scanner.h @@ -111,31 +111,34 @@ public: }; Scanner() { reset(CharStream()); } - explicit Scanner(CharStream const& _source, bool _skipDocumentationComments = true) - { - reset(_source, _skipDocumentationComments); - } + explicit Scanner(CharStream const& _source) { reset(_source); } /// Resets the scanner as if newly constructed with _input as input. - void reset(CharStream const& _source, bool _skipDocumentationComments = true); + void reset(CharStream const& _source); - /// Returns the next token and advances input. - Token::Value next(bool _skipDocumentationComments = true); + /// Returns the next token and advances input. If called from reset() + /// and ScanToken() found a documentation token then next should be called + /// with _change_skipped_comment=true + Token::Value next(bool _change_skipped_comment = false); ///@{ ///@name Information about the current token /// Returns the current token - Token::Value getCurrentToken(bool _skipDocumentationComments = true) + Token::Value getCurrentToken() { - if (!_skipDocumentationComments) - next(_skipDocumentationComments); return m_current_token.token; } Location getCurrentLocation() const { return m_current_token.location; } std::string const& getCurrentLiteral() const { return m_current_token.literal; } ///@} + ///@{ + ///@name Information about the current comment token + Location getCurrentCommentLocation() const { return m_skipped_comment.location; } + std::string const& getCurrentCommentLiteral() const { return m_skipped_comment.literal; } + ///@} + ///@{ ///@name Information about the next token @@ -154,7 +157,7 @@ public: ///@} private: - // Used for the current and look-ahead token. + // Used for the current and look-ahead token and comments struct TokenDesc { Token::Value token; @@ -166,6 +169,7 @@ private: ///@name Literal buffer support inline void startNewLiteral() { m_next_token.literal.clear(); } inline void addLiteralChar(char c) { m_next_token.literal.push_back(c); } + inline void addCommentLiteralChar(char c) { m_next_skipped_comment.literal.push_back(c); } inline void dropLiteral() { m_next_token.literal.clear(); } inline void addLiteralCharAndAdvance() { addLiteralChar(m_char); advance(); } ///@} @@ -179,8 +183,9 @@ private: bool scanHexByte(char& o_scannedByte); - /// Scans a single Solidity token. - void scanToken(bool _skipDocumentationComments = true); + /// Scans a single Solidity token. Returns true if the scanned token was + /// a skipped documentation comment. False in all other cases. + bool scanToken(); /// Skips all whitespace and @returns true if something was skipped. bool skipWhitespace(); @@ -203,6 +208,9 @@ private: int getSourcePos() { return m_source.getPos(); } bool isSourcePastEndOfInput() { return m_source.isPastEndOfInput(); } + TokenDesc m_skipped_comment; // desc for current skipped comment + TokenDesc m_next_skipped_comment; // desc for next skiped comment + TokenDesc m_current_token; // desc for current token (as returned by Next()) TokenDesc m_next_token; // desc for next token (one token look-ahead) diff --git a/test/solidityScanner.cpp b/test/solidityScanner.cpp index 28f52d3af..1a2299f37 100644 --- a/test/solidityScanner.cpp +++ b/test/solidityScanner.cpp @@ -154,35 +154,20 @@ BOOST_AUTO_TEST_CASE(ambiguities) } BOOST_AUTO_TEST_CASE(documentation_comments_parsed_begin) -{ - Scanner scanner(CharStream("/// Send $(value / 1000) chocolates to the user"), false); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::COMMENT_LITERAL); - BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), " Send $(value / 1000) chocolates to the user"); -} - -BOOST_AUTO_TEST_CASE(documentation_comments_skipped_begin) { Scanner scanner(CharStream("/// Send $(value / 1000) chocolates to the user")); BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::EOS); + BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), " Send $(value / 1000) chocolates to the user"); } BOOST_AUTO_TEST_CASE(documentation_comments_parsed) -{ - Scanner scanner(CharStream("some other tokens /// Send $(value / 1000) chocolates to the user")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER); - BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); - BOOST_CHECK_EQUAL(scanner.next(false), Token::IDENTIFIER); - BOOST_CHECK_EQUAL(scanner.next(false), Token::COMMENT_LITERAL); - BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), " Send $(value / 1000) chocolates to the user"); -} - -BOOST_AUTO_TEST_CASE(documentation_comments_skipped) { Scanner scanner(CharStream("some other tokens /// Send $(value / 1000) chocolates to the user")); BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER); BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), " Send $(value / 1000) chocolates to the user"); } BOOST_AUTO_TEST_CASE(comment_before_eos) @@ -193,9 +178,9 @@ BOOST_AUTO_TEST_CASE(comment_before_eos) BOOST_AUTO_TEST_CASE(documentation_comment_before_eos) { - Scanner scanner(CharStream("///"), false); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::COMMENT_LITERAL); - BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), ""); + Scanner scanner(CharStream("///")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::EOS); + BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), ""); } BOOST_AUTO_TEST_SUITE_END() From 03f8208b6b02ded3f506f559ebe6d5854cbb040a Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 20 Nov 2014 22:08:16 +0100 Subject: [PATCH 09/14] styling fixes --- libsolidity/Scanner.cpp | 22 +++++++++++----------- libsolidity/Scanner.h | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index cddc687a9..934b30dcf 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -104,12 +104,12 @@ int HexValue(char c) void Scanner::reset(CharStream const& _source) { - bool found_doc_comment; + bool foundDocComment; m_source = _source; m_char = m_source.get(); skipWhitespace(); - found_doc_comment = scanToken(); - next(found_doc_comment); + foundDocComment = scanToken(); + next(foundDocComment); } @@ -135,10 +135,10 @@ bool Scanner::scanHexByte(char& o_scannedByte) // Ensure that tokens can be stored in a byte. BOOST_STATIC_ASSERT(Token::NUM_TOKENS <= 0x100); -Token::Value Scanner::next(bool _change_skipped_comment) +Token::Value Scanner::next(bool _changeSkippedComment) { m_current_token = m_next_token; - if (scanToken() || _change_skipped_comment) + if (scanToken() || _changeSkippedComment) m_skipped_comment = m_next_skipped_comment; return m_current_token.token; } @@ -173,7 +173,7 @@ Token::Value Scanner::skipSingleLineComment() return Token::WHITESPACE; } -// For the moment this function simply consumes a single line triple slash doc comment +/// For the moment this function simply consumes a single line triple slash doc comment Token::Value Scanner::scanDocumentationComment() { LiteralScope literal(this); @@ -213,7 +213,7 @@ Token::Value Scanner::skipMultiLineComment() bool Scanner::scanToken() { - bool found_doc_comment = false; + bool foundDocComment = false; m_next_token.literal.clear(); Token::Value token; do @@ -326,7 +326,7 @@ bool Scanner::scanToken() m_next_skipped_comment.location.end = getSourcePos(); m_next_skipped_comment.token = comment; token = Token::WHITESPACE; - found_doc_comment = true; + foundDocComment = true; } else token = skipSingleLineComment(); @@ -423,7 +423,7 @@ bool Scanner::scanToken() m_next_token.location.end = getSourcePos(); m_next_token.token = token; - return found_doc_comment; + return foundDocComment; } bool Scanner::scanEscape() @@ -567,9 +567,9 @@ Token::Value Scanner::scanNumber(char _charSeen) // ---------------------------------------------------------------------------- // Keyword Matcher -#define KEYWORDS(KEYWORD_GROUP, KEYWORD) \ +#define KEYWORDS(KEYWORD_GROUP, KEYWORD) \ KEYWORD_GROUP('a') \ - KEYWORD("address", Token::ADDRESS) \ + KEYWORD("address", Token::ADDRESS) \ KEYWORD_GROUP('b') \ KEYWORD("break", Token::BREAK) \ KEYWORD("bool", Token::BOOL) \ diff --git a/libsolidity/Scanner.h b/libsolidity/Scanner.h index 23007fe12..0a6778ecd 100644 --- a/libsolidity/Scanner.h +++ b/libsolidity/Scanner.h @@ -119,7 +119,7 @@ public: /// Returns the next token and advances input. If called from reset() /// and ScanToken() found a documentation token then next should be called /// with _change_skipped_comment=true - Token::Value next(bool _change_skipped_comment = false); + Token::Value next(bool _changeSkippedComment = false); ///@{ ///@name Information about the current token @@ -157,7 +157,7 @@ public: ///@} private: - // Used for the current and look-ahead token and comments + /// Used for the current and look-ahead token and comments struct TokenDesc { Token::Value token; From 06e2c08af73628c7a614b50301904a7d681d8a75 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 20 Nov 2014 23:18:05 +0100 Subject: [PATCH 10/14] extra comments scanning test --- libsolidity/Scanner.h | 2 +- test/solidityScanner.cpp | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/libsolidity/Scanner.h b/libsolidity/Scanner.h index 0a6778ecd..94c67840a 100644 --- a/libsolidity/Scanner.h +++ b/libsolidity/Scanner.h @@ -118,7 +118,7 @@ public: /// Returns the next token and advances input. If called from reset() /// and ScanToken() found a documentation token then next should be called - /// with _change_skipped_comment=true + /// with _changeSkippedComment=true Token::Value next(bool _changeSkippedComment = false); ///@{ diff --git a/test/solidityScanner.cpp b/test/solidityScanner.cpp index 1a2299f37..573affe6d 100644 --- a/test/solidityScanner.cpp +++ b/test/solidityScanner.cpp @@ -174,6 +174,7 @@ BOOST_AUTO_TEST_CASE(comment_before_eos) { Scanner scanner(CharStream("//")); BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::EOS); + BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), ""); } BOOST_AUTO_TEST_CASE(documentation_comment_before_eos) @@ -183,6 +184,16 @@ BOOST_AUTO_TEST_CASE(documentation_comment_before_eos) BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), ""); } +BOOST_AUTO_TEST_CASE(comments_mixed_in_sequence) +{ + Scanner scanner(CharStream("hello_world ///documentation comment \n" + "//simple comment \n" + "<<")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.next(), Token::SHL); + BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "documentation comment "); +} + BOOST_AUTO_TEST_SUITE_END() } From babddd394ec774a3b5d7426a6469bbd836de6f9a Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 20 Nov 2014 23:56:24 +0100 Subject: [PATCH 11/14] cleaning up the external interface of Scanner::next(). No special cases --- libsolidity/Scanner.cpp | 10 +++++++--- libsolidity/Scanner.h | 6 ++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index 934b30dcf..d30000999 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -109,7 +109,11 @@ void Scanner::reset(CharStream const& _source) m_char = m_source.get(); skipWhitespace(); foundDocComment = scanToken(); - next(foundDocComment); + + // special version of Scanner:next() taking the previous scanToken() result into account + m_current_token = m_next_token; + if (scanToken() || foundDocComment) + m_skipped_comment = m_next_skipped_comment; } @@ -135,10 +139,10 @@ bool Scanner::scanHexByte(char& o_scannedByte) // Ensure that tokens can be stored in a byte. BOOST_STATIC_ASSERT(Token::NUM_TOKENS <= 0x100); -Token::Value Scanner::next(bool _changeSkippedComment) +Token::Value Scanner::next() { m_current_token = m_next_token; - if (scanToken() || _changeSkippedComment) + if (scanToken()) m_skipped_comment = m_next_skipped_comment; return m_current_token.token; } diff --git a/libsolidity/Scanner.h b/libsolidity/Scanner.h index 94c67840a..5dfe7a33a 100644 --- a/libsolidity/Scanner.h +++ b/libsolidity/Scanner.h @@ -116,10 +116,8 @@ public: /// Resets the scanner as if newly constructed with _input as input. void reset(CharStream const& _source); - /// Returns the next token and advances input. If called from reset() - /// and ScanToken() found a documentation token then next should be called - /// with _changeSkippedComment=true - Token::Value next(bool _changeSkippedComment = false); + /// Returns the next token and advances input + Token::Value next(); ///@{ ///@name Information about the current token From 7f959f12921a7620c7b3de01da5dfbbcf0d6cb95 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 21 Nov 2014 09:09:39 +0100 Subject: [PATCH 12/14] simplifying scanDocumentationComment() --- libsolidity/Scanner.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index d30000999..9382b1346 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -184,9 +184,8 @@ Token::Value Scanner::scanDocumentationComment() advance(); //consume the last '/' while (!isSourcePastEndOfInput() && !IsLineTerminator(m_char)) { - char c = m_char; + addCommentLiteralChar(m_char); advance(); - addCommentLiteralChar(c); } literal.Complete(); return Token::COMMENT_LITERAL; From d455d6f6512a8d6d83005f52a6bcdf8cd058274a Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 21 Nov 2014 17:08:35 +0100 Subject: [PATCH 13/14] modifying solidity scanner class to conform with the coding standards --- libsolidity/Scanner.cpp | 128 ++++++++++++++++++++-------------------- libsolidity/Scanner.h | 46 +++++++-------- 2 files changed, 88 insertions(+), 86 deletions(-) diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index 9382b1346..5ebe8d643 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -63,34 +63,34 @@ namespace solidity namespace { -bool IsDecimalDigit(char c) +bool isDecimalDigit(char c) { return '0' <= c && c <= '9'; } -bool IsHexDigit(char c) +bool isHexDigit(char c) { - return IsDecimalDigit(c) + return isDecimalDigit(c) || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F'); } -bool IsLineTerminator(char c) +bool isLineTerminator(char c) { return c == '\n'; } -bool IsWhiteSpace(char c) +bool isWhiteSpace(char c) { return c == ' ' || c == '\n' || c == '\t'; } -bool IsIdentifierStart(char c) +bool isIdentifierStart(char c) { return c == '_' || c == '$' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); } -bool IsIdentifierPart(char c) +bool isIdentifierPart(char c) { - return IsIdentifierStart(c) || IsDecimalDigit(c); + return isIdentifierStart(c) || isDecimalDigit(c); } -int HexValue(char c) +int hexValue(char c) { if (c >= '0' && c <= '9') return c - '0'; @@ -111,9 +111,9 @@ void Scanner::reset(CharStream const& _source) foundDocComment = scanToken(); // special version of Scanner:next() taking the previous scanToken() result into account - m_current_token = m_next_token; + m_currentToken = m_nextToken; if (scanToken() || foundDocComment) - m_skipped_comment = m_next_skipped_comment; + m_skippedComment = m_nextSkippedComment; } @@ -122,7 +122,7 @@ bool Scanner::scanHexByte(char& o_scannedByte) char x = 0; for (int i = 0; i < 2; i++) { - int d = HexValue(m_char); + int d = hexValue(m_char); if (d < 0) { rollback(i); @@ -141,10 +141,10 @@ BOOST_STATIC_ASSERT(Token::NUM_TOKENS <= 0x100); Token::Value Scanner::next() { - m_current_token = m_next_token; + m_currentToken = m_nextToken; if (scanToken()) - m_skipped_comment = m_next_skipped_comment; - return m_current_token.token; + m_skippedComment = m_nextSkippedComment; + return m_currentToken.token; } Token::Value Scanner::selectToken(char _next, Token::Value _then, Token::Value _else) @@ -159,11 +159,11 @@ Token::Value Scanner::selectToken(char _next, Token::Value _then, Token::Value _ bool Scanner::skipWhitespace() { - int const start_position = getSourcePos(); - while (IsWhiteSpace(m_char)) + int const startPosition = getSourcePos(); + while (isWhiteSpace(m_char)) advance(); // Return whether or not we skipped any characters. - return getSourcePos() != start_position; + return getSourcePos() != startPosition; } @@ -173,7 +173,7 @@ Token::Value Scanner::skipSingleLineComment() // to be part of the single-line comment; it is recognized // separately by the lexical grammar and becomes part of the // stream of input elements for the syntactic grammar - while (advance() && !IsLineTerminator(m_char)) { }; + while (advance() && !isLineTerminator(m_char)) { }; return Token::WHITESPACE; } @@ -182,12 +182,12 @@ Token::Value Scanner::scanDocumentationComment() { LiteralScope literal(this); advance(); //consume the last '/' - while (!isSourcePastEndOfInput() && !IsLineTerminator(m_char)) + while (!isSourcePastEndOfInput() && !isLineTerminator(m_char)) { addCommentLiteralChar(m_char); advance(); } - literal.Complete(); + literal.complete(); return Token::COMMENT_LITERAL; } @@ -217,12 +217,12 @@ Token::Value Scanner::skipMultiLineComment() bool Scanner::scanToken() { bool foundDocComment = false; - m_next_token.literal.clear(); + m_nextToken.literal.clear(); Token::Value token; do { // Remember the position of the next token - m_next_token.location.start = getSourcePos(); + m_nextToken.location.start = getSourcePos(); switch (m_char) { case '\n': // fall-through @@ -301,7 +301,7 @@ bool Scanner::scanToken() } else if (m_char == '=') token = selectToken(Token::ASSIGN_SUB); - else if (m_char == '.' || IsDecimalDigit(m_char)) + else if (m_char == '.' || isDecimalDigit(m_char)) token = scanNumber('-'); else token = Token::SUB; @@ -324,10 +324,10 @@ bool Scanner::scanToken() else if (m_char == '/') { Token::Value comment; - m_next_skipped_comment.location.start = getSourcePos(); + m_nextSkippedComment.location.start = getSourcePos(); comment = scanDocumentationComment(); - m_next_skipped_comment.location.end = getSourcePos(); - m_next_skipped_comment.token = comment; + m_nextSkippedComment.location.end = getSourcePos(); + m_nextSkippedComment.token = comment; token = Token::WHITESPACE; foundDocComment = true; } @@ -368,7 +368,7 @@ bool Scanner::scanToken() case '.': // . Number advance(); - if (IsDecimalDigit(m_char)) + if (isDecimalDigit(m_char)) token = scanNumber('.'); else token = Token::PERIOD; @@ -407,9 +407,9 @@ bool Scanner::scanToken() token = selectToken(Token::BIT_NOT); break; default: - if (IsIdentifierStart(m_char)) + if (isIdentifierStart(m_char)) token = scanIdentifierOrKeyword(); - else if (IsDecimalDigit(m_char)) + else if (isDecimalDigit(m_char)) token = scanNumber(); else if (skipWhitespace()) token = Token::WHITESPACE; @@ -423,8 +423,8 @@ bool Scanner::scanToken() // whitespace. } while (token == Token::WHITESPACE); - m_next_token.location.end = getSourcePos(); - m_next_token.token = token; + m_nextToken.location.end = getSourcePos(); + m_nextToken.token = token; return foundDocComment; } @@ -434,7 +434,7 @@ bool Scanner::scanEscape() char c = m_char; advance(); // Skip escaped newlines. - if (IsLineTerminator(c)) + if (isLineTerminator(c)) return true; switch (c) { @@ -475,7 +475,7 @@ Token::Value Scanner::scanString() char const quote = m_char; advance(); // consume quote LiteralScope literal(this); - while (m_char != quote && !isSourcePastEndOfInput() && !IsLineTerminator(m_char)) + while (m_char != quote && !isSourcePastEndOfInput() && !isLineTerminator(m_char)) { char c = m_char; advance(); @@ -487,8 +487,9 @@ Token::Value Scanner::scanString() else addLiteralChar(c); } - if (m_char != quote) return Token::ILLEGAL; - literal.Complete(); + if (m_char != quote) + return Token::ILLEGAL; + literal.complete(); advance(); // consume quote return Token::STRING_LITERAL; } @@ -496,7 +497,7 @@ Token::Value Scanner::scanString() void Scanner::scanDecimalDigits() { - while (IsDecimalDigit(m_char)) + while (isDecimalDigit(m_char)) addLiteralCharAndAdvance(); } @@ -525,9 +526,9 @@ Token::Value Scanner::scanNumber(char _charSeen) // hex number kind = HEX; addLiteralCharAndAdvance(); - if (!IsHexDigit(m_char)) + if (!isHexDigit(m_char)) return Token::ILLEGAL; // we must have at least one hex digit after 'x'/'X' - while (IsHexDigit(m_char)) + while (isHexDigit(m_char)) addLiteralCharAndAdvance(); } } @@ -547,12 +548,13 @@ Token::Value Scanner::scanNumber(char _charSeen) { if (asserts(kind != HEX)) // 'e'/'E' must be scanned as part of the hex number BOOST_THROW_EXCEPTION(InternalCompilerError()); - if (kind != DECIMAL) return Token::ILLEGAL; + if (kind != DECIMAL) + return Token::ILLEGAL; // scan exponent addLiteralCharAndAdvance(); if (m_char == '+' || m_char == '-') addLiteralCharAndAdvance(); - if (!IsDecimalDigit(m_char)) + if (!isDecimalDigit(m_char)) return Token::ILLEGAL; // we must have at least one decimal digit after 'e'/'E' scanDecimalDigits(); } @@ -560,9 +562,9 @@ Token::Value Scanner::scanNumber(char _charSeen) // not be an identifier start or a decimal digit; see ECMA-262 // section 7.8.3, page 17 (note that we read only one decimal digit // if the value is 0). - if (IsDecimalDigit(m_char) || IsIdentifierStart(m_char)) + if (isDecimalDigit(m_char) || isIdentifierStart(m_char)) return Token::ILLEGAL; - literal.Complete(); + literal.complete(); return Token::NUMBER; } @@ -724,29 +726,29 @@ Token::Value Scanner::scanNumber(char _charSeen) KEYWORD("while", Token::WHILE) \ -static Token::Value KeywordOrIdentifierToken(string const& input) +static Token::Value KeywordOrIdentifierToken(string const& c_input) { - if (asserts(!input.empty())) + if (asserts(!c_input.empty())) BOOST_THROW_EXCEPTION(InternalCompilerError()); int const kMinLength = 2; int const kMaxLength = 10; - if (input.size() < kMinLength || input.size() > kMaxLength) + if (c_input.size() < kMinLength || c_input.size() > kMaxLength) return Token::IDENTIFIER; - switch (input[0]) + switch (c_input[0]) { default: -#define KEYWORD_GROUP_CASE(ch) \ - break; \ -case ch: -#define KEYWORD(keyword, token) \ - { \ - /* 'keyword' is a char array, so sizeof(keyword) is */ \ - /* strlen(keyword) plus 1 for the NUL char. */ \ - int const keyword_length = sizeof(keyword) - 1; \ - BOOST_STATIC_ASSERT(keyword_length >= kMinLength); \ - BOOST_STATIC_ASSERT(keyword_length <= kMaxLength); \ - if (input == keyword) \ - return token; \ +#define KEYWORD_GROUP_CASE(ch) \ + break; \ + case ch: +#define KEYWORD(keyword, token) \ + { \ + /* 'keyword' is a char array, so sizeof(keyword) is */ \ + /* strlen(keyword) plus 1 for the NUL char. */ \ + int const keywordLength = sizeof(keyword) - 1; \ + BOOST_STATIC_ASSERT(keywordLength >= kMinLength); \ + BOOST_STATIC_ASSERT(keywordLength <= kMaxLength); \ + if (c_input == keyword) \ + return token; \ } KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD) } @@ -755,15 +757,15 @@ case ch: Token::Value Scanner::scanIdentifierOrKeyword() { - if (asserts(IsIdentifierStart(m_char))) + if (asserts(isIdentifierStart(m_char))) BOOST_THROW_EXCEPTION(InternalCompilerError()); LiteralScope literal(this); addLiteralCharAndAdvance(); // Scan the rest of the identifier characters. - while (IsIdentifierPart(m_char)) + while (isIdentifierPart(m_char)) addLiteralCharAndAdvance(); - literal.Complete(); - return KeywordOrIdentifierToken(m_next_token.literal); + literal.complete(); + return KeywordOrIdentifierToken(m_nextToken.literal); } char CharStream::advanceAndGet() diff --git a/libsolidity/Scanner.h b/libsolidity/Scanner.h index 5dfe7a33a..957f02b1f 100644 --- a/libsolidity/Scanner.h +++ b/libsolidity/Scanner.h @@ -96,18 +96,18 @@ private: class Scanner { public: - // Scoped helper for literal recording. Automatically drops the literal - // if aborting the scanning before it's complete. + /// Scoped helper for literal recording. Automatically drops the literal + /// if aborting the scanning before it's complete. class LiteralScope { public: - explicit LiteralScope(Scanner* self): scanner_(self), complete_(false) { scanner_->startNewLiteral(); } - ~LiteralScope() { if (!complete_) scanner_->dropLiteral(); } - void Complete() { complete_ = true; } + explicit LiteralScope(Scanner* self): m_scanner(self), m_complete(false) { m_scanner->startNewLiteral(); } + ~LiteralScope() { if (!m_complete) m_scanner->dropLiteral(); } + void complete() { m_complete = true; } private: - Scanner* scanner_; - bool complete_; + Scanner* m_scanner; + bool m_complete; }; Scanner() { reset(CharStream()); } @@ -125,25 +125,25 @@ public: /// Returns the current token Token::Value getCurrentToken() { - return m_current_token.token; + return m_currentToken.token; } - Location getCurrentLocation() const { return m_current_token.location; } - std::string const& getCurrentLiteral() const { return m_current_token.literal; } + Location getCurrentLocation() const { return m_currentToken.location; } + std::string const& getCurrentLiteral() const { return m_currentToken.literal; } ///@} ///@{ ///@name Information about the current comment token - Location getCurrentCommentLocation() const { return m_skipped_comment.location; } - std::string const& getCurrentCommentLiteral() const { return m_skipped_comment.literal; } + Location getCurrentCommentLocation() const { return m_skippedComment.location; } + std::string const& getCurrentCommentLiteral() const { return m_skippedComment.literal; } ///@} ///@{ ///@name Information about the next token /// Returns the next token without advancing input. - Token::Value peekNextToken() const { return m_next_token.token; } - Location peekLocation() const { return m_next_token.location; } - std::string const& peekLiteral() const { return m_next_token.literal; } + Token::Value peekNextToken() const { return m_nextToken.token; } + Location peekLocation() const { return m_nextToken.location; } + std::string const& peekLiteral() const { return m_nextToken.literal; } ///@} ///@{ @@ -165,10 +165,10 @@ private: ///@{ ///@name Literal buffer support - inline void startNewLiteral() { m_next_token.literal.clear(); } - inline void addLiteralChar(char c) { m_next_token.literal.push_back(c); } - inline void addCommentLiteralChar(char c) { m_next_skipped_comment.literal.push_back(c); } - inline void dropLiteral() { m_next_token.literal.clear(); } + inline void startNewLiteral() { m_nextToken.literal.clear(); } + inline void addLiteralChar(char c) { m_nextToken.literal.push_back(c); } + inline void addCommentLiteralChar(char c) { m_nextSkippedComment.literal.push_back(c); } + inline void dropLiteral() { m_nextToken.literal.clear(); } inline void addLiteralCharAndAdvance() { addLiteralChar(m_char); advance(); } ///@} @@ -206,11 +206,11 @@ private: int getSourcePos() { return m_source.getPos(); } bool isSourcePastEndOfInput() { return m_source.isPastEndOfInput(); } - TokenDesc m_skipped_comment; // desc for current skipped comment - TokenDesc m_next_skipped_comment; // desc for next skiped comment + TokenDesc m_skippedComment; // desc for current skipped comment + TokenDesc m_nextSkippedComment; // desc for next skiped comment - TokenDesc m_current_token; // desc for current token (as returned by Next()) - TokenDesc m_next_token; // desc for next token (one token look-ahead) + TokenDesc m_currentToken; // desc for current token (as returned by Next()) + TokenDesc m_nextToken; // desc for next token (one token look-ahead) CharStream m_source; From 86e5a3bb48505507504f47eb90df806fa61f4e0a Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 21 Nov 2014 17:43:24 +0100 Subject: [PATCH 14/14] no c_ prefix for const parameter names --- libsolidity/Scanner.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index 5ebe8d643..895b8ae7a 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -726,15 +726,15 @@ Token::Value Scanner::scanNumber(char _charSeen) KEYWORD("while", Token::WHILE) \ -static Token::Value KeywordOrIdentifierToken(string const& c_input) +static Token::Value KeywordOrIdentifierToken(string const& _input) { - if (asserts(!c_input.empty())) + if (asserts(!_input.empty())) BOOST_THROW_EXCEPTION(InternalCompilerError()); int const kMinLength = 2; int const kMaxLength = 10; - if (c_input.size() < kMinLength || c_input.size() > kMaxLength) + if (_input.size() < kMinLength || _input.size() > kMaxLength) return Token::IDENTIFIER; - switch (c_input[0]) + switch (_input[0]) { default: #define KEYWORD_GROUP_CASE(ch) \ @@ -747,7 +747,7 @@ static Token::Value KeywordOrIdentifierToken(string const& c_input) int const keywordLength = sizeof(keyword) - 1; \ BOOST_STATIC_ASSERT(keywordLength >= kMinLength); \ BOOST_STATIC_ASSERT(keywordLength <= kMaxLength); \ - if (c_input == keyword) \ + if (_input == keyword) \ return token; \ } KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD)