diff --git a/.gitignore b/.gitignore index b6abc22a3..8d8974809 100644 --- a/.gitignore +++ b/.gitignore @@ -65,7 +65,6 @@ DerivedData project.pbxproj -evmjit doc/html *.autosave node_modules/ diff --git a/CMakeLists.txt b/CMakeLists.txt index f1246e152..9bab77585 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,9 +124,7 @@ include(EthExecutableHelper) createBuildInfo() if (EVMJIT) - # Workaround for Ubuntu broken LLVM package - link_directories(/usr/lib/llvm-3.5/lib) - + set(EVMJIT_CPP TRUE) # include CPP-JIT connector add_subdirectory(evmjit) endif() diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 66c66face..7ed961a77 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -344,8 +344,8 @@ void Main::onNewBlock() refreshBlockChain(); refreshAccounts(); - // We must update balances since we can't filter updates to basic accounts. - refreshBalances(); + // We must update balances since we can't filter updates to basic accounts. + refreshBalances(); } void Main::onNewPending() diff --git a/evmjit/.gitignore b/evmjit/.gitignore new file mode 100644 index 000000000..84c048a73 --- /dev/null +++ b/evmjit/.gitignore @@ -0,0 +1 @@ +/build/ diff --git a/evmjit/CMakeLists.txt b/evmjit/CMakeLists.txt index 18601b921..519346007 100644 --- a/evmjit/CMakeLists.txt +++ b/evmjit/CMakeLists.txt @@ -4,8 +4,18 @@ project(evmjit) set_property(GLOBAL PROPERTY USE_FOLDERS ON) +if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") +else() + set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wno-unknown-pragmas -Wextra -fPIC") +endif() + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + # Do not allow unresovled symbols in shared library (default on linux) + set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined") +endif() + # LLVM -if(LLVM_DIR) # local LLVM build +if(LLVM_DIR OR APPLE) # local LLVM build find_package(LLVM REQUIRED CONFIG) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") @@ -18,11 +28,19 @@ else() execute_process(COMMAND llvm-config-3.5 --includedir OUTPUT_VARIABLE LLVM_INCLUDE_DIRS) message(STATUS "LLVM include dirs: ${LLVM_INCLUDE_DIRS}") set(LLVM_LIBS "-lLLVMBitWriter -lLLVMX86CodeGen -lLLVMSelectionDAG -lLLVMAsmPrinter -lLLVMCodeGen -lLLVMScalarOpts -lLLVMInstCombine -lLLVMTransformUtils -lLLVMipa -lLLVMAnalysis -lLLVMX86AsmParser -lLLVMX86Desc -lLLVMX86Info -lLLVMX86AsmPrinter -lLLVMX86Utils -lLLVMMCJIT -lLLVMTarget -lLLVMRuntimeDyld -lLLVMObject -lLLVMMCParser -lLLVMBitReader -lLLVMExecutionEngine -lLLVMMC -lLLVMCore -lLLVMSupport -lz -lpthread -lffi -ltinfo -ldl -lm") + add_definitions(-D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS) + link_directories(/usr/lib/llvm-3.5/lib) endif() # Boost find_package(Boost REQUIRED) add_subdirectory(libevmjit) -add_subdirectory(libevmjit-cpp) -add_subdirectory(evmcc) + +if(EVMJIT_CPP) + add_subdirectory(libevmjit-cpp) +endif() + +if(EVMJIT_TOOLS) + add_subdirectory(evmcc) +endif() diff --git a/evmjit/libevmjit-cpp/Env.cpp b/evmjit/libevmjit-cpp/Env.cpp index 6320aa222..90474c7e4 100644 --- a/evmjit/libevmjit-cpp/Env.cpp +++ b/evmjit/libevmjit-cpp/Env.cpp @@ -89,7 +89,7 @@ extern "C" *o_hash = hash; } - EXPORT byte const* env_getExtCode(ExtVMFace* _env, h256* _addr256, uint64_t* o_size) + EXPORT byte const* env_extcode(ExtVMFace* _env, h256* _addr256, uint64_t* o_size) { auto addr = right160(*_addr256); auto& code = _env->codeAt(addr); diff --git a/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h index 7a5364a4e..cda4f89bd 100644 --- a/evmjit/libevmjit/BasicBlock.h +++ b/evmjit/libevmjit/BasicBlock.h @@ -57,7 +57,7 @@ public: explicit BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest); BasicBlock(const BasicBlock&) = delete; - void operator=(const BasicBlock&) = delete; + BasicBlock& operator=(const BasicBlock&) = delete; llvm::BasicBlock* llvm() { return m_llvmBB; } @@ -66,6 +66,9 @@ public: bool isJumpDest() const { return m_isJumpDest; } + llvm::Value* getJumpTarget() const { return m_jumpTarget; } + void setJumpTarget(llvm::Value* _jumpTarget) { m_jumpTarget = _jumpTarget; } + LocalStack& localStack() { return m_stack; } /// Optimization: propagates values between local stacks in basic blocks @@ -112,6 +115,9 @@ private: /// Is the basic block a valid jump destination. /// JUMPDEST is the first instruction of the basic block. bool const m_isJumpDest = false; + + /// If block finishes with dynamic jump target index is stored here + llvm::Value* m_jumpTarget = nullptr; }; } diff --git a/evmjit/libevmjit/CMakeLists.txt b/evmjit/libevmjit/CMakeLists.txt index 7c35169a7..c1acfc409 100644 --- a/evmjit/libevmjit/CMakeLists.txt +++ b/evmjit/libevmjit/CMakeLists.txt @@ -2,23 +2,24 @@ set(TARGET_NAME evmjit) file(GLOB SOURCES "*.cpp") file(GLOB HEADERS "*.h") +set(INTERFACE_HEADERS interface.h) source_group("" FILES ${HEADERS}) source_group("" FILES ${SOURCES}) if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # Disable rtti for Cache as LLVM has no rtti set_source_files_properties(Cache.cpp PROPERTIES COMPILE_FLAGS -fno-rtti) -endif () +endif() -add_library(${TARGET_NAME} ${SOURCES} ${HEADERS}) +add_library(${TARGET_NAME} SHARED ${SOURCES} ${HEADERS}) set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs") include_directories(${LLVM_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS}) -target_link_libraries(${TARGET_NAME} ${LLVM_LIBS}) +target_link_libraries(${TARGET_NAME} PRIVATE ${LLVM_LIBS}) #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") -#install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) -#install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) +install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION lib) +install(FILES ${INTERFACE_HEADERS} DESTINATION include/${TARGET_NAME}) diff --git a/evmjit/libevmjit/Cache.cpp b/evmjit/libevmjit/Cache.cpp index 0b725bc24..0e6fb517a 100644 --- a/evmjit/libevmjit/Cache.cpp +++ b/evmjit/libevmjit/Cache.cpp @@ -36,6 +36,20 @@ std::unique_ptr Cache::getObject(std::string const& id) llvm::sys::path::system_temp_directory(false, cachePath); llvm::sys::path::append(cachePath, "evm_objs", id); +#if defined(__GNUC__) && !defined(NDEBUG) + llvm::sys::fs::file_status st; + auto err = llvm::sys::fs::status(cachePath.str(), st); + if (err) + return nullptr; + auto mtime = st.getLastModificationTime().toEpochTime(); + + std::tm tm; + strptime(__DATE__ __TIME__, " %b %d %Y %H:%M:%S", &tm); + auto btime = (uint64_t)std::mktime(&tm); + if (btime > mtime) + return nullptr; +#endif + if (auto r = llvm::MemoryBuffer::getFile(cachePath.str(), -1, false)) lastObject = llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer()); else if (r.getError() != std::make_error_code(std::errc::no_such_file_or_directory)) diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp index 26c78b4ca..1d00fd225 100644 --- a/evmjit/libevmjit/Compiler.cpp +++ b/evmjit/libevmjit/Compiler.cpp @@ -100,7 +100,7 @@ llvm::BasicBlock* Compiler::getJumpTableBlock() m_jumpTableBlock.reset(new BasicBlock("JumpTable", m_mainFunc, m_builder, true)); InsertPointGuard g{m_builder}; m_builder.SetInsertPoint(m_jumpTableBlock->llvm()); - auto dest = m_jumpTableBlock->localStack().pop(); + auto dest = m_builder.CreatePHI(Type::Word, 8, "target"); auto switchInstr = m_builder.CreateSwitch(dest, getBadJumpBlock()); for (auto&& p : m_basicBlocks) { @@ -165,6 +165,26 @@ std::unique_ptr Compiler::compile(bytes const& _bytecode, std::str removeDeadBlocks(); + // Link jump table target index + if (m_jumpTableBlock) + { + auto phi = llvm::cast(&m_jumpTableBlock->llvm()->getInstList().front()); + for (auto predIt = llvm::pred_begin(m_jumpTableBlock->llvm()); predIt != llvm::pred_end(m_jumpTableBlock->llvm()); ++predIt) + { + BasicBlock* pred = nullptr; + for (auto&& p : m_basicBlocks) + { + if (p.second.llvm() == *predIt) + { + pred = &p.second; + break; + } + } + + phi->addIncoming(pred->getJumpTarget(), pred->llvm()); + } + } + dumpCFGifRequired("blocks-init.dot"); if (m_options.optimizeStack) @@ -394,7 +414,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode value = Endianness::toBE(m_builder, value); auto bytes = m_builder.CreateBitCast(value, llvm::VectorType::get(Type::Byte, 32), "bytes"); - auto byte = m_builder.CreateExtractElement(bytes, byteNum, "byte"); + auto safeByteNum = m_builder.CreateZExt(m_builder.CreateTrunc(byteNum, m_builder.getIntNTy(5)), Type::lowPrecision); // Trim index, large values can crash + auto byte = m_builder.CreateExtractElement(bytes, safeByteNum, "byte"); value = m_builder.CreateZExt(byte, Type::Word); auto byteNumValid = m_builder.CreateICmpULT(byteNum, Constant::get(32)); @@ -564,7 +585,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode } else { - stack.push(target); + _basicBlock.setJumpTarget(target); m_builder.CreateBr(getJumpTableBlock()); } } @@ -580,7 +601,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode } else { - stack.push(target); + _basicBlock.setJumpTarget(target); m_builder.CreateCondBr(cond, getJumpTableBlock(), _nextBasicBlock); } } diff --git a/evmjit/libevmjit/ExecutionEngine.cpp b/evmjit/libevmjit/ExecutionEngine.cpp index 7e99932c3..b6b140fd5 100644 --- a/evmjit/libevmjit/ExecutionEngine.cpp +++ b/evmjit/libevmjit/ExecutionEngine.cpp @@ -19,7 +19,12 @@ #include "Compiler.h" #include "Cache.h" -extern "C" void env_sha3(dev::eth::jit::byte const* _begin, uint64_t _size, std::array* o_hash); +#if defined(NDEBUG) +#define DEBUG_ENV_OPTION(name) false +#else +#include +#define DEBUG_ENV_OPTION(name) (std::getenv(#name) != nullptr) +#endif namespace dev { @@ -49,14 +54,17 @@ ReturnCode runEntryFunc(EntryFuncPtr _mainFunc, Runtime* _runtime) std::string codeHash(bytes const& _code) { - std::array binHash; - env_sha3(_code.data(), _code.size(), &binHash); - - std::ostringstream os; - for (auto i: binHash) - os << std::hex << std::setfill('0') << std::setw(2) << (int)(std::make_unsigned::type)i; - - return os.str(); + uint32_t hash = 0; + for (auto b : _code) + { + hash += b; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return std::to_string(hash); } } @@ -64,6 +72,8 @@ std::string codeHash(bytes const& _code) ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _env) { static std::unique_ptr ee; // TODO: Use Managed Objects from LLVM? + static auto debugDumpModule = DEBUG_ENV_OPTION(EVMJIT_DUMP_MODULE); + static bool objectCacheEnabled = !DEBUG_ENV_OPTION(EVMJIT_CACHE_OFF); auto mainFuncName = codeHash(_code); EntryFuncPtr entryFuncPtr{}; @@ -74,14 +84,14 @@ ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _en } else { - bool objectCacheEnabled = true; auto objectCache = objectCacheEnabled ? Cache::getObjectCache() : nullptr; std::unique_ptr module; if (objectCache) module = Cache::getObject(mainFuncName); if (!module) module = Compiler({}).compile(_code, mainFuncName); - //module->dump(); + if (debugDumpModule) + module->dump(); if (!ee) { llvm::InitializeNativeTarget(); @@ -92,7 +102,7 @@ ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _en builder.setUseMCJIT(true); std::unique_ptr memoryManager(new llvm::SectionMemoryManager); builder.setMCJITMemoryManager(memoryManager.get()); - builder.setOptLevel(llvm::CodeGenOpt::Default); + builder.setOptLevel(llvm::CodeGenOpt::None); auto triple = llvm::Triple(llvm::sys::getProcessTriple()); if (triple.getOS() == llvm::Triple::OSType::Win32) diff --git a/evmjit/libevmjit/Ext.cpp b/evmjit/libevmjit/Ext.cpp index aae68f43a..a37a6fe99 100644 --- a/evmjit/libevmjit/Ext.cpp +++ b/evmjit/libevmjit/Ext.cpp @@ -5,9 +5,6 @@ #include #include -//#include -//#include - #include "RuntimeManager.h" #include "Memory.h" #include "Type.h" @@ -23,12 +20,9 @@ namespace jit Ext::Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan): RuntimeHelper(_runtimeManager), m_memoryMan(_memoryMan) -#ifdef __MSCVER - , - m_funcs({}), // The only std::array initialization that works in both Visual Studio & GCC - m_argAllocas({}) -#endif { + m_funcs = decltype(m_funcs)(); + m_argAllocas = decltype(m_argAllocas)(); m_size = m_builder.CreateAlloca(Type::Size, nullptr, "env.size"); } @@ -74,7 +68,6 @@ llvm::Value* Ext::getArgAlloca() getBuilder().SetInsertPoint(getMainFunction()->front().getFirstNonPHI()); a = getBuilder().CreateAlloca(Type::Word, nullptr, "arg"); } - return a; } diff --git a/evmjit/libevmjit/Runtime.h b/evmjit/libevmjit/Runtime.h index 8cc5b7968..f97efa128 100644 --- a/evmjit/libevmjit/Runtime.h +++ b/evmjit/libevmjit/Runtime.h @@ -34,7 +34,7 @@ public: Runtime(RuntimeData* _data, Env* _env); Runtime(const Runtime&) = delete; - void operator=(const Runtime&) = delete; + Runtime& operator=(const Runtime&) = delete; StackImpl& getStack() { return m_stack; } MemoryImpl& getMemory() { return m_memory; } diff --git a/evmjit/libevmjit/interface.cpp b/evmjit/libevmjit/interface.cpp new file mode 100644 index 000000000..eef92d00f --- /dev/null +++ b/evmjit/libevmjit/interface.cpp @@ -0,0 +1,34 @@ +#include "interface.h" +#include +#include "ExecutionEngine.h" + +extern "C" +{ + +evmjit_result evmjit_run(void* _data, void* _env) +{ + using namespace dev::eth::jit; + + auto data = static_cast(_data); + + ExecutionEngine engine; + + auto codePtr = data->code; + auto codeSize = data->elems[RuntimeData::CodeSize].a; + bytes bytecode; + bytecode.insert(bytecode.end(), codePtr, codePtr + codeSize); + + auto returnCode = engine.run(bytecode, data, static_cast(_env)); + evmjit_result result = {static_cast(returnCode), 0, nullptr}; + if (returnCode == ReturnCode::Return && !engine.returnData.empty()) + { + // TODO: Optimized returning data. Allocating memory on client side by callback function might be a good idea + result.returnDataSize = engine.returnData.size(); + result.returnData = std::malloc(result.returnDataSize); + std::memcpy(result.returnData, engine.returnData.data(), result.returnDataSize); + } + + return result; +} + +} diff --git a/evmjit/libevmjit/interface.c b/evmjit/libevmjit/interface.h similarity index 76% rename from evmjit/libevmjit/interface.c rename to evmjit/libevmjit/interface.h index 47589578b..5d9307ab2 100644 --- a/evmjit/libevmjit/interface.c +++ b/evmjit/libevmjit/interface.h @@ -1,5 +1,20 @@ +#include #include +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct evmjit_result +{ + int32_t returnCode; + uint64_t returnDataSize; + void* returnData; + +} evmjit_result; + +evmjit_result evmjit_run(void* _data, void* _env); + // JIT object opaque type typedef struct evm_jit evm_jit; @@ -9,6 +24,11 @@ typedef int evm_jit_return_code; // Host-endian 256-bit integer type typedef struct i256 i256; +struct i256 +{ + char b[33]; +}; + // Big-endian right aligned 256-bit hash typedef struct h256 h256; @@ -28,3 +48,7 @@ evm_jit_return_code evm_jit_execute(evm_jit* _jit); void evm_jit_get_return_data(evm_jit* _jit, char* _return_data_offset, size_t* _return_data_size); void evm_jit_destroy(evm_jit* _jit); + +#ifdef __cplusplus +} +#endif diff --git a/exp/main.cpp b/exp/main.cpp index d5070f778..80f4fa37d 100644 --- a/exp/main.cpp +++ b/exp/main.cpp @@ -78,7 +78,7 @@ int main() #elif 0 int main() { - KeyPair u = KeyPair::create(); + KeyPair u = KeyPair::create(); KeyPair cb = KeyPair::create(); OverlayDB db; State s(cb.address(), db, BaseState::Empty); @@ -98,8 +98,8 @@ int main() #else int main() { - cnote << KeyPair(Secret("0000000000000000000000000000000000000000000000000000000000000000")).address(); - cnote << KeyPair(Secret("1111111111111111111111111111111111111111111111111111111111111111")).address(); + cnote << KeyPair(Secret("0000000000000000000000000000000000000000000000000000000000000000")).address(); + cnote << KeyPair(Secret("1111111111111111111111111111111111111111111111111111111111111111")).address(); } #endif diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index 2573995c5..dada9dd5d 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -124,6 +124,10 @@ inline bytes toCompactBigEndian(_T _val, unsigned _min = 0) toBigEndian(_val, ret); return ret; } +inline bytes toCompactBigEndian(byte _val, unsigned _min = 0) +{ + return (_min || _val) ? bytes{ _val } : bytes{}; +} /// Convenience function for toBigEndian. /// @returns a string just big enough to represent @a _val. diff --git a/libdevcore/Exceptions.h b/libdevcore/Exceptions.h index 061f181b3..e13d41476 100644 --- a/libdevcore/Exceptions.h +++ b/libdevcore/Exceptions.h @@ -31,7 +31,7 @@ namespace dev { // base class for all exceptions -struct Exception: virtual std::exception, virtual boost::exception {}; +struct Exception: virtual std::exception, virtual boost::exception { mutable std::string m_message; }; struct BadHexCharacter: virtual Exception {}; struct RLPException: virtual Exception {}; diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index acd59184f..2d0170fb9 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -199,6 +199,8 @@ bool Secp256k1::verifySecret(Secret const& _s, Public& _p) void Secp256k1::agree(Secret const& _s, Public const& _r, h256& o_s) { + (void)o_s; + (void)_s; ECDH::Domain d(m_oid); assert(d.AgreedValueLength() == sizeof(o_s)); byte remote[65] = {0x04}; diff --git a/libethcore/Exceptions.cpp b/libethcore/Exceptions.cpp index b0aff4551..9b07743c5 100644 --- a/libethcore/Exceptions.cpp +++ b/libethcore/Exceptions.cpp @@ -27,14 +27,13 @@ using namespace std; using namespace dev; using namespace dev::eth; -#if ALL_COMPILERS_ARE_CPP11_COMPLIANT +#if ALL_COMPILERS_ARE_CPP11 #define ETH_RETURN_STRING(S) thread_local static string s_what; s_what = S; return s_what.c_str(); +#elsif USE_BOOST_TLS +static boost::thread_specific_ptr g_exceptionMessage; +#define ETH_RETURN_STRING(S) if (!g_exceptionMessage.get()); g_exceptionMessage.reset(new string); *g_exceptionMessage.get() = S; return g_exceptionMessage.get()->c_str(); #else -#define ETH_RETURN_STRING(S) \ - static boost::thread_specific_ptr s_what; \ - if (!s_what.get()) \ - s_what.reset(new string); \ - *s_what = S; return s_what->c_str(); +#define ETH_RETURN_STRING(S) m_message = S; return m_message.c_str(); #endif const char* InvalidBlockFormat::what() const noexcept { ETH_RETURN_STRING("Invalid block format: Bad field " + toString(m_f) + " (" + toHex(m_d) + ")"); } diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 476f096e2..4eed2b284 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -35,7 +35,7 @@ using namespace dev::eth; Executive::Executive(State& _s, BlockChain const& _bc, unsigned _level): m_s(_s), - m_lastHashes(_s.getLastHashes(_bc, (unsigned)_s.info().number - 1)), + m_lastHashes(_s.getLastHashes(_bc, (unsigned)_s.info().number - 1)), m_depth(_level) {} diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 7e2429417..9ed9a3a3a 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -418,7 +418,7 @@ TransactionReceipts State::sync(BlockChain const& _bc, TransactionQueue& _tq, bo TransactionReceipts ret; auto ts = _tq.transactions(); - auto lh = getLastHashes(_bc, _bc.number()); + auto lh = getLastHashes(_bc, _bc.number()); for (int goodTxs = 1; goodTxs;) { @@ -498,7 +498,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce) GenericTrieDB receiptsTrie(&rm); receiptsTrie.init(); - LastHashes lh = getLastHashes(_bc, (unsigned)m_previousBlock.number); + LastHashes lh = getLastHashes(_bc, (unsigned)m_previousBlock.number); // All ok with the block generally. Play back the transactions now... unsigned i = 0; @@ -527,7 +527,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce) cwarn << "Bad receipts state root."; cwarn << "Block:" << toHex(_block); cwarn << "Block RLP:" << RLP(_block); - cwarn << "Calculated: " << receiptsTrie.root(); + cwarn << "Calculated: " << receiptsTrie.root(); for (unsigned j = 0; j < i; ++j) { RLPStream k; @@ -539,15 +539,15 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce) cwarn << TransactionReceipt(&b); } cwarn << "Recorded: " << m_currentBlock.receiptsRoot; - auto rs = _bc.receipts(bi.hash); - for (unsigned j = 0; j < rs.receipts.size(); ++j) - { - auto b = rs.receipts[j].rlp(); - cwarn << j << ": "; - cwarn << "RLP: " << RLP(b); - cwarn << "Hex: " << toHex(b); - cwarn << rs.receipts[j]; - } + auto rs = _bc.receipts(m_currentBlock.hash); + for (unsigned j = 0; j < rs.receipts.size(); ++j) + { + auto b = rs.receipts[j].rlp(); + cwarn << j << ": "; + cwarn << "RLP: " << RLP(b); + cwarn << "Hex: " << toHex(b); + cwarn << rs.receipts[j]; + } BOOST_THROW_EXCEPTION(InvalidReceiptsStateRoot()); } @@ -1016,7 +1016,7 @@ LastHashes State::getLastHashes(BlockChain const& _bc, unsigned _n) const ret.resize(256); if (c_protocolVersion > 49) { - ret[0] = _bc.numberHash(_n); + ret[0] = _bc.numberHash(_n); for (unsigned i = 1; i < 256; ++i) ret[i] = ret[i - 1] ? _bc.details(ret[i - 1]).parent : h256(); } @@ -1025,12 +1025,12 @@ LastHashes State::getLastHashes(BlockChain const& _bc, unsigned _n) const u256 State::execute(BlockChain const& _bc, bytes const& _rlp, bytes* o_output, bool _commit) { - return execute(getLastHashes(_bc, _bc.number()), &_rlp, o_output, _commit); + return execute(getLastHashes(_bc, _bc.number()), &_rlp, o_output, _commit); } u256 State::execute(BlockChain const& _bc, bytesConstRef _rlp, bytes* o_output, bool _commit) { - return execute(getLastHashes(_bc, _bc.number()), _rlp, o_output, _commit); + return execute(getLastHashes(_bc, _bc.number()), _rlp, o_output, _commit); } // TODO: maintain node overlay revisions for stateroots -> each commit gives a stateroot + OverlayDB; allow overlay copying for rewind operations. diff --git a/libethereum/State.h b/libethereum/State.h index 462989e4a..85abd9366 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -147,12 +147,12 @@ public: /// Like sync but only operate on _tq, killing the invalid/old ones. bool cull(TransactionQueue& _tq) const; - LastHashes getLastHashes(BlockChain const& _bc, unsigned _n) const; + LastHashes getLastHashes(BlockChain const& _bc, unsigned _n) const; /// Execute a given transaction. /// This will append @a _t to the transaction list and change the state accordingly. - u256 execute(BlockChain const& _bc, bytes const& _rlp, bytes* o_output = nullptr, bool _commit = true); - u256 execute(BlockChain const& _bc, bytesConstRef _rlp, bytes* o_output = nullptr, bool _commit = true); + u256 execute(BlockChain const& _bc, bytes const& _rlp, bytes* o_output = nullptr, bool _commit = true); + u256 execute(BlockChain const& _bc, bytesConstRef _rlp, bytes* o_output = nullptr, bool _commit = true); u256 execute(LastHashes const& _lh, bytes const& _rlp, bytes* o_output = nullptr, bool _commit = true) { return execute(_lh, &_rlp, o_output, _commit); } u256 execute(LastHashes const& _lh, bytesConstRef _rlp, bytes* o_output = nullptr, bool _commit = true); diff --git a/libevm/VM.cpp b/libevm/VM.cpp index aa3caff44..45017db9c 100644 --- a/libevm/VM.cpp +++ b/libevm/VM.cpp @@ -38,10 +38,12 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) if (m_jumpDests.empty()) for (unsigned i = 0; i < _ext.code.size(); ++i) + { if (_ext.code[i] == (byte)Instruction::JUMPDEST) m_jumpDests.insert(i); else if (_ext.code[i] >= (byte)Instruction::PUSH1 && _ext.code[i] <= (byte)Instruction::PUSH32) i += _ext.code[i] - (unsigned)Instruction::PUSH1 + 1; + } u256 nextPC = m_curPC + 1; auto osteps = _steps; for (bool stopped = false; !stopped && _steps--; m_curPC = nextPC, nextPC = m_curPC + 1) diff --git a/libsolidity/ASTJsonConverter.cpp b/libsolidity/ASTJsonConverter.cpp new file mode 100644 index 000000000..04ddee0a7 --- /dev/null +++ b/libsolidity/ASTJsonConverter.cpp @@ -0,0 +1,471 @@ +/* + 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 Lefteris + * @date 2015 + * Converts the AST into json format + */ + +#include +#include + +using namespace std; + +namespace dev +{ +namespace solidity +{ + +void ASTJsonConverter::addKeyValue(Json::Value& _obj, string const& _key, string const& _val) +{ + // special handling for booleans + if (_key == "const" || _key == "public" || _key == "local" || + _key == "lvalue" || _key == "local_lvalue" || _key == "prefix") + _obj[_key] = (_val == "1") ? true : false; + else + // else simply add it as a string + _obj[_key] = _val; +} + +void ASTJsonConverter::addJsonNode(string const& _nodeName, + initializer_list> _list, + bool _hasChildren = false) +{ + Json::Value node; + + node["name"] = _nodeName; + if (_list.size() != 0) + { + Json::Value attrs; + for (auto& e: _list) + addKeyValue(attrs, e.first, e.second); + node["attributes"] = attrs; + } + + m_jsonNodePtrs.top()->append(node); + + if (_hasChildren) + { + Json::Value& addedNode = (*m_jsonNodePtrs.top())[m_jsonNodePtrs.top()->size() - 1]; + Json::Value children(Json::arrayValue); + addedNode["children"] = children; + m_jsonNodePtrs.push(&addedNode["children"]); + } +} + +ASTJsonConverter::ASTJsonConverter(ASTNode const& _ast): m_ast(&_ast) +{ + Json::Value children(Json::arrayValue); + + m_astJson["name"] = "root"; + m_astJson["children"] = children; + m_jsonNodePtrs.push(&m_astJson["children"]); +} + +void ASTJsonConverter::print(ostream& _stream) +{ + m_ast->accept(*this); + _stream << m_astJson; +} + +bool ASTJsonConverter::visit(ImportDirective const& _node) +{ + addJsonNode("Import", { make_pair("file", _node.getIdentifier())}); + return true; +} + +bool ASTJsonConverter::visit(ContractDefinition const& _node) +{ + addJsonNode("Contract", { make_pair("name", _node.getName()) }, true); + return true; +} + +bool ASTJsonConverter::visit(StructDefinition const& _node) +{ + addJsonNode("Struct", { make_pair("name", _node.getName()) }, true); + return true; +} + +bool ASTJsonConverter::visit(ParameterList const&) +{ + addJsonNode("ParameterList", {}, true); + return true; +} + +bool ASTJsonConverter::visit(FunctionDefinition const& _node) +{ + addJsonNode("Function", + { make_pair("name", _node.getName()), + make_pair("public", boost::lexical_cast(_node.isPublic())), + make_pair("const", boost::lexical_cast(_node.isDeclaredConst())) }, + true); + return true; +} + +bool ASTJsonConverter::visit(VariableDeclaration const& _node) +{ + addJsonNode("VariableDeclaration", + { make_pair("name", _node.getName()), + make_pair("local", boost::lexical_cast(_node.isLocalVariable()))}, + true); + return true; +} + +bool ASTJsonConverter::visit(TypeName const&) +{ + return true; +} + +bool ASTJsonConverter::visit(ElementaryTypeName const& _node) +{ + addJsonNode("ElementaryTypeName", { make_pair("name", Token::toString(_node.getTypeName())) }); + return true; +} + +bool ASTJsonConverter::visit(UserDefinedTypeName const& _node) +{ + addJsonNode("UserDefinedTypeName", { make_pair("name", _node.getName()) }); + return true; +} + +bool ASTJsonConverter::visit(Mapping const&) +{ + addJsonNode("Mapping", {}, true); + return true; +} + +bool ASTJsonConverter::visit(Statement const&) +{ + addJsonNode("Statement", {}, true); + return true; +} + +bool ASTJsonConverter::visit(Block const&) +{ + addJsonNode("Block", {}, true); + return true; +} + +bool ASTJsonConverter::visit(IfStatement const&) +{ + addJsonNode("IfStatement", {}, true); + return true; +} + +bool ASTJsonConverter::visit(BreakableStatement const&) +{ + return true; +} + +bool ASTJsonConverter::visit(WhileStatement const&) +{ + addJsonNode("WhileStatement", {}, true); + return true; +} + +bool ASTJsonConverter::visit(ForStatement const&) +{ + addJsonNode("ForStatement", {}, true); + return true; +} + +bool ASTJsonConverter::visit(Continue const&) +{ + addJsonNode("Continue", {}); + return true; +} + +bool ASTJsonConverter::visit(Break const&) +{ + addJsonNode("Break", {}); + return true; +} + +bool ASTJsonConverter::visit(Return const&) +{ + addJsonNode("Return", {}, true);; + return true; +} + +bool ASTJsonConverter::visit(VariableDefinition const&) +{ + addJsonNode("VariableDefinition", {}, true); + return true; +} + +bool ASTJsonConverter::visit(ExpressionStatement const&) +{ + addJsonNode("ExpressionStatement", {}, true); + return true; +} + +bool ASTJsonConverter::visit(Expression const& _node) +{ + addJsonNode("Expression", + { make_pair("type", getType(_node)), + make_pair("lvalue", boost::lexical_cast(_node.isLValue())), + make_pair("local_lvalue", boost::lexical_cast(_node.isLocalLValue())) }, + true); + return true; +} + +bool ASTJsonConverter::visit(Assignment const& _node) +{ + addJsonNode("Assignment", + { make_pair("operator", Token::toString(_node.getAssignmentOperator())), + make_pair("type", getType(_node)) }, + true); + return true; +} + +bool ASTJsonConverter::visit(UnaryOperation const& _node) +{ + addJsonNode("UnaryOperation", + { make_pair("prefix", boost::lexical_cast(_node.isPrefixOperation())), + make_pair("operator", Token::toString(_node.getOperator())), + make_pair("type", getType(_node)) }, + true); + return true; +} + +bool ASTJsonConverter::visit(BinaryOperation const& _node) +{ + addJsonNode("BinaryOperation", + { make_pair("operator", Token::toString(_node.getOperator())), + make_pair("type", getType(_node))}, + true); + return true; +} + +bool ASTJsonConverter::visit(FunctionCall const& _node) +{ + addJsonNode("FunctionCall", + { make_pair("type_conversion", boost::lexical_cast(_node.isTypeConversion())), + make_pair("type", getType(_node)) }, + true); + return true; +} + +bool ASTJsonConverter::visit(NewExpression const& _node) +{ + addJsonNode("NewExpression", { make_pair("type", getType(_node)) }, true); + return true; +} + +bool ASTJsonConverter::visit(MemberAccess const& _node) +{ + addJsonNode("MemberAccess", + { make_pair("member_name", _node.getMemberName()), + make_pair("type", getType(_node)) }, + true); + return true; +} + +bool ASTJsonConverter::visit(IndexAccess const& _node) +{ + addJsonNode("IndexAccess", { make_pair("type", getType(_node)) }, true); + return true; +} + +bool ASTJsonConverter::visit(PrimaryExpression const&) +{ + return true; +} + +bool ASTJsonConverter::visit(Identifier const& _node) +{ + addJsonNode("Identifier", + { make_pair("value", _node.getName()), make_pair("type", getType(_node)) }); + return true; +} + +bool ASTJsonConverter::visit(ElementaryTypeNameExpression const& _node) +{ + addJsonNode("ElementaryTypenameExpression", + { make_pair("value", Token::toString(_node.getTypeToken())), make_pair("type", getType(_node)) }); + return true; +} + +bool ASTJsonConverter::visit(Literal const& _node) +{ + char const* tokenString = Token::toString(_node.getToken()); + addJsonNode("Literal", + { make_pair("string", (tokenString) ? tokenString : "null"), + make_pair("value", _node.getValue()), + make_pair("type", getType(_node)) }); + return true; +} + +void ASTJsonConverter::endVisit(ImportDirective const&) +{ +} + +void ASTJsonConverter::endVisit(ContractDefinition const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(StructDefinition const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(ParameterList const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(FunctionDefinition const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(VariableDeclaration const&) +{ +} + +void ASTJsonConverter::endVisit(TypeName const&) +{ +} + +void ASTJsonConverter::endVisit(ElementaryTypeName const&) +{ +} + +void ASTJsonConverter::endVisit(UserDefinedTypeName const&) +{ +} + +void ASTJsonConverter::endVisit(Mapping const&) +{ +} + +void ASTJsonConverter::endVisit(Statement const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(Block const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(IfStatement const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(BreakableStatement const&) +{ +} + +void ASTJsonConverter::endVisit(WhileStatement const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(ForStatement const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(Continue const&) +{ +} + +void ASTJsonConverter::endVisit(Break const&) +{ +} + +void ASTJsonConverter::endVisit(Return const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(VariableDefinition const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(ExpressionStatement const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(Expression const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(Assignment const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(UnaryOperation const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(BinaryOperation const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(FunctionCall const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(NewExpression const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(MemberAccess const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(IndexAccess const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(PrimaryExpression const&) +{ +} + +void ASTJsonConverter::endVisit(Identifier const&) +{ +} + +void ASTJsonConverter::endVisit(ElementaryTypeNameExpression const&) +{ +} + +void ASTJsonConverter::endVisit(Literal const&) +{ +} + +string ASTJsonConverter::getType(Expression const& _expression) +{ + return (_expression.getType()) ? _expression.getType()->toString() : "Unknown"; +} + +} +} diff --git a/libsolidity/ASTJsonConverter.h b/libsolidity/ASTJsonConverter.h new file mode 100644 index 000000000..cb43c2d9e --- /dev/null +++ b/libsolidity/ASTJsonConverter.h @@ -0,0 +1,135 @@ +/* + 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 Lefteris + * @date 2015 + * Converts the AST into json format + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace dev +{ +namespace solidity +{ + +/** + * Converter of the AST into JSON format + */ +class ASTJsonConverter: public ASTConstVisitor +{ +public: + /// Create a converter to JSON for the given abstract syntax tree. + ASTJsonConverter(ASTNode const& _ast); + /// Output the json representation of the AST to _stream. + void print(std::ostream& _stream); + + bool visit(ImportDirective const& _node) override; + bool visit(ContractDefinition const& _node) override; + bool visit(StructDefinition const& _node) override; + bool visit(ParameterList const& _node) override; + bool visit(FunctionDefinition const& _node) override; + bool visit(VariableDeclaration const& _node) override; + bool visit(TypeName const& _node) override; + bool visit(ElementaryTypeName const& _node) override; + bool visit(UserDefinedTypeName const& _node) override; + bool visit(Mapping const& _node) override; + bool visit(Statement const& _node) override; + bool visit(Block const& _node) override; + bool visit(IfStatement const& _node) override; + bool visit(BreakableStatement const& _node) override; + bool visit(WhileStatement const& _node) override; + bool visit(ForStatement const& _node) override; + bool visit(Continue const& _node) override; + bool visit(Break const& _node) override; + bool visit(Return const& _node) override; + bool visit(VariableDefinition const& _node) override; + bool visit(ExpressionStatement const& _node) override; + bool visit(Expression const& _node) override; + bool visit(Assignment const& _node) override; + bool visit(UnaryOperation const& _node) override; + bool visit(BinaryOperation const& _node) override; + bool visit(FunctionCall const& _node) override; + bool visit(NewExpression const& _node) override; + bool visit(MemberAccess const& _node) override; + bool visit(IndexAccess const& _node) override; + bool visit(PrimaryExpression const& _node) override; + bool visit(Identifier const& _node) override; + bool visit(ElementaryTypeNameExpression const& _node) override; + bool visit(Literal const& _node) override; + + void endVisit(ImportDirective const&) override; + void endVisit(ContractDefinition const&) override; + void endVisit(StructDefinition const&) override; + void endVisit(ParameterList const&) override; + void endVisit(FunctionDefinition const&) override; + void endVisit(VariableDeclaration const&) override; + void endVisit(TypeName const&) override; + void endVisit(ElementaryTypeName const&) override; + void endVisit(UserDefinedTypeName const&) override; + void endVisit(Mapping const&) override; + void endVisit(Statement const&) override; + void endVisit(Block const&) override; + void endVisit(IfStatement const&) override; + void endVisit(BreakableStatement const&) override; + void endVisit(WhileStatement const&) override; + void endVisit(ForStatement const&) override; + void endVisit(Continue const&) override; + void endVisit(Break const&) override; + void endVisit(Return const&) override; + void endVisit(VariableDefinition const&) override; + void endVisit(ExpressionStatement const&) override; + void endVisit(Expression const&) override; + void endVisit(Assignment const&) override; + void endVisit(UnaryOperation const&) override; + void endVisit(BinaryOperation const&) override; + void endVisit(FunctionCall const&) override; + void endVisit(NewExpression const&) override; + void endVisit(MemberAccess const&) override; + void endVisit(IndexAccess const&) override; + void endVisit(PrimaryExpression const&) override; + void endVisit(Identifier const&) override; + void endVisit(ElementaryTypeNameExpression const&) override; + void endVisit(Literal const&) override; + +private: + void addKeyValue(Json::Value& _obj, std::string const& _key, std::string const& _val); + void addJsonNode(std::string const& _nodeName, + std::initializer_list> _list, + bool _hasChildren); + std::string getType(Expression const& _expression); + inline void goUp() + { + solAssert(!m_jsonNodePtrs.empty(), "Uneven json nodes stack. Internal error."); + m_jsonNodePtrs.pop(); + }; + + Json::Value m_astJson; + std::stack m_jsonNodePtrs; + std::string m_source; + ASTNode const* m_ast; +}; + +} +} diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 2b6e53cb8..b6a73a884 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -94,13 +94,8 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) m_context << eth::Instruction::NOT; break; case Token::DELETE: // delete - // @todo semantics change for complex types solAssert(m_currentLValue.isValid(), "LValue not retrieved."); - - m_context << u256(0); - if (m_currentLValue.storesReferenceOnStack()) - m_context << eth::Instruction::SWAP1; - m_currentLValue.storeValue(_unaryOperation); + m_currentLValue.setToZero(_unaryOperation); m_currentLValue.reset(); break; case Token::INC: // ++ (pre- or postfix) @@ -753,9 +748,14 @@ unsigned ExpressionCompiler::appendArgumentCopyToMemory(TypePointers const& _typ ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, unsigned _baseStackOffset): - m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset), - m_stackSize(_dataType.getSizeOnStack()) + m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset) { + //@todo change the type cast for arrays + solAssert(_dataType.getStorageSize() <= numeric_limits::max(), "The storage size of " +_dataType.toString() + " should fit in unsigned"); + if (m_type == STORAGE) + m_size = unsigned(_dataType.getStorageSize()); + else + m_size = unsigned(_dataType.getSizeOnStack()); } void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bool _remove) const @@ -768,7 +768,7 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) << errinfo_comment("Stack too deep.")); - for (unsigned i = 0; i < m_stackSize; ++i) + for (unsigned i = 0; i < m_size; ++i) *m_context << eth::dupInstruction(stackPos + 1); break; } @@ -777,14 +777,14 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo break; // no distinction between value and reference for non-value types if (!_remove) *m_context << eth::Instruction::DUP1; - if (m_stackSize == 1) + if (m_size == 1) *m_context << eth::Instruction::SLOAD; else - for (unsigned i = 0; i < m_stackSize; ++i) + for (unsigned i = 0; i < m_size; ++i) { *m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1; - if (i + 1 < m_stackSize) - *m_context << u256(1) << eth::Instruction::ADD; + if (i + 1 < m_size) + *m_context << u256(1) << eth::Instruction::ADD; else *m_context << eth::Instruction::POP; } @@ -808,12 +808,12 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool { case STACK: { - unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)) - m_stackSize + 1; + unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)) - m_size + 1; if (stackDiff > 16) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) << errinfo_comment("Stack too deep.")); else if (stackDiff > 0) - for (unsigned i = 0; i < m_stackSize; ++i) + for (unsigned i = 0; i < m_size; ++i) *m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP; if (!_move) retrieveValue(_expression); @@ -825,17 +825,17 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool // stack layout: value value ... value ref if (!_move) // copy values { - if (m_stackSize + 1 > 16) + if (m_size + 1 > 16) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) << errinfo_comment("Stack too deep.")); - for (unsigned i = 0; i < m_stackSize; ++i) - *m_context << eth::dupInstruction(m_stackSize + 1) << eth::Instruction::SWAP1; + for (unsigned i = 0; i < m_size; ++i) + *m_context << eth::dupInstruction(m_size + 1) << eth::Instruction::SWAP1; } - if (m_stackSize > 0) // store high index value first - *m_context << u256(m_stackSize - 1) << eth::Instruction::ADD; - for (unsigned i = 0; i < m_stackSize; ++i) + if (m_size > 0) // store high index value first + *m_context << u256(m_size - 1) << eth::Instruction::ADD; + for (unsigned i = 0; i < m_size; ++i) { - if (i + 1 >= m_stackSize) + if (i + 1 >= m_size) *m_context << eth::Instruction::SSTORE; else // v v ... v v r+x @@ -857,6 +857,48 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool } } +void ExpressionCompiler::LValue::setToZero(Expression const& _expression) const +{ + switch (m_type) + { + case STACK: + { + unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)); + if (stackDiff > 16) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) + << errinfo_comment("Stack too deep.")); + solAssert(stackDiff >= m_size - 1, ""); + for (unsigned i = 0; i < m_size; ++i) + *m_context << u256(0) << eth::swapInstruction(stackDiff + 1 - i) + << eth::Instruction::POP; + break; + } + case LValue::STORAGE: + if (m_size == 0) + *m_context << eth::Instruction::POP; + for (unsigned i = 0; i < m_size; ++i) + { + if (i + 1 >= m_size) + *m_context << u256(0) << eth::Instruction::SWAP1 << eth::Instruction::SSTORE; + else + *m_context << u256(0) << eth::Instruction::DUP2 << eth::Instruction::SSTORE + << u256(1) << eth::Instruction::ADD; + } + break; + case LValue::MEMORY: + if (!_expression.getType()->isValueType()) + break; // no distinction between value and reference for non-value types + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation()) + << errinfo_comment("Location type not yet implemented.")); + break; + default: + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation()) + << errinfo_comment("Unsupported location type.")); + break; + } + +} + void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression const& _expression) { if (!_expression.lvalueRequested()) @@ -868,15 +910,17 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression co void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration) { - m_stackSize = _identifier.getType()->getSizeOnStack(); if (m_context->isLocalVariable(&_declaration)) { m_type = STACK; + m_size = _identifier.getType()->getSizeOnStack(); m_baseStackOffset = m_context->getBaseStackOffsetOfVariable(_declaration); } else if (m_context->isStateVariable(&_declaration)) { m_type = STORAGE; + solAssert(_identifier.getType()->getStorageSize() <= numeric_limits::max(), "The storage size of " + _identifier.getType()->toString() + " should fit in unsigned"); + m_size = unsigned(_identifier.getType()->getStorageSize()); *m_context << m_context->getStorageLocationOfVariable(_declaration); } else diff --git a/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h index 4a696c39d..8f784fde9 100644 --- a/libsolidity/ExpressionCompiler.h +++ b/libsolidity/ExpressionCompiler.h @@ -111,7 +111,7 @@ private: /// Set type according to the declaration and retrieve the reference. /// @a _expression is the current expression void fromIdentifier(Identifier const& _identifier, Declaration const& _declaration); - void reset() { m_type = NONE; m_baseStackOffset = 0; } + void reset() { m_type = NONE; m_baseStackOffset = 0; m_size = 0; } bool isValid() const { return m_type != NONE; } bool isInOnStack() const { return m_type == STACK; } @@ -130,7 +130,9 @@ private: /// Also removes the stored value from the stack if @a _move is /// true. @a _expression is the current expression, used for error reporting. void storeValue(Expression const& _expression, bool _move = false) const; - + /// Stores zero in the lvalue. + /// @a _expression is the current expression, used for error reporting. + void setToZero(Expression const& _expression) const; /// Convenience function to convert the stored reference to a value and reset type to NONE if /// the reference was not requested by @a _expression. void retrieveValueIfLValueNotRequested(Expression const& _expression); @@ -141,8 +143,8 @@ private: /// If m_type is STACK, this is base stack offset (@see /// CompilerContext::getBaseStackOffsetOfVariable) of a local variable. unsigned m_baseStackOffset = 0; - /// Size of the value of this lvalue on the stack. - unsigned m_stackSize = 0; + /// Size of the value of this lvalue on the stack or the storage. + unsigned m_size = 0; }; bool m_optimize; diff --git a/libsolidity/Token.h b/libsolidity/Token.h index e5c61c7e4..9fb86e7f4 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -48,7 +48,8 @@ #include #if defined(DELETE) -#error The macro "DELETE" from windows.h conflicts with this file. Please change the order of includes. +#undef DELETE +//#error The macro "DELETE" from windows.h conflicts with this file. Please change the order of includes. #endif namespace dev diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index ae87a0886..a99e4853f 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -147,7 +147,7 @@ TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const { // "delete" is ok for all integer types if (_operator == Token::DELETE) - return shared_from_this(); + return make_shared(); // no further unary operators for addresses else if (isAddress()) return TypePointer(); @@ -408,6 +408,13 @@ u256 BoolType::literalValue(Literal const* _literal) const BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Bool type constructed from non-boolean literal.")); } +TypePointer BoolType::unaryOperatorResult(Token::Value _operator) const +{ + if (_operator == Token::DELETE) + return make_shared(); + return (_operator == Token::NOT) ? shared_from_this() : TypePointer(); +} + TypePointer BoolType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const { if (getCategory() != _other->getCategory()) @@ -432,6 +439,11 @@ bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const return isImplicitlyConvertibleTo(_convertTo) || _convertTo.getCategory() == Category::INTEGER; } +TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const +{ + return _operator == Token::DELETE ? make_shared() : TypePointer(); +} + bool ContractType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) @@ -440,14 +452,6 @@ bool ContractType::operator==(Type const& _other) const return other.m_contract == m_contract; } -u256 ContractType::getStorageSize() const -{ - u256 size = 0; - for (ASTPointer const& variable: m_contract.getStateVariables()) - size += variable->getType()->getStorageSize(); - return max(1, size); -} - string ContractType::toString() const { return "contract " + m_contract.getName(); @@ -491,6 +495,11 @@ u256 ContractType::getFunctionIdentifier(string const& _functionName) const return Invalid256; } +TypePointer StructType::unaryOperatorResult(Token::Value _operator) const +{ + return _operator == Token::DELETE ? make_shared() : TypePointer(); +} + bool StructType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 38e50acfa..cb8a8db0d 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -259,10 +259,7 @@ public: BoolType() {} virtual Category getCategory() const { return Category::BOOL; } virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override - { - return (_operator == Token::NOT || _operator == Token::DELETE) ? shared_from_this() : TypePointer(); - } + virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; virtual unsigned getCalldataEncodedSize() const { return 1; } @@ -284,8 +281,8 @@ public: virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; /// Contracts can be converted to themselves and to integers. virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual bool operator==(Type const& _other) const override; - virtual u256 getStorageSize() const override; virtual bool isValueType() const override { return true; } virtual std::string toString() const override; @@ -317,11 +314,7 @@ class StructType: public Type public: virtual Category getCategory() const override { return Category::STRUCT; } StructType(StructDefinition const& _struct): m_struct(_struct) {} - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override - { - return _operator == Token::DELETE ? shared_from_this() : TypePointer(); - } - + virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual bool operator==(Type const& _other) const override; virtual u256 getStorageSize() const override; virtual bool canLiveOutsideStorage() const override; diff --git a/neth/main.cpp b/neth/main.cpp index 1252b205b..638e9c624 100644 --- a/neth/main.cpp +++ b/neth/main.cpp @@ -379,13 +379,14 @@ int main(int argc, char** argv) mining = ~(unsigned)0; else if (isFalse(m)) mining = 0; - else if (int i = stoi(m)) - mining = i; else - { - cerr << "Unknown -m/--mining option: " << m << endl; - return -1; - } + try { + mining = stoi(m); + } + catch (...) { + cerr << "Unknown -m/--mining option: " << m << endl; + return -1; + } } else if (arg == "-b" || arg == "--bootstrap") bootstrap = true; diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 8c5192abe..3888f2314 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,7 @@ static string const g_argAbiStr = "json-abi"; static string const g_argSolAbiStr = "sol-abi"; static string const g_argAsmStr = "asm"; static string const g_argAstStr = "ast"; +static string const g_argAstJson = "ast-json"; static string const g_argBinaryStr = "binary"; static string const g_argOpcodesStr = "opcodes"; static string const g_argNatspecDevStr = "natspec-dev"; @@ -75,9 +77,10 @@ static inline bool argToStdout(po::variables_map const& _args, string const& _na static bool needStdout(po::variables_map const& _args) { + return argToStdout(_args, g_argAbiStr) || argToStdout(_args, g_argSolAbiStr) || - argToStdout(_args, g_argNatspecUserStr) || + argToStdout(_args, g_argNatspecUserStr) || argToStdout(_args, g_argAstJson) || argToStdout(_args, g_argNatspecDevStr) || argToStdout(_args, g_argAsmStr) || argToStdout(_args, g_argOpcodesStr) || argToStdout(_args, g_argBinaryStr); } @@ -215,6 +218,8 @@ bool CommandLineInterface::parseArguments(int argc, char** argv) ("input-file", po::value>(), "input file") (g_argAstStr.c_str(), po::value(), "Request to output the AST of the contract. " OUTPUT_TYPE_STR) + (g_argAstJson.c_str(), po::value(), + "Request to output the AST of the contract in JSON format. " OUTPUT_TYPE_STR) (g_argAsmStr.c_str(), po::value(), "Request to output the EVM assembly of the contract. " OUTPUT_TYPE_STR) (g_argOpcodesStr.c_str(), po::value(), @@ -339,20 +344,44 @@ bool CommandLineInterface::processInput() return true; } -void CommandLineInterface::actOnInput() +void CommandLineInterface::handleAst(string const& _argStr) { + string title; + string suffix; + + if (_argStr == g_argAstStr) + { + title = "Syntax trees:"; + suffix = ".ast"; + } + else if (_argStr == g_argAstJson) + { + title = "JSON AST:"; + suffix = ".json"; + } + else + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal argStr for AST")); + // do we need AST output? - if (m_args.count(g_argAstStr)) + if (m_args.count(_argStr)) { - auto choice = m_args[g_argAstStr].as(); + auto choice = m_args[_argStr].as(); if (outputToStdout(choice)) { - cout << "Syntax trees:" << endl << endl; + cout << title << endl << endl; for (auto const& sourceCode: m_sourceCodes) { cout << endl << "======= " << sourceCode.first << " =======" << endl; - ASTPrinter printer(m_compiler.getAST(sourceCode.first), sourceCode.second); - printer.print(cout); + if (_argStr == g_argAstStr) + { + ASTPrinter printer(m_compiler.getAST(sourceCode.first), sourceCode.second); + printer.print(cout); + } + else + { + ASTJsonConverter converter(m_compiler.getAST(sourceCode.first)); + converter.print(cout); + } } } @@ -362,12 +391,27 @@ void CommandLineInterface::actOnInput() { boost::filesystem::path p(sourceCode.first); ofstream outFile(p.stem().string() + ".ast"); - ASTPrinter printer(m_compiler.getAST(sourceCode.first), sourceCode.second); - printer.print(outFile); + if (_argStr == g_argAstStr) + { + ASTPrinter printer(m_compiler.getAST(sourceCode.first), sourceCode.second); + printer.print(outFile); + } + else + { + ASTJsonConverter converter(m_compiler.getAST(sourceCode.first)); + converter.print(outFile); + } outFile.close(); } } } +} + +void CommandLineInterface::actOnInput() +{ + // do we need AST output? + handleAst(g_argAstStr); + handleAst(g_argAstJson); vector contracts = m_compiler.getContractNames(); for (string const& contract: contracts) diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 9dfee7199..2862773ba 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -53,6 +53,7 @@ public: void actOnInput(); private: + void handleAst(std::string const& _argStr); void handleBinary(std::string const& _contract); void handleOpcode(std::string const& _contract); void handleBytecode(std::string const& _contract); diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index b79e9c4b7..dd57a1d15 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -772,6 +772,94 @@ BOOST_AUTO_TEST_CASE(struct_reference) BOOST_CHECK(callContractFunction("check()") == encodeArgs(true)); } +BOOST_AUTO_TEST_CASE(deleteStruct) +{ + char const* sourceCode = R"( + contract test { + struct topStruct { + nestedStruct nstr; + emptyStruct empty; + uint topValue; + mapping (uint => uint) topMapping; + } + uint toDelete; + topStruct str; + struct nestedStruct { + uint nestedValue; + mapping (uint => bool) nestedMapping; + } + struct emptyStruct{ + } + function test(){ + toDelete = 5; + str.topValue = 1; + str.topMapping[0] = 1; + str.topMapping[1] = 2; + + str.nstr.nestedValue = 2; + str.nstr.nestedMapping[0] = true; + str.nstr.nestedMapping[1] = false; + delete str; + delete toDelete; + } + function getToDelete() returns (uint res){ + res = toDelete; + } + function getTopValue() returns(uint topValue){ + topValue = str.topValue; + } + function getNestedValue() returns(uint nestedValue){ + nestedValue = str.nstr.nestedValue; + } + function getTopMapping(uint index) returns(uint ret) { + ret = str.topMapping[index]; + } + function getNestedMapping(uint index) returns(bool ret) { + return str.nstr.nestedMapping[index]; + } + })"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("getToDelete()") == encodeArgs(0)); + BOOST_CHECK(callContractFunction("getTopValue()") == encodeArgs(0)); + BOOST_CHECK(callContractFunction("getNestedValue()") == encodeArgs(0)); + // mapping values should be the same + BOOST_CHECK(callContractFunction("getTopMapping(uint256)", 0) == encodeArgs(1)); + BOOST_CHECK(callContractFunction("getTopMapping(uint256)", 1) == encodeArgs(2)); + BOOST_CHECK(callContractFunction("getNestedMapping(uint256)", 0) == encodeArgs(true)); + BOOST_CHECK(callContractFunction("getNestedMapping(uint256)", 1) == encodeArgs(false)); +} + +BOOST_AUTO_TEST_CASE(deleteLocal) +{ + char const* sourceCode = R"( + contract test { + function delLocal() returns (uint res){ + uint v = 5; + delete v; + res = v; + } + })"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("delLocal()") == encodeArgs(0)); +} + +BOOST_AUTO_TEST_CASE(deleteLocals) +{ + char const* sourceCode = R"( + contract test { + function delLocal() returns (uint res1, uint res2){ + uint v = 5; + uint w = 6; + uint x = 7; + delete v; + res1 = w; + res2 = x; + } + })"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("delLocal()") == encodeArgs(6, 7)); +} + BOOST_AUTO_TEST_CASE(constructor) { char const* sourceCode = "contract test {\n" @@ -1243,6 +1331,7 @@ BOOST_AUTO_TEST_CASE(constructor_arguments) contract Helper { string3 name; bool flag; + function Helper(string3 x, bool f) { name = x; flag = f;