Browse Source

Merge remote-tracking branch 'up/develop' into bugFix

cl-refactor
yann300 10 years ago
parent
commit
0802757f72
  1. 2
      evmjit/CMakeLists.txt
  2. 4
      evmjit/include/evmjit/JIT.h
  3. 2
      evmjit/libevmjit/ExecutionEngine.cpp
  4. 8
      evmjit/libevmjit/JIT.cpp
  5. 2
      libethash-cl/ethash_cl_miner.cpp
  6. 4
      libevm/VM.cpp
  7. 1
      libevmasm/Assembly.cpp
  8. 6
      libevmasm/AssemblyItem.h
  9. 126
      libevmasm/GasMeter.cpp
  10. 27
      libevmasm/GasMeter.h
  11. 9
      libevmasm/KnownState.cpp
  12. 4
      libevmasm/KnownState.h
  13. 20
      libsolidity/ASTVisitor.h
  14. 38
      libsolidity/CompilerStack.cpp
  15. 5
      libsolidity/CompilerStack.h
  16. 18
      libsolidity/InterfaceHandler.cpp
  17. 39
      libsolidity/StructuralGasEstimator.cpp
  18. 4
      libsolidity/StructuralGasEstimator.h
  19. 9
      test/TestHelper.cpp
  20. 4
      test/libethereum/StateTestsFiller/stMemoryStressTestFiller.json
  21. 2
      test/libethereum/state.cpp
  22. 2
      test/libevm/vm.cpp
  23. 60
      test/libsolidity/GasMeter.cpp
  24. 86
      test/libsolidity/SolidityEndToEndTest.cpp
  25. 11
      test/libsolidity/solidityExecutionFramework.h

2
evmjit/CMakeLists.txt

@ -7,7 +7,7 @@ set(CMAKE_AUTOMOC OFF)
if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
else() else()
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra -Wconversion -Wno-sign-conversion -Wno-unknown-pragmas") set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra -Wconversion -Wno-sign-conversion -Wno-unknown-pragmas ${CMAKE_CXX_FLAGS}")
endif() endif()
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")

4
evmjit/include/evmjit/JIT.h

@ -28,8 +28,8 @@ public:
private: private:
friend class dev::eth::jit::ExecutionEngine; friend class dev::eth::jit::ExecutionEngine;
static void* getCode(h256 _codeHash); static uint64_t getCode(h256 _codeHash);
static void mapCode(h256 _codeHash, void* _funcAddr); static void mapCode(h256 _codeHash, uint64_t _funcAddr);
}; };
} }

2
evmjit/libevmjit/ExecutionEngine.cpp

@ -183,7 +183,7 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName); entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName);
if (!CHECK(entryFuncPtr)) if (!CHECK(entryFuncPtr))
return ReturnCode::LLVMLinkError; return ReturnCode::LLVMLinkError;
JIT::mapCode(_data->codeHash, (void*)entryFuncPtr); // FIXME: Remove cast JIT::mapCode(_data->codeHash, (uint64_t)entryFuncPtr); // FIXME: Remove cast
} }
listener->stateChanged(ExecState::Execution); listener->stateChanged(ExecState::Execution);

8
evmjit/libevmjit/JIT.cpp

@ -12,7 +12,7 @@ namespace
class JITImpl: JIT class JITImpl: JIT
{ {
public: public:
std::unordered_map<h256, void*> codeMap; std::unordered_map<h256, uint64_t> codeMap;
static JITImpl& instance() static JITImpl& instance()
{ {
@ -28,16 +28,16 @@ bool JIT::isCodeReady(h256 _codeHash)
return JITImpl::instance().codeMap.count(_codeHash) != 0; return JITImpl::instance().codeMap.count(_codeHash) != 0;
} }
void* JIT::getCode(h256 _codeHash) uint64_t JIT::getCode(h256 _codeHash)
{ {
auto& codeMap = JITImpl::instance().codeMap; auto& codeMap = JITImpl::instance().codeMap;
auto it = codeMap.find(_codeHash); auto it = codeMap.find(_codeHash);
if (it != codeMap.end()) if (it != codeMap.end())
return it->second; return it->second;
return nullptr; return 0;
} }
void JIT::mapCode(h256 _codeHash, void* _funcAddr) void JIT::mapCode(h256 _codeHash, uint64_t _funcAddr)
{ {
JITImpl::instance().codeMap.insert(std::make_pair(_codeHash, _funcAddr)); JITImpl::instance().codeMap.insert(std::make_pair(_codeHash, _funcAddr));
} }

2
libethash-cl/ethash_cl_miner.cpp

@ -304,7 +304,7 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
}; };
std::queue<pending_batch> pending; std::queue<pending_batch> pending;
static uint32_t const c_zero = 0; uint32_t const c_zero = 0;
// update header constant buffer // update header constant buffer
m_queue.enqueueWriteBuffer(m_header, false, 0, 32, header); m_queue.enqueueWriteBuffer(m_header, false, 0, 32, header);

4
libevm/VM.cpp

@ -635,7 +635,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
if (m_stack[m_stack.size() - 3] > 0) if (m_stack[m_stack.size() - 3] > 0)
callParams->gas += c_callStipend; callParams->gas += c_callStipend;
m_stack.pop_back(); m_stack.pop_back();
callParams->receiveAddress = asAddress(m_stack.back()); callParams->codeAddress = asAddress(m_stack.back());
m_stack.pop_back(); m_stack.pop_back();
callParams->value = m_stack.back(); callParams->value = m_stack.back();
m_stack.pop_back(); m_stack.pop_back();
@ -653,7 +653,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
{ {
callParams->onOp = _onOp; callParams->onOp = _onOp;
callParams->senderAddress = _ext.myAddress; callParams->senderAddress = _ext.myAddress;
callParams->codeAddress = inst == Instruction::CALL ? callParams->receiveAddress : callParams->senderAddress; callParams->receiveAddress = inst == Instruction::CALL ? callParams->codeAddress : callParams->senderAddress;
callParams->data = bytesConstRef(m_temp.data() + inOff, inSize); callParams->data = bytesConstRef(m_temp.data() + inOff, inSize);
callParams->out = bytesRef(m_temp.data() + outOff, outSize); callParams->out = bytesRef(m_temp.data() + outOff, outSize);
m_stack.push_back(_ext.call(*callParams)); m_stack.push_back(_ext.call(*callParams));

1
libevmasm/Assembly.cpp

@ -431,6 +431,7 @@ bytes Assembly::assemble() const
case PushSubSize: case PushSubSize:
{ {
auto s = m_data[i.data()].size(); auto s = m_data[i.data()].size();
i.setPushedValue(u256(s));
byte b = max<unsigned>(1, dev::bytesRequired(s)); byte b = max<unsigned>(1, dev::bytesRequired(s));
ret.push_back((byte)Instruction::PUSH1 - 1 + b); ret.push_back((byte)Instruction::PUSH1 - 1 + b);
ret.resize(ret.size() + b); ret.resize(ret.size() + b);

6
libevmasm/AssemblyItem.h

@ -84,11 +84,17 @@ public:
JumpType getJumpType() const { return m_jumpType; } JumpType getJumpType() const { return m_jumpType; }
std::string getJumpTypeAsString() const; std::string getJumpTypeAsString() const;
void setPushedValue(u256 const& _value) const { m_pushedValue = std::make_shared<u256>(_value); }
u256 const* pushedValue() const { return m_pushedValue.get(); }
private: private:
AssemblyItemType m_type; AssemblyItemType m_type;
u256 m_data; u256 m_data;
SourceLocation m_location; SourceLocation m_location;
JumpType m_jumpType = JumpType::Ordinary; JumpType m_jumpType = JumpType::Ordinary;
/// Pushed value for operations with data to be determined during assembly stage,
/// e.g. PushSubSize, PushTag, PushSub, etc.
mutable std::shared_ptr<u256> m_pushedValue;
}; };
using AssemblyItems = std::vector<AssemblyItem>; using AssemblyItems = std::vector<AssemblyItem>;

126
libevmasm/GasMeter.cpp

@ -20,6 +20,7 @@
*/ */
#include "GasMeter.h" #include "GasMeter.h"
#include <libevmasm/KnownState.h>
#include <libevmcore/Params.h> #include <libevmcore/Params.h>
using namespace std; using namespace std;
@ -41,55 +42,162 @@ GasMeter::GasConsumption& GasMeter::GasConsumption::operator+=(GasConsumption co
GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item) GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item)
{ {
switch (_item.type()) { GasConsumption gas;
switch (_item.type())
{
case Push: case Push:
case PushTag: case PushTag:
return runGas(Instruction::PUSH1); case PushData:
case PushString:
case PushSub:
case PushSubSize:
case PushProgramSize:
gas = runGas(Instruction::PUSH1);
break;
case Tag: case Tag:
return runGas(Instruction::JUMPDEST); gas = runGas(Instruction::JUMPDEST);
break;
case Operation: case Operation:
{ {
GasConsumption gas = runGas(_item.instruction()); ExpressionClasses& classes = m_state->expressionClasses();
gas = runGas(_item.instruction());
switch (_item.instruction()) switch (_item.instruction())
{ {
case Instruction::SSTORE: case Instruction::SSTORE:
// @todo logic can be improved {
ExpressionClasses::Id slot = m_state->relativeStackElement(0);
ExpressionClasses::Id value = m_state->relativeStackElement(-1);
if (classes.knownZero(value) || (
m_state->storageContent().count(slot) &&
classes.knownNonZero(m_state->storageContent().at(slot))
))
gas += c_sstoreResetGas; //@todo take refunds into account
else
gas += c_sstoreSetGas; gas += c_sstoreSetGas;
break; break;
}
case Instruction::SLOAD: case Instruction::SLOAD:
gas += c_sloadGas; gas += c_sloadGas;
break; break;
case Instruction::RETURN:
gas += memoryGas(0, -1);
break;
case Instruction::MLOAD:
case Instruction::MSTORE: case Instruction::MSTORE:
gas += memoryGas(classes.find(eth::Instruction::ADD, {
m_state->relativeStackElement(0),
classes.find(AssemblyItem(32))
}));
break;
case Instruction::MSTORE8: case Instruction::MSTORE8:
case Instruction::MLOAD: gas += memoryGas(classes.find(eth::Instruction::ADD, {
case Instruction::RETURN: m_state->relativeStackElement(0),
classes.find(AssemblyItem(1))
}));
break;
case Instruction::SHA3: case Instruction::SHA3:
gas = c_sha3Gas;
gas += wordGas(c_sha3WordGas, m_state->relativeStackElement(-1));
gas += memoryGas(0, -1);
break;
case Instruction::CALLDATACOPY: case Instruction::CALLDATACOPY:
case Instruction::CODECOPY: case Instruction::CODECOPY:
gas += memoryGas(0, -2);
gas += wordGas(c_copyGas, m_state->relativeStackElement(-2));
break;
case Instruction::EXTCODECOPY: case Instruction::EXTCODECOPY:
gas += memoryGas(-1, -3);
gas += wordGas(c_copyGas, m_state->relativeStackElement(-3));
break;
case Instruction::LOG0: case Instruction::LOG0:
case Instruction::LOG1: case Instruction::LOG1:
case Instruction::LOG2: case Instruction::LOG2:
case Instruction::LOG3: case Instruction::LOG3:
case Instruction::LOG4: case Instruction::LOG4:
{
unsigned n = unsigned(_item.instruction()) - unsigned(Instruction::LOG0);
gas = c_logGas + c_logTopicGas * n;
gas += memoryGas(0, -1);
if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1)))
gas += c_logDataGas * (*value);
else
gas = GasConsumption::infinite();
break;
}
case Instruction::CALL: case Instruction::CALL:
case Instruction::CALLCODE: case Instruction::CALLCODE:
gas = c_callGas;
if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(0)))
gas += (*value);
else
gas = GasConsumption::infinite();
if (_item.instruction() != Instruction::CALLCODE)
gas += c_callNewAccountGas; // We very rarely know whether the address exists.
if (!classes.knownZero(m_state->relativeStackElement(-2)))
gas += c_callValueTransferGas;
gas += memoryGas(-3, -4);
gas += memoryGas(-5, -6);
break;
case Instruction::CREATE: case Instruction::CREATE:
gas = c_createGas;
gas += memoryGas(-1, -2);
break;
case Instruction::EXP: case Instruction::EXP:
// @todo logic can be improved gas = c_expGas;
if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1)))
gas += c_expByteGas * (32 - (h256(*value).firstBitSet() / 8));
else
gas = GasConsumption::infinite(); gas = GasConsumption::infinite();
break; break;
default: default:
break; break;
} }
return gas;
break; break;
} }
default: default:
gas = GasConsumption::infinite();
break; break;
} }
m_state->feedItem(_item);
return gas;
}
GasMeter::GasConsumption GasMeter::wordGas(u256 const& _multiplier, ExpressionClasses::Id _position)
{
u256 const* value = m_state->expressionClasses().knownConstant(_position);
if (!value)
return GasConsumption::infinite();
return GasConsumption(_multiplier * ((*value + 31) / 32));
}
GasMeter::GasConsumption GasMeter::memoryGas(ExpressionClasses::Id _position)
{
u256 const* value = m_state->expressionClasses().knownConstant(_position);
if (!value)
return GasConsumption::infinite(); return GasConsumption::infinite();
if (*value < m_largestMemoryAccess)
return GasConsumption(u256(0));
u256 previous = m_largestMemoryAccess;
m_largestMemoryAccess = *value;
auto memGas = [](u256 const& pos) -> u256
{
u256 size = (pos + 31) / 32;
return c_memoryGas * size + size * size / c_quadCoeffDiv;
};
return memGas(*value) - memGas(previous);
}
GasMeter::GasConsumption GasMeter::memoryGas(int _stackPosOffset, int _stackPosSize)
{
ExpressionClasses& classes = m_state->expressionClasses();
if (classes.knownZero(m_state->relativeStackElement(_stackPosSize)))
return GasConsumption(0);
else
return memoryGas(classes.find(eth::Instruction::ADD, {
m_state->relativeStackElement(_stackPosOffset),
m_state->relativeStackElement(_stackPosSize)
}));
} }
GasMeter::GasConsumption GasMeter::runGas(Instruction _instruction) GasMeter::GasConsumption GasMeter::runGas(Instruction _instruction)

27
libevmasm/GasMeter.h

@ -22,6 +22,7 @@
#pragma once #pragma once
#include <ostream> #include <ostream>
#include <libevmasm/ExpressionClasses.h>
#include <libevmasm/AssemblyItem.h> #include <libevmasm/AssemblyItem.h>
namespace dev namespace dev
@ -29,8 +30,13 @@ namespace dev
namespace eth namespace eth
{ {
class KnownState;
/** /**
* Class that helps computing the maximum gas consumption for instructions. * Class that helps computing the maximum gas consumption for instructions.
* Has to be initialized with a certain known state that will be automatically updated for
* each call to estimateMax. These calls have to supply strictly subsequent AssemblyItems.
* A new gas meter has to be constructed (with a new state) for control flow changes.
*/ */
class GasMeter class GasMeter
{ {
@ -47,11 +53,28 @@ public:
bool isInfinite; bool isInfinite;
}; };
/// Returns an upper bound on the gas consumed by the given instruction. /// Constructs a new gas meter given the current state.
GasMeter(std::shared_ptr<KnownState> const& _state): m_state(_state) {}
/// @returns an upper bound on the gas consumed by the given instruction and updates
/// the state.
GasConsumption estimateMax(AssemblyItem const& _item); GasConsumption estimateMax(AssemblyItem const& _item);
private: private:
/// @returns _multiplier * (_value + 31) / 32, if _value is a known constant and infinite otherwise.
GasConsumption wordGas(u256 const& _multiplier, ExpressionClasses::Id _value);
/// @returns the gas needed to access the given memory position.
/// @todo this assumes that memory was never accessed before and thus over-estimates gas usage.
GasConsumption memoryGas(ExpressionClasses::Id _position);
/// @returns the memory gas for accessing the memory at a specific offset for a number of bytes
/// given as values on the stack at the given relative positions.
GasConsumption memoryGas(int _stackPosOffset, int _stackPosSize);
static GasConsumption runGas(Instruction _instruction); static GasConsumption runGas(Instruction _instruction);
std::shared_ptr<KnownState> m_state;
/// Largest point where memory was accessed since the creation of this object.
u256 m_largestMemoryAccess;
}; };
inline std::ostream& operator<<(std::ostream& _str, GasMeter::GasConsumption const& _consumption) inline std::ostream& operator<<(std::ostream& _str, GasMeter::GasConsumption const& _consumption)
@ -59,7 +82,7 @@ inline std::ostream& operator<<(std::ostream& _str, GasMeter::GasConsumption con
if (_consumption.isInfinite) if (_consumption.isInfinite)
return _str << "inf"; return _str << "inf";
else else
return _str << _consumption.value; return _str << std::dec << _consumption.value;
} }

9
libevmasm/KnownState.cpp

@ -92,6 +92,10 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool
else if (_item.type() != Operation) else if (_item.type() != Operation)
{ {
assertThrow(_item.deposit() == 1, InvalidDeposit, ""); assertThrow(_item.deposit() == 1, InvalidDeposit, "");
if (_item.pushedValue())
// only available after assembly stage, should not be used for optimisation
setStackElement(++m_stackHeight, m_expressionClasses->find(*_item.pushedValue()));
else
setStackElement(++m_stackHeight, m_expressionClasses->find(_item, {}, _copyItem)); setStackElement(++m_stackHeight, m_expressionClasses->find(_item, {}, _copyItem));
} }
else else
@ -233,6 +237,11 @@ ExpressionClasses::Id KnownState::stackElement(int _stackHeight, SourceLocation
m_expressionClasses->find(AssemblyItem(UndefinedItem, _stackHeight, _location)); m_expressionClasses->find(AssemblyItem(UndefinedItem, _stackHeight, _location));
} }
KnownState::Id KnownState::relativeStackElement(int _stackOffset, SourceLocation const& _location)
{
return stackElement(m_stackHeight + _stackOffset, _location);
}
void KnownState::clearTagUnions() void KnownState::clearTagUnions()
{ {
for (auto it = m_stackElements.begin(); it != m_stackElements.end();) for (auto it = m_stackElements.begin(); it != m_stackElements.end();)

4
libevmasm/KnownState.h

@ -111,6 +111,8 @@ public:
/// Retrieves the current equivalence class fo the given stack element (or generates a new /// Retrieves the current equivalence class fo the given stack element (or generates a new
/// one if it does not exist yet). /// one if it does not exist yet).
Id stackElement(int _stackHeight, SourceLocation const& _location); Id stackElement(int _stackHeight, SourceLocation const& _location);
/// @returns the stackElement relative to the current stack height.
Id relativeStackElement(int _stackOffset, SourceLocation const& _location = SourceLocation());
/// @returns its set of tags if the given expression class is a known tag union; returns a set /// @returns its set of tags if the given expression class is a known tag union; returns a set
/// containing the tag if it is a PushTag expression and the empty set otherwise. /// containing the tag if it is a PushTag expression and the empty set otherwise.
@ -123,6 +125,8 @@ public:
std::map<int, Id> const& stackElements() const { return m_stackElements; } std::map<int, Id> const& stackElements() const { return m_stackElements; }
ExpressionClasses& expressionClasses() const { return *m_expressionClasses; } ExpressionClasses& expressionClasses() const { return *m_expressionClasses; }
std::map<Id, Id> const& storageContent() const { return m_storageContent; }
private: private:
/// Assigns a new equivalence class to the next sequence number of the given stack element. /// Assigns a new equivalence class to the next sequence number of the given stack element.
void setStackElement(int _stackHeight, Id _class); void setStackElement(int _stackHeight, Id _class);

20
libsolidity/ASTVisitor.h

@ -220,6 +220,26 @@ protected:
virtual void endVisitNode(ASTNode const&) { } virtual void endVisitNode(ASTNode const&) { }
}; };
/**
* Utility class that accepts std::functions and calls them for visitNode and endVisitNode.
*/
class SimpleASTVisitor: public ASTConstVisitor
{
public:
SimpleASTVisitor(
std::function<bool(ASTNode const&)> _onVisit,
std::function<void(ASTNode const&)> _onEndVisit
): m_onVisit(_onVisit), m_onEndVisit(_onEndVisit) {}
protected:
virtual bool visitNode(ASTNode const& _n) override { return m_onVisit ? m_onVisit(_n) : true; }
virtual void endVisitNode(ASTNode const& _n) override { m_onEndVisit(_n); }
private:
std::function<bool(ASTNode const&)> m_onVisit;
std::function<void(ASTNode const&)> m_onEndVisit;
};
/** /**
* Utility class that visits the AST in depth-first order and calls a function on each node and each edge. * Utility class that visits the AST in depth-first order and calls a function on each node and each edge.
* Child nodes are only visited if the node callback of the parent returns true. * Child nodes are only visited if the node callback of the parent returns true.

38
libsolidity/CompilerStack.cpp

@ -55,12 +55,29 @@ const map<string, string> StandardSources = map<string, string>{
}; };
CompilerStack::CompilerStack(bool _addStandardSources): CompilerStack::CompilerStack(bool _addStandardSources):
m_addStandardSources(_addStandardSources), m_parseSuccessful(false) m_parseSuccessful(false)
{ {
if (m_addStandardSources) if (_addStandardSources)
addSources(StandardSources, true); // add them as libraries addSources(StandardSources, true); // add them as libraries
} }
void CompilerStack::reset(bool _keepSources, bool _addStandardSources)
{
m_parseSuccessful = false;
if (_keepSources)
for (auto sourcePair: m_sources)
sourcePair.second.reset();
else
{
m_sources.clear();
if (_addStandardSources)
addSources(StandardSources, true);
}
m_globalContext.reset();
m_sourceOrder.clear();
m_contracts.clear();
}
bool CompilerStack::addSource(string const& _name, string const& _content, bool _isLibrary) bool CompilerStack::addSource(string const& _name, string const& _content, bool _isLibrary)
{ {
bool existed = m_sources.count(_name) != 0; bool existed = m_sources.count(_name) != 0;
@ -269,23 +286,6 @@ tuple<int, int, int, int> CompilerStack::positionFromSourceLocation(SourceLocati
return make_tuple(++startLine, ++startColumn, ++endLine, ++endColumn); return make_tuple(++startLine, ++startColumn, ++endLine, ++endColumn);
} }
void CompilerStack::reset(bool _keepSources)
{
m_parseSuccessful = false;
if (_keepSources)
for (auto sourcePair: m_sources)
sourcePair.second.reset();
else
{
m_sources.clear();
if (m_addStandardSources)
addSources(StandardSources, true);
}
m_globalContext.reset();
m_sourceOrder.clear();
m_contracts.clear();
}
void CompilerStack::resolveImports() void CompilerStack::resolveImports()
{ {
// topological sorting (depth first search) of the import graph, cutting potential cycles // topological sorting (depth first search) of the import graph, cutting potential cycles

5
libsolidity/CompilerStack.h

@ -72,6 +72,9 @@ public:
/// Creates a new compiler stack. Adds standard sources if @a _addStandardSources. /// Creates a new compiler stack. Adds standard sources if @a _addStandardSources.
explicit CompilerStack(bool _addStandardSources = true); explicit CompilerStack(bool _addStandardSources = true);
/// Resets the compiler to a state where the sources are not parsed or even removed.
void reset(bool _keepSources = false, bool _addStandardSources = true);
/// Adds a source object (e.g. file) to the parser. After this, parse has to be called again. /// Adds a source object (e.g. file) to the parser. After this, parse has to be called again.
/// @returns true if a source object by the name already existed and was replaced. /// @returns true if a source object by the name already existed and was replaced.
void addSources(StringMap const& _nameContents, bool _isLibrary = false) { for (auto const& i: _nameContents) addSource(i.first, i.second, _isLibrary); } void addSources(StringMap const& _nameContents, bool _isLibrary = false) { for (auto const& i: _nameContents) addSource(i.first, i.second, _isLibrary); }
@ -165,13 +168,11 @@ private:
Contract(); Contract();
}; };
void reset(bool _keepSources = false);
void resolveImports(); void resolveImports();
Contract const& getContract(std::string const& _contractName = "") const; Contract const& getContract(std::string const& _contractName = "") const;
Source const& getSource(std::string const& _sourceName = "") const; Source const& getSource(std::string const& _sourceName = "") const;
bool m_addStandardSources; ///< If true, standard sources are added.
bool m_parseSuccessful; bool m_parseSuccessful;
std::map<std::string const, Source> m_sources; std::map<std::string const, Source> m_sources;
std::shared_ptr<GlobalContext> m_globalContext; std::shared_ptr<GlobalContext> m_globalContext;

18
libsolidity/InterfaceHandler.cpp

@ -107,10 +107,8 @@ std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinitio
unique_ptr<string> InterfaceHandler::getABISolidityInterface(ContractDefinition const& _contractDef) unique_ptr<string> InterfaceHandler::getABISolidityInterface(ContractDefinition const& _contractDef)
{ {
string ret = "contract " + _contractDef.getName() + "{"; string ret = "contract " + _contractDef.getName() + "{";
for (auto const& it: _contractDef.getInterfaceFunctions())
{ auto populateParameters = [](vector<string> const& _paramNames, vector<string> const& _paramTypes)
auto populateParameters = [](vector<string> const& _paramNames,
vector<string> const& _paramTypes)
{ {
string r = ""; string r = "";
solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match"); solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match");
@ -118,6 +116,18 @@ unique_ptr<string> InterfaceHandler::getABISolidityInterface(ContractDefinition
r += (r.size() ? "," : "(") + _paramTypes[i] + " " + _paramNames[i]; r += (r.size() ? "," : "(") + _paramTypes[i] + " " + _paramNames[i];
return r.size() ? r + ")" : "()"; return r.size() ? r + ")" : "()";
}; };
if (_contractDef.getConstructor())
{
auto externalFunction = FunctionType(*_contractDef.getConstructor()).externalFunctionType();
solAssert(!!externalFunction, "");
ret +=
"function " +
_contractDef.getName() +
populateParameters(externalFunction->getParameterNames(), externalFunction->getParameterTypeNames()) +
";";
}
for (auto const& it: _contractDef.getInterfaceFunctions())
{
ret += "function " + it.second->getDeclaration().getName() + ret += "function " + it.second->getDeclaration().getName() +
populateParameters(it.second->getParameterNames(), it.second->getParameterTypeNames()) + populateParameters(it.second->getParameterNames(), it.second->getParameterTypeNames()) +
(it.second->isConstant() ? "constant " : ""); (it.second->isConstant() ? "constant " : "");

39
libsolidity/StructuralGasEstimator.cpp

@ -23,6 +23,9 @@
#include "StructuralGasEstimator.h" #include "StructuralGasEstimator.h"
#include <map> #include <map>
#include <functional> #include <functional>
#include <memory>
#include <libevmasm/ControlFlowGraph.h>
#include <libevmasm/KnownState.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/ASTVisitor.h> #include <libsolidity/ASTVisitor.h>
@ -38,14 +41,23 @@ StructuralGasEstimator::ASTGasConsumptionSelfAccumulated StructuralGasEstimator:
{ {
solAssert(std::count(_ast.begin(), _ast.end(), nullptr) == 0, ""); solAssert(std::count(_ast.begin(), _ast.end(), nullptr) == 0, "");
map<SourceLocation, GasMeter::GasConsumption> particularCosts; map<SourceLocation, GasMeter::GasConsumption> particularCosts;
GasMeter meter;
for (auto const& item: _items) ControlFlowGraph cfg(_items);
particularCosts[item.getLocation()] += meter.estimateMax(item); for (BasicBlock const& block: cfg.optimisedBlocks())
{
assertThrow(!!block.startState, OptimizerException, "");
GasMeter meter(block.startState->copy());
auto const end = _items.begin() + block.end;
for (auto iter = _items.begin() + block.begin; iter != end; ++iter)
particularCosts[iter->getLocation()] += meter.estimateMax(*iter);
}
set<ASTNode const*> finestNodes = finestNodesAtLocation(_ast);
ASTGasConsumptionSelfAccumulated gasCosts; ASTGasConsumptionSelfAccumulated gasCosts;
auto onNode = [&](ASTNode const& _node) auto onNode = [&](ASTNode const& _node)
{ {
if (!finestNodes.count(&_node))
return true;
gasCosts[&_node][0] = gasCosts[&_node][1] = particularCosts[_node.getLocation()]; gasCosts[&_node][0] = gasCosts[&_node][1] = particularCosts[_node.getLocation()];
return true; return true;
}; };
@ -108,3 +120,24 @@ map<ASTNode const*, GasMeter::GasConsumption> StructuralGasEstimator::breakToSta
// gasCosts should only contain non-overlapping locations // gasCosts should only contain non-overlapping locations
return gasCosts; return gasCosts;
} }
set<ASTNode const*> StructuralGasEstimator::finestNodesAtLocation(
vector<ASTNode const*> const& _roots
)
{
map<SourceLocation, ASTNode const*> locations;
set<ASTNode const*> nodes;
SimpleASTVisitor visitor(function<bool(ASTNode const&)>(), [&](ASTNode const& _n)
{
if (!locations.count(_n.getLocation()))
{
locations[_n.getLocation()] = &_n;
nodes.insert(&_n);
}
});
for (ASTNode const* root: _roots)
root->accept(visitor);
return nodes;
}

4
libsolidity/StructuralGasEstimator.h

@ -56,6 +56,10 @@ public:
ASTGasConsumptionSelfAccumulated const& _gasCosts, ASTGasConsumptionSelfAccumulated const& _gasCosts,
std::vector<ASTNode const*> const& _roots std::vector<ASTNode const*> const& _roots
); );
private:
/// @returns the set of AST nodes which are the finest nodes at their location.
std::set<ASTNode const*> finestNodesAtLocation(std::vector<ASTNode const*> const& _roots);
}; };
} }

9
test/TestHelper.cpp

@ -327,7 +327,8 @@ void ImportTest::checkExpectedState(State const& _stateExpect, State const& _sta
void ImportTest::exportTest(bytes const& _output, State const& _statePost) void ImportTest::exportTest(bytes const& _output, State const& _statePost)
{ {
// export output // export output
m_TestObject["out"] = toHex(_output, 2, HexPrefix::Add);
m_TestObject["out"] = _output.size() > 4096 ? "#" + toString(_output.size()) : toHex(_output, 2, HexPrefix::Add);
// export logs // export logs
m_TestObject["logs"] = exportLog(_statePost.pending().size() ? _statePost.log(0) : LogEntries()); m_TestObject["logs"] = exportLog(_statePost.pending().size() ? _statePost.log(0) : LogEntries());
@ -489,7 +490,11 @@ LogEntries importLog(json_spirit::mArray& _a)
void checkOutput(bytes const& _output, json_spirit::mObject& _o) void checkOutput(bytes const& _output, json_spirit::mObject& _o)
{ {
int j = 0; int j = 0;
if (_o["out"].type() == json_spirit::array_type)
if (_o["out"].get_str().find("#") == 0)
BOOST_CHECK((u256)_output.size() == toInt(_o["out"].get_str().substr(1)));
else if (_o["out"].type() == json_spirit::array_type)
for (auto const& d: _o["out"].get_array()) for (auto const& d: _o["out"].get_array())
{ {
BOOST_CHECK_MESSAGE(_output[j] == toInt(d), "Output byte [" << j << "] different!"); BOOST_CHECK_MESSAGE(_output[j] == toInt(d), "Output byte [" << j << "] different!");

4
test/libethereum/StateTestsFiller/stMemoryStressTestFiller.json

@ -58,7 +58,7 @@
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000", "balance" : "1000000000000000000",
"nonce" : "0", "nonce" : "0",
"code" : "{ (RETURN 0 4294967297) } ", "code" : "{ (RETURN 0 4294967295) } ",
"storage": {} "storage": {}
}, },
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
@ -98,7 +98,7 @@
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000", "balance" : "1000000000000000000",
"nonce" : "0", "nonce" : "0",
"code" : "{[ 0 ] 1 (RETURN 0 4294967296) } ", "code" : "{[ 0 ] 1 (RETURN 0 4294967295) } ",
"storage": {} "storage": {}
}, },
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {

2
test/libethereum/state.cpp

@ -176,12 +176,10 @@ BOOST_AUTO_TEST_CASE(stMemoryStressTest)
dev::test::executeTests("stMemoryStressTest", "/StateTests",dev::test::getFolder(__FILE__) + "/StateTestsFiller", dev::test::doStateTests); dev::test::executeTests("stMemoryStressTest", "/StateTests",dev::test::getFolder(__FILE__) + "/StateTestsFiller", dev::test::doStateTests);
} }
#if ETH_SOLIDITY
BOOST_AUTO_TEST_CASE(stSolidityTest) BOOST_AUTO_TEST_CASE(stSolidityTest)
{ {
dev::test::executeTests("stSolidityTest", "/StateTests",dev::test::getFolder(__FILE__) + "/StateTestsFiller", dev::test::doStateTests); dev::test::executeTests("stSolidityTest", "/StateTests",dev::test::getFolder(__FILE__) + "/StateTestsFiller", dev::test::doStateTests);
} }
#endif
BOOST_AUTO_TEST_CASE(stMemoryTest) BOOST_AUTO_TEST_CASE(stMemoryTest)
{ {

2
test/libevm/vm.cpp

@ -390,7 +390,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
} }
o["callcreates"] = fev.exportCallCreates(); o["callcreates"] = fev.exportCallCreates();
o["out"] = toHex(output, 2, HexPrefix::Add); o["out"] = output.size() > 4096 ? "#" + toString(output.size()) : toHex(output, 2, HexPrefix::Add);
o["gas"] = toCompactHex(gas, HexPrefix::Add, 1); o["gas"] = toCompactHex(gas, HexPrefix::Add, 1);
o["logs"] = exportLog(fev.sub.logs); o["logs"] = exportLog(fev.sub.logs);
} }

60
test/GasMeter.cpp → test/libsolidity/GasMeter.cpp

@ -21,6 +21,8 @@
*/ */
#include <test/libsolidity/solidityExecutionFramework.h> #include <test/libsolidity/solidityExecutionFramework.h>
#include <libevmasm/GasMeter.h>
#include <libevmasm/KnownState.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/StructuralGasEstimator.h> #include <libsolidity/StructuralGasEstimator.h>
#include <libsolidity/SourceReferenceFormatter.h> #include <libsolidity/SourceReferenceFormatter.h>
@ -55,8 +57,21 @@ public:
); );
} }
void testCreationTimeGas(string const& _sourceCode, string const& _contractName = "")
{
compileAndRun(_sourceCode);
auto state = make_shared<KnownState>();
GasMeter meter(state);
GasMeter::GasConsumption gas;
for (AssemblyItem const& item: *m_compiler.getAssemblyItems(_contractName))
gas += meter.estimateMax(item);
u256 bytecodeSize(m_compiler.getRuntimeBytecode(_contractName).size());
gas += bytecodeSize * c_createDataGas;
BOOST_REQUIRE(!gas.isInfinite);
BOOST_CHECK(gas.value == m_gasUsed);
}
protected: protected:
dev::solidity::CompilerStack m_compiler;
map<ASTNode const*, eth::GasMeter::GasConsumption> m_gasCosts; map<ASTNode const*, eth::GasMeter::GasConsumption> m_gasCosts;
}; };
@ -91,6 +106,49 @@ BOOST_AUTO_TEST_CASE(non_overlapping_filtered_costs)
} }
} }
BOOST_AUTO_TEST_CASE(simple_contract)
{
// Tests a simple "deploy contract" code without constructor. The actual contract is not relevant.
char const* sourceCode = R"(
contract test {
bytes32 public shaValue;
function f(uint a) {
shaValue = sha3(a);
}
}
)";
testCreationTimeGas(sourceCode);
}
BOOST_AUTO_TEST_CASE(store_sha3)
{
char const* sourceCode = R"(
contract test {
bytes32 public shaValue;
function test(uint a) {
shaValue = sha3(a);
}
}
)";
testCreationTimeGas(sourceCode);
}
BOOST_AUTO_TEST_CASE(updating_store)
{
char const* sourceCode = R"(
contract test {
uint data;
uint data2;
function test() {
data = 1;
data = 2;
data2 = 0;
}
}
)";
testCreationTimeGas(sourceCode);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

86
test/libsolidity/SolidityEndToEndTest.cpp

@ -4023,6 +4023,92 @@ BOOST_AUTO_TEST_CASE(overwriting_inheritance)
BOOST_CHECK(callContractFunction("checkOk()") == encodeArgs(6)); BOOST_CHECK(callContractFunction("checkOk()") == encodeArgs(6));
} }
BOOST_AUTO_TEST_CASE(struct_assign_reference_to_struct)
{
char const* sourceCode = R"(
contract test {
struct testStruct
{
uint m_value;
}
testStruct data1;
testStruct data2;
testStruct data3;
function test()
{
data1.m_value = 2;
}
function assign() returns (uint ret_local, uint ret_global, uint ret_global3, uint ret_global1)
{
testStruct x = data1; //x is a reference data1.m_value == 2 as well as x.m_value = 2
data2 = data1; // should copy data. data2.m_value == 2
ret_local = x.m_value; // = 2
ret_global = data2.m_value; // = 2
x.m_value = 3;
data3 = x; //should copy the data. data3.m_value == 3
ret_global3 = data3.m_value; // = 3
ret_global1 = data1.m_value; // = 3. Changed due to the assignment to x.m_value
}
}
)";
compileAndRun(sourceCode, 0, "test");
BOOST_CHECK(callContractFunction("assign()") == encodeArgs(2, 2, 3, 3));
}
BOOST_AUTO_TEST_CASE(struct_delete_member)
{
char const* sourceCode = R"(
contract test {
struct testStruct
{
uint m_value;
}
testStruct data1;
function test()
{
data1.m_value = 2;
}
function deleteMember() returns (uint ret_value)
{
testStruct x = data1; //should not copy the data. data1.m_value == 2 but x.m_value = 0
x.m_value = 4;
delete x.m_value;
ret_value = data1.m_value;
}
}
)";
compileAndRun(sourceCode, 0, "test");
auto res = callContractFunction("deleteMember()");
BOOST_CHECK(callContractFunction("deleteMember()") == encodeArgs(0));
}
BOOST_AUTO_TEST_CASE(struct_delete_struct_in_mapping)
{
char const* sourceCode = R"(
contract test {
struct testStruct
{
uint m_value;
}
mapping (uint => testStruct) campaigns;
function test()
{
campaigns[0].m_value = 2;
}
function deleteIt() returns (uint)
{
delete campaigns[0];
return campaigns[0].m_value;
}
}
)";
compileAndRun(sourceCode, 0, "test");
auto res = callContractFunction("deleteIt()");
BOOST_CHECK(callContractFunction("deleteIt()") == encodeArgs(0));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

11
test/libsolidity/solidityExecutionFramework.h

@ -44,11 +44,11 @@ public:
bytes const& compileAndRun(std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "") bytes const& compileAndRun(std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "")
{ {
dev::solidity::CompilerStack compiler(m_addStandardSources); m_compiler.reset(false, m_addStandardSources);
compiler.addSource("", _sourceCode); m_compiler.addSource("", _sourceCode);
ETH_TEST_REQUIRE_NO_THROW(compiler.compile(m_optimize), "Compiling contract failed"); ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize), "Compiling contract failed");
bytes code = compiler.getBytecode(_contractName); bytes code = m_compiler.getBytecode(_contractName);
sendMessage(code, true, _value); sendMessage(code, true, _value);
BOOST_REQUIRE(!m_output.empty()); BOOST_REQUIRE(!m_output.empty());
return m_output; return m_output;
@ -160,12 +160,14 @@ protected:
BOOST_REQUIRE(executive.go()); BOOST_REQUIRE(executive.go());
m_state.noteSending(m_sender); m_state.noteSending(m_sender);
executive.finalize(); executive.finalize();
m_gasUsed = executive.gasUsed();
m_output = executive.out().toVector(); m_output = executive.out().toVector();
m_logs = executive.logs(); m_logs = executive.logs();
} }
bool m_optimize = false; bool m_optimize = false;
bool m_addStandardSources = false; bool m_addStandardSources = false;
dev::solidity::CompilerStack m_compiler;
Address m_sender; Address m_sender;
Address m_contractAddress; Address m_contractAddress;
eth::State m_state; eth::State m_state;
@ -173,6 +175,7 @@ protected:
u256 const m_gas = 100000000; u256 const m_gas = 100000000;
bytes m_output; bytes m_output;
eth::LogEntries m_logs; eth::LogEntries m_logs;
u256 m_gasUsed;
}; };
} }

Loading…
Cancel
Save