diff --git a/alethzero/CMakeLists.txt b/alethzero/CMakeLists.txt index 4ad06f7a6..7f9cbb812 100644 --- a/alethzero/CMakeLists.txt +++ b/alethzero/CMakeLists.txt @@ -53,7 +53,7 @@ else () endif () qt5_use_modules(${EXECUTEABLE} Core)# Gui Widgets Network WebKit WebKitWidgets) -target_link_libraries(${EXECUTEABLE} webthree qethereum ethereum evm ethcore devcrypto secp256k1 gmp ${CRYPTOPP_LS} serpent lll evmface devcore) +target_link_libraries(${EXECUTEABLE} webthree qethereum ethereum evm ethcore devcrypto secp256k1 gmp ${CRYPTOPP_LS} serpent lll solidity evmface devcore) if (APPLE) # First have qt5 install plugins and frameworks diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 2853536b9..3c874d8a2 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -35,6 +35,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -1560,6 +1563,20 @@ void Main::on_data_textChanged() { m_data = fromHex(src); } + else if (src.substr(0, 8) == "contract") // improve this heuristic + { + shared_ptr scanner = make_shared(); + try + { + m_data = dev::solidity::CompilerStack::compile(src, scanner); + } + catch (dev::Exception const& exception) + { + ostringstream error; + solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", *scanner); + errors.push_back(error.str()); + } + } else { m_data = dev::eth::compileLLL(src, m_enableOptimizer, &errors); diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 7e40db15f..b1e3c3da3 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -46,9 +46,22 @@ void Compiler::compileContract(ContractDefinition& _contract) for (ASTPointer const& function: _contract.getDefinedFunctions()) m_context.addFunction(*function); + appendFunctionSelector(_contract.getDefinedFunctions()); for (ASTPointer const& function: _contract.getDefinedFunctions()) function->accept(*this); + + packIntoContractCreator(); +} + +void Compiler::packIntoContractCreator() +{ + CompilerContext creatorContext; + eth::AssemblyItem sub = creatorContext.addSubroutine(m_context.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); } void Compiler::appendFunctionSelector(vector> const& _functions) diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h index ebd786658..d10374a9b 100644 --- a/libsolidity/Compiler.h +++ b/libsolidity/Compiler.h @@ -40,6 +40,8 @@ public: static bytes compile(ContractDefinition& _contract); 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 appendFunctionCallSection(FunctionDefinition const& _function); void appendCalldataUnpacker(FunctionDefinition const& _function); diff --git a/libsolidity/CompilerContext.h b/libsolidity/CompilerContext.h index cce5838ef..46c4c72ab 100644 --- a/libsolidity/CompilerContext.h +++ b/libsolidity/CompilerContext.h @@ -63,6 +63,9 @@ public: eth::AssemblyItem pushNewTag() { return m_asm.append(m_asm.newPushTag()).tag(); } /// @returns a new tag without pushing any opcodes or data eth::AssemblyItem newTag() { return m_asm.newTag(); } + /// Adds a subroutine to the code (in the data section) and pushes its size (via a tag) + /// on the stack. @returns the assembly item corresponding to the pushed subroutine, i.e. its offset. + eth::AssemblyItem addSubroutine(eth::Assembly const& _assembly) { return m_asm.appendSubSize(_assembly); } /// Append elements to the current instruction list and adjust @a m_stackOffset. CompilerContext& operator<<(eth::AssemblyItem const& _item) { m_asm.append(_item); return *this; } @@ -70,6 +73,7 @@ public: CompilerContext& operator<<(u256 const& _value) { m_asm.append(_value); return *this; } CompilerContext& operator<<(bytes const& _data) { m_asm.append(_data); return *this; } + eth::Assembly const& getAssembly() const { return m_asm; } void streamAssembly(std::ostream& _stream) const { _stream << m_asm; } bytes getAssembledBytecode() const { return m_asm.assemble(); } private: diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp new file mode 100644 index 000000000..bbd693ae5 --- /dev/null +++ b/libsolidity/CompilerStack.cpp @@ -0,0 +1,49 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * Full-stack compiler that converts a source code string to bytecode. + */ + +#include +#include +#include +#include +#include +#include + +using namespace std; + +namespace dev +{ +namespace solidity +{ + +bytes CompilerStack::compile(std::string const& _sourceCode, shared_ptr _scanner) +{ + if (!_scanner) + _scanner = make_shared(); + _scanner->reset(CharStream(_sourceCode)); + + ASTPointer contract = Parser().parse(_scanner); + NameAndTypeResolver().resolveNamesAndTypes(*contract); + return Compiler::compile(*contract); +} + +} +} diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h new file mode 100644 index 000000000..9f3f81c04 --- /dev/null +++ b/libsolidity/CompilerStack.h @@ -0,0 +1,43 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * Full-stack compiler that converts a source code string to bytecode. + */ + +#pragma once + +#include +#include +#include + +namespace dev { +namespace solidity { + +class Scanner; // forward + +class CompilerStack +{ +public: + /// Compile the given @a _sourceCode to bytecode. If a scanner is provided, it is used for + /// scanning the source code - this is useful for printing exception information. + static bytes compile(std::string const& _sourceCode, std::shared_ptr _scanner = std::shared_ptr()); +}; + +} +} diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index 3148de52e..d8defb50a 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -103,11 +103,6 @@ int HexValue(char c) } } // end anonymous namespace -Scanner::Scanner(CharStream const& _source) -{ - reset(_source); -} - void Scanner::reset(CharStream const& _source) { m_source = _source; diff --git a/libsolidity/Scanner.h b/libsolidity/Scanner.h index fbaba9ed6..7754d71b4 100644 --- a/libsolidity/Scanner.h +++ b/libsolidity/Scanner.h @@ -110,7 +110,8 @@ public: bool complete_; }; - explicit Scanner(CharStream const& _source); + Scanner() { reset(CharStream()); } + explicit Scanner(CharStream const& _source) { reset(_source); } /// Resets the scanner as if newly constructed with _input as input. void reset(CharStream const& _source); diff --git a/test/solidityCompiler.cpp b/test/solidityCompiler.cpp index 4272354c3..c0d4e32df 100644 --- a/test/solidityCompiler.cpp +++ b/test/solidityCompiler.cpp @@ -81,7 +81,7 @@ BOOST_AUTO_TEST_CASE(smoke_test) "}\n"; bytes code = compileContract(sourceCode); - unsigned boilerplateSize = 39; + unsigned boilerplateSize = 51; bytes expectation({byte(Instruction::JUMPDEST), byte(Instruction::PUSH1), 0x0, // initialize local variable x byte(Instruction::PUSH1), 0x2, @@ -101,13 +101,14 @@ BOOST_AUTO_TEST_CASE(different_argument_numbers) "}\n"; bytes code = compileContract(sourceCode); - unsigned boilerplateSize = 76; + unsigned shift = 76; + unsigned boilerplateSize = 88; bytes expectation({byte(Instruction::JUMPDEST), byte(Instruction::PUSH1), 0x0, // initialize return variable d byte(Instruction::DUP3), byte(Instruction::SWAP1), // assign b to d byte(Instruction::POP), - byte(Instruction::PUSH1), 0xa + boilerplateSize, // jump to return + byte(Instruction::PUSH1), 0xa + shift, // jump to return byte(Instruction::JUMP), byte(Instruction::JUMPDEST), byte(Instruction::SWAP4), // store d and fetch return address @@ -119,11 +120,11 @@ BOOST_AUTO_TEST_CASE(different_argument_numbers) byte(Instruction::JUMPDEST), // beginning of g byte(Instruction::PUSH1), 0x0, byte(Instruction::DUP1), // initialized e and h - byte(Instruction::PUSH1), 0x20 + boilerplateSize, // ret address + byte(Instruction::PUSH1), 0x20 + shift, // ret address byte(Instruction::PUSH1), 0x1, byte(Instruction::PUSH1), 0x2, byte(Instruction::PUSH1), 0x3, - byte(Instruction::PUSH1), 0x1 + boilerplateSize, + byte(Instruction::PUSH1), 0x1 + shift, // stack here: ret e h 0x20 1 2 3 0x1 byte(Instruction::JUMP), byte(Instruction::JUMPDEST), @@ -153,28 +154,29 @@ BOOST_AUTO_TEST_CASE(ifStatement) "}\n"; bytes code = compileContract(sourceCode); - unsigned boilerplateSize = 39; + unsigned shift = 39; + unsigned boilerplateSize = 51; bytes expectation({byte(Instruction::JUMPDEST), byte(Instruction::PUSH1), 0x0, byte(Instruction::DUP1), - byte(Instruction::PUSH1), 0x1b + boilerplateSize, // "true" target + byte(Instruction::PUSH1), 0x1b + shift, // "true" target byte(Instruction::JUMPI), // new check "else if" condition byte(Instruction::DUP1), byte(Instruction::NOT), - byte(Instruction::PUSH1), 0x13 + boilerplateSize, + byte(Instruction::PUSH1), 0x13 + shift, byte(Instruction::JUMPI), // "else" body byte(Instruction::PUSH1), 0x4f, byte(Instruction::POP), - byte(Instruction::PUSH1), 0x17 + boilerplateSize, // exit path of second part + byte(Instruction::PUSH1), 0x17 + shift, // exit path of second part byte(Instruction::JUMP), // "else if" body byte(Instruction::JUMPDEST), byte(Instruction::PUSH1), 0x4e, byte(Instruction::POP), byte(Instruction::JUMPDEST), - byte(Instruction::PUSH1), 0x1f + boilerplateSize, + byte(Instruction::PUSH1), 0x1f + shift, byte(Instruction::JUMP), // "if" body byte(Instruction::JUMPDEST), @@ -194,28 +196,29 @@ BOOST_AUTO_TEST_CASE(loops) "}\n"; bytes code = compileContract(sourceCode); - unsigned boilerplateSize = 39; + unsigned shift = 39; + unsigned boilerplateSize = 51; bytes expectation({byte(Instruction::JUMPDEST), byte(Instruction::JUMPDEST), byte(Instruction::PUSH1), 0x1, byte(Instruction::NOT), - byte(Instruction::PUSH1), 0x21 + boilerplateSize, + byte(Instruction::PUSH1), 0x21 + shift, byte(Instruction::JUMPI), byte(Instruction::PUSH1), 0x1, byte(Instruction::POP), - byte(Instruction::PUSH1), 0x21 + boilerplateSize, + byte(Instruction::PUSH1), 0x21 + shift, byte(Instruction::JUMP), // break byte(Instruction::PUSH1), 0x2, byte(Instruction::POP), - byte(Instruction::PUSH1), 0x2 + boilerplateSize, + byte(Instruction::PUSH1), 0x2 + shift, byte(Instruction::JUMP), // continue byte(Instruction::PUSH1), 0x3, byte(Instruction::POP), - byte(Instruction::PUSH1), 0x22 + boilerplateSize, + byte(Instruction::PUSH1), 0x22 + shift, byte(Instruction::JUMP), // return byte(Instruction::PUSH1), 0x4, byte(Instruction::POP), - byte(Instruction::PUSH1), 0x2 + boilerplateSize, + byte(Instruction::PUSH1), 0x2 + shift, byte(Instruction::JUMP), byte(Instruction::JUMPDEST), byte(Instruction::JUMPDEST),