mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #471 from winsvega/libevmcore
move libevmcore to solidity
This commit is contained in:
commit
737623cd0c
@ -26,7 +26,7 @@
|
|||||||
#include <libdevcore/Common.h>
|
#include <libdevcore/Common.h>
|
||||||
#include <libdevcore/Assertions.h>
|
#include <libdevcore/Assertions.h>
|
||||||
#include <libdevcore/SHA3.h>
|
#include <libdevcore/SHA3.h>
|
||||||
#include <libevmcore/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
#include <libevmasm/SourceLocation.h>
|
#include <libevmasm/SourceLocation.h>
|
||||||
#include <libevmasm/AssemblyItem.h>
|
#include <libevmasm/AssemblyItem.h>
|
||||||
#include <libevmasm/LinkerObject.h>
|
#include <libevmasm/LinkerObject.h>
|
||||||
@ -69,10 +69,10 @@ public:
|
|||||||
void appendProgramSize() { append(AssemblyItem(PushProgramSize)); }
|
void appendProgramSize() { append(AssemblyItem(PushProgramSize)); }
|
||||||
void appendLibraryAddress(std::string const& _identifier) { append(newPushLibraryAddress(_identifier)); }
|
void appendLibraryAddress(std::string const& _identifier) { append(newPushLibraryAddress(_identifier)); }
|
||||||
|
|
||||||
AssemblyItem appendJump() { auto ret = append(newPushTag()); append(Instruction::JUMP); return ret; }
|
AssemblyItem appendJump() { auto ret = append(newPushTag()); append(solidity::Instruction::JUMP); return ret; }
|
||||||
AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(Instruction::JUMPI); return ret; }
|
AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(solidity::Instruction::JUMPI); return ret; }
|
||||||
AssemblyItem appendJump(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(Instruction::JUMP); return ret; }
|
AssemblyItem appendJump(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(solidity::Instruction::JUMP); return ret; }
|
||||||
AssemblyItem appendJumpI(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(Instruction::JUMPI); return ret; }
|
AssemblyItem appendJumpI(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(solidity::Instruction::JUMPI); return ret; }
|
||||||
AssemblyItem errorTag() { return AssemblyItem(PushTag, 0); }
|
AssemblyItem errorTag() { return AssemblyItem(PushTag, 0); }
|
||||||
|
|
||||||
template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; }
|
template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; }
|
||||||
@ -86,7 +86,7 @@ public:
|
|||||||
void ignored() { m_baseDeposit = m_deposit; }
|
void ignored() { m_baseDeposit = m_deposit; }
|
||||||
void endIgnored() { m_deposit = m_baseDeposit; m_baseDeposit = 0; }
|
void endIgnored() { m_deposit = m_baseDeposit; m_baseDeposit = 0; }
|
||||||
|
|
||||||
void popTo(int _deposit) { while (m_deposit > _deposit) append(Instruction::POP); }
|
void popTo(int _deposit) { while (m_deposit > _deposit) append(solidity::Instruction::POP); }
|
||||||
|
|
||||||
void injectStart(AssemblyItem const& _i);
|
void injectStart(AssemblyItem const& _i);
|
||||||
std::string out() const;
|
std::string out() const;
|
||||||
|
@ -94,7 +94,7 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItem const& _item)
|
|||||||
{
|
{
|
||||||
case Operation:
|
case Operation:
|
||||||
_out << " " << instructionInfo(_item.instruction()).name;
|
_out << " " << instructionInfo(_item.instruction()).name;
|
||||||
if (_item.instruction() == eth::Instruction::JUMP || _item.instruction() == eth::Instruction::JUMPI)
|
if (_item.instruction() == solidity::Instruction::JUMP || _item.instruction() == solidity::Instruction::JUMPI)
|
||||||
_out << "\t" << _item.getJumpTypeAsString();
|
_out << "\t" << _item.getJumpTypeAsString();
|
||||||
break;
|
break;
|
||||||
case Push:
|
case Push:
|
||||||
|
@ -25,9 +25,10 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <libdevcore/Common.h>
|
#include <libdevcore/Common.h>
|
||||||
#include <libdevcore/Assertions.h>
|
#include <libdevcore/Assertions.h>
|
||||||
#include <libevmcore/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
#include <libevmasm/SourceLocation.h>
|
#include <libevmasm/SourceLocation.h>
|
||||||
#include "Exceptions.h"
|
#include "Exceptions.h"
|
||||||
|
using namespace dev::solidity;
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
@ -57,7 +58,7 @@ public:
|
|||||||
|
|
||||||
AssemblyItem(u256 _push, SourceLocation const& _location = SourceLocation()):
|
AssemblyItem(u256 _push, SourceLocation const& _location = SourceLocation()):
|
||||||
AssemblyItem(Push, _push, _location) { }
|
AssemblyItem(Push, _push, _location) { }
|
||||||
AssemblyItem(Instruction _i, SourceLocation const& _location = SourceLocation()):
|
AssemblyItem(solidity::Instruction _i, SourceLocation const& _location = SourceLocation()):
|
||||||
AssemblyItem(Operation, byte(_i), _location) { }
|
AssemblyItem(Operation, byte(_i), _location) { }
|
||||||
AssemblyItem(AssemblyItemType _type, u256 _data = 0, SourceLocation const& _location = SourceLocation()):
|
AssemblyItem(AssemblyItemType _type, u256 _data = 0, SourceLocation const& _location = SourceLocation()):
|
||||||
m_type(_type),
|
m_type(_type),
|
||||||
|
@ -106,7 +106,7 @@ BlockDeduplicator::BlockIterator& BlockDeduplicator::BlockIterator::operator++()
|
|||||||
{
|
{
|
||||||
if (it == end)
|
if (it == end)
|
||||||
return *this;
|
return *this;
|
||||||
if (SemanticInformation::altersControlFlow(*it) && *it != AssemblyItem(eth::Instruction::JUMPI))
|
if (SemanticInformation::altersControlFlow(*it) && *it != AssemblyItem(Instruction::JUMPI))
|
||||||
it = end;
|
it = end;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -8,7 +8,7 @@ file(GLOB HEADERS "*.h")
|
|||||||
|
|
||||||
include_directories(BEFORE ..)
|
include_directories(BEFORE ..)
|
||||||
add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
|
add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
|
||||||
eth_use(${EXECUTABLE} REQUIRED Eth::evmcore)
|
eth_use(${EXECUTABLE} REQUIRED Dev::devcore Eth::ethcore)
|
||||||
|
|
||||||
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
|
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
|
||||||
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )
|
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )
|
||||||
|
@ -84,13 +84,13 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item)
|
|||||||
break;
|
break;
|
||||||
case Instruction::MLOAD:
|
case Instruction::MLOAD:
|
||||||
case Instruction::MSTORE:
|
case Instruction::MSTORE:
|
||||||
gas += memoryGas(classes.find(eth::Instruction::ADD, {
|
gas += memoryGas(classes.find(Instruction::ADD, {
|
||||||
m_state->relativeStackElement(0),
|
m_state->relativeStackElement(0),
|
||||||
classes.find(AssemblyItem(32))
|
classes.find(AssemblyItem(32))
|
||||||
}));
|
}));
|
||||||
break;
|
break;
|
||||||
case Instruction::MSTORE8:
|
case Instruction::MSTORE8:
|
||||||
gas += memoryGas(classes.find(eth::Instruction::ADD, {
|
gas += memoryGas(classes.find(Instruction::ADD, {
|
||||||
m_state->relativeStackElement(0),
|
m_state->relativeStackElement(0),
|
||||||
classes.find(AssemblyItem(1))
|
classes.find(AssemblyItem(1))
|
||||||
}));
|
}));
|
||||||
@ -198,7 +198,7 @@ GasMeter::GasConsumption GasMeter::memoryGas(int _stackPosOffset, int _stackPosS
|
|||||||
if (classes.knownZero(m_state->relativeStackElement(_stackPosSize)))
|
if (classes.knownZero(m_state->relativeStackElement(_stackPosSize)))
|
||||||
return GasConsumption(0);
|
return GasConsumption(0);
|
||||||
else
|
else
|
||||||
return memoryGas(classes.find(eth::Instruction::ADD, {
|
return memoryGas(classes.find(Instruction::ADD, {
|
||||||
m_state->relativeStackElement(_stackPosOffset),
|
m_state->relativeStackElement(_stackPosOffset),
|
||||||
m_state->relativeStackElement(_stackPosSize)
|
m_state->relativeStackElement(_stackPosSize)
|
||||||
}));
|
}));
|
||||||
|
354
libevmasm/Instruction.cpp
Normal file
354
libevmasm/Instruction.cpp
Normal file
@ -0,0 +1,354 @@
|
|||||||
|
/*
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/** @file Instruction.cpp
|
||||||
|
* @author Gav Wood <i@gavwood.com>
|
||||||
|
* @date 2014
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "./Instruction.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <libdevcore/Common.h>
|
||||||
|
#include <libdevcore/CommonIO.h>
|
||||||
|
#include <libdevcore/Log.h>
|
||||||
|
using namespace std;
|
||||||
|
using namespace dev;
|
||||||
|
using namespace dev::solidity;
|
||||||
|
|
||||||
|
const std::map<std::string, Instruction> dev::solidity::c_instructions =
|
||||||
|
{
|
||||||
|
{ "STOP", Instruction::STOP },
|
||||||
|
{ "ADD", Instruction::ADD },
|
||||||
|
{ "SUB", Instruction::SUB },
|
||||||
|
{ "MUL", Instruction::MUL },
|
||||||
|
{ "DIV", Instruction::DIV },
|
||||||
|
{ "SDIV", Instruction::SDIV },
|
||||||
|
{ "MOD", Instruction::MOD },
|
||||||
|
{ "SMOD", Instruction::SMOD },
|
||||||
|
{ "EXP", Instruction::EXP },
|
||||||
|
{ "BNOT", Instruction::NOT },
|
||||||
|
{ "LT", Instruction::LT },
|
||||||
|
{ "GT", Instruction::GT },
|
||||||
|
{ "SLT", Instruction::SLT },
|
||||||
|
{ "SGT", Instruction::SGT },
|
||||||
|
{ "EQ", Instruction::EQ },
|
||||||
|
{ "NOT", Instruction::ISZERO },
|
||||||
|
{ "AND", Instruction::AND },
|
||||||
|
{ "OR", Instruction::OR },
|
||||||
|
{ "XOR", Instruction::XOR },
|
||||||
|
{ "BYTE", Instruction::BYTE },
|
||||||
|
{ "ADDMOD", Instruction::ADDMOD },
|
||||||
|
{ "MULMOD", Instruction::MULMOD },
|
||||||
|
{ "SIGNEXTEND", Instruction::SIGNEXTEND },
|
||||||
|
{ "SHA3", Instruction::SHA3 },
|
||||||
|
{ "ADDRESS", Instruction::ADDRESS },
|
||||||
|
{ "BALANCE", Instruction::BALANCE },
|
||||||
|
{ "ORIGIN", Instruction::ORIGIN },
|
||||||
|
{ "CALLER", Instruction::CALLER },
|
||||||
|
{ "CALLVALUE", Instruction::CALLVALUE },
|
||||||
|
{ "CALLDATALOAD", Instruction::CALLDATALOAD },
|
||||||
|
{ "CALLDATASIZE", Instruction::CALLDATASIZE },
|
||||||
|
{ "CALLDATACOPY", Instruction::CALLDATACOPY },
|
||||||
|
{ "CODESIZE", Instruction::CODESIZE },
|
||||||
|
{ "CODECOPY", Instruction::CODECOPY },
|
||||||
|
{ "GASPRICE", Instruction::GASPRICE },
|
||||||
|
{ "EXTCODESIZE", Instruction::EXTCODESIZE },
|
||||||
|
{ "EXTCODECOPY", Instruction::EXTCODECOPY },
|
||||||
|
{ "BLOCKHASH", Instruction::BLOCKHASH },
|
||||||
|
{ "COINBASE", Instruction::COINBASE },
|
||||||
|
{ "TIMESTAMP", Instruction::TIMESTAMP },
|
||||||
|
{ "NUMBER", Instruction::NUMBER },
|
||||||
|
{ "DIFFICULTY", Instruction::DIFFICULTY },
|
||||||
|
{ "GASLIMIT", Instruction::GASLIMIT },
|
||||||
|
{ "POP", Instruction::POP },
|
||||||
|
{ "MLOAD", Instruction::MLOAD },
|
||||||
|
{ "MSTORE", Instruction::MSTORE },
|
||||||
|
{ "MSTORE8", Instruction::MSTORE8 },
|
||||||
|
{ "SLOAD", Instruction::SLOAD },
|
||||||
|
{ "SSTORE", Instruction::SSTORE },
|
||||||
|
{ "JUMP", Instruction::JUMP },
|
||||||
|
{ "JUMPI", Instruction::JUMPI },
|
||||||
|
{ "PC", Instruction::PC },
|
||||||
|
{ "MSIZE", Instruction::MSIZE },
|
||||||
|
{ "GAS", Instruction::GAS },
|
||||||
|
{ "JUMPDEST", Instruction::JUMPDEST },
|
||||||
|
{ "PUSH1", Instruction::PUSH1 },
|
||||||
|
{ "PUSH2", Instruction::PUSH2 },
|
||||||
|
{ "PUSH3", Instruction::PUSH3 },
|
||||||
|
{ "PUSH4", Instruction::PUSH4 },
|
||||||
|
{ "PUSH5", Instruction::PUSH5 },
|
||||||
|
{ "PUSH6", Instruction::PUSH6 },
|
||||||
|
{ "PUSH7", Instruction::PUSH7 },
|
||||||
|
{ "PUSH8", Instruction::PUSH8 },
|
||||||
|
{ "PUSH9", Instruction::PUSH9 },
|
||||||
|
{ "PUSH10", Instruction::PUSH10 },
|
||||||
|
{ "PUSH11", Instruction::PUSH11 },
|
||||||
|
{ "PUSH12", Instruction::PUSH12 },
|
||||||
|
{ "PUSH13", Instruction::PUSH13 },
|
||||||
|
{ "PUSH14", Instruction::PUSH14 },
|
||||||
|
{ "PUSH15", Instruction::PUSH15 },
|
||||||
|
{ "PUSH16", Instruction::PUSH16 },
|
||||||
|
{ "PUSH17", Instruction::PUSH17 },
|
||||||
|
{ "PUSH18", Instruction::PUSH18 },
|
||||||
|
{ "PUSH19", Instruction::PUSH19 },
|
||||||
|
{ "PUSH20", Instruction::PUSH20 },
|
||||||
|
{ "PUSH21", Instruction::PUSH21 },
|
||||||
|
{ "PUSH22", Instruction::PUSH22 },
|
||||||
|
{ "PUSH23", Instruction::PUSH23 },
|
||||||
|
{ "PUSH24", Instruction::PUSH24 },
|
||||||
|
{ "PUSH25", Instruction::PUSH25 },
|
||||||
|
{ "PUSH26", Instruction::PUSH26 },
|
||||||
|
{ "PUSH27", Instruction::PUSH27 },
|
||||||
|
{ "PUSH28", Instruction::PUSH28 },
|
||||||
|
{ "PUSH29", Instruction::PUSH29 },
|
||||||
|
{ "PUSH30", Instruction::PUSH30 },
|
||||||
|
{ "PUSH31", Instruction::PUSH31 },
|
||||||
|
{ "PUSH32", Instruction::PUSH32 },
|
||||||
|
{ "DUP1", Instruction::DUP1 },
|
||||||
|
{ "DUP2", Instruction::DUP2 },
|
||||||
|
{ "DUP3", Instruction::DUP3 },
|
||||||
|
{ "DUP4", Instruction::DUP4 },
|
||||||
|
{ "DUP5", Instruction::DUP5 },
|
||||||
|
{ "DUP6", Instruction::DUP6 },
|
||||||
|
{ "DUP7", Instruction::DUP7 },
|
||||||
|
{ "DUP8", Instruction::DUP8 },
|
||||||
|
{ "DUP9", Instruction::DUP9 },
|
||||||
|
{ "DUP10", Instruction::DUP10 },
|
||||||
|
{ "DUP11", Instruction::DUP11 },
|
||||||
|
{ "DUP12", Instruction::DUP12 },
|
||||||
|
{ "DUP13", Instruction::DUP13 },
|
||||||
|
{ "DUP14", Instruction::DUP14 },
|
||||||
|
{ "DUP15", Instruction::DUP15 },
|
||||||
|
{ "DUP16", Instruction::DUP16 },
|
||||||
|
{ "SWAP1", Instruction::SWAP1 },
|
||||||
|
{ "SWAP2", Instruction::SWAP2 },
|
||||||
|
{ "SWAP3", Instruction::SWAP3 },
|
||||||
|
{ "SWAP4", Instruction::SWAP4 },
|
||||||
|
{ "SWAP5", Instruction::SWAP5 },
|
||||||
|
{ "SWAP6", Instruction::SWAP6 },
|
||||||
|
{ "SWAP7", Instruction::SWAP7 },
|
||||||
|
{ "SWAP8", Instruction::SWAP8 },
|
||||||
|
{ "SWAP9", Instruction::SWAP9 },
|
||||||
|
{ "SWAP10", Instruction::SWAP10 },
|
||||||
|
{ "SWAP11", Instruction::SWAP11 },
|
||||||
|
{ "SWAP12", Instruction::SWAP12 },
|
||||||
|
{ "SWAP13", Instruction::SWAP13 },
|
||||||
|
{ "SWAP14", Instruction::SWAP14 },
|
||||||
|
{ "SWAP15", Instruction::SWAP15 },
|
||||||
|
{ "SWAP16", Instruction::SWAP16 },
|
||||||
|
{ "LOG0", Instruction::LOG0 },
|
||||||
|
{ "LOG1", Instruction::LOG1 },
|
||||||
|
{ "LOG2", Instruction::LOG2 },
|
||||||
|
{ "LOG3", Instruction::LOG3 },
|
||||||
|
{ "LOG4", Instruction::LOG4 },
|
||||||
|
{ "CREATE", Instruction::CREATE },
|
||||||
|
{ "CALL", Instruction::CALL },
|
||||||
|
{ "CALLCODE", Instruction::CALLCODE },
|
||||||
|
{ "RETURN", Instruction::RETURN },
|
||||||
|
{ "DELEGATECALL", Instruction::DELEGATECALL },
|
||||||
|
{ "SUICIDE", Instruction::SUICIDE }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::map<Instruction, InstructionInfo> c_instructionInfo =
|
||||||
|
{ // Add, Args, Ret, SideEffects, GasPriceTier
|
||||||
|
{ Instruction::STOP, { "STOP", 0, 0, 0, true, ZeroTier } },
|
||||||
|
{ Instruction::ADD, { "ADD", 0, 2, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::SUB, { "SUB", 0, 2, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::MUL, { "MUL", 0, 2, 1, false, LowTier } },
|
||||||
|
{ Instruction::DIV, { "DIV", 0, 2, 1, false, LowTier } },
|
||||||
|
{ Instruction::SDIV, { "SDIV", 0, 2, 1, false, LowTier } },
|
||||||
|
{ Instruction::MOD, { "MOD", 0, 2, 1, false, LowTier } },
|
||||||
|
{ Instruction::SMOD, { "SMOD", 0, 2, 1, false, LowTier } },
|
||||||
|
{ Instruction::EXP, { "EXP", 0, 2, 1, false, SpecialTier } },
|
||||||
|
{ Instruction::NOT, { "NOT", 0, 1, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::LT, { "LT", 0, 2, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::GT, { "GT", 0, 2, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::SLT, { "SLT", 0, 2, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::SGT, { "SGT", 0, 2, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::EQ, { "EQ", 0, 2, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::ISZERO, { "ISZERO", 0, 1, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::AND, { "AND", 0, 2, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::OR, { "OR", 0, 2, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::XOR, { "XOR", 0, 2, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::BYTE, { "BYTE", 0, 2, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::ADDMOD, { "ADDMOD", 0, 3, 1, false, MidTier } },
|
||||||
|
{ Instruction::MULMOD, { "MULMOD", 0, 3, 1, false, MidTier } },
|
||||||
|
{ Instruction::SIGNEXTEND, { "SIGNEXTEND", 0, 2, 1, false, LowTier } },
|
||||||
|
{ Instruction::SHA3, { "SHA3", 0, 2, 1, false, SpecialTier } },
|
||||||
|
{ Instruction::ADDRESS, { "ADDRESS", 0, 0, 1, false, BaseTier } },
|
||||||
|
{ Instruction::BALANCE, { "BALANCE", 0, 1, 1, false, ExtTier } },
|
||||||
|
{ Instruction::ORIGIN, { "ORIGIN", 0, 0, 1, false, BaseTier } },
|
||||||
|
{ Instruction::CALLER, { "CALLER", 0, 0, 1, false, BaseTier } },
|
||||||
|
{ Instruction::CALLVALUE, { "CALLVALUE", 0, 0, 1, false, BaseTier } },
|
||||||
|
{ Instruction::CALLDATALOAD,{ "CALLDATALOAD", 0, 1, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::CALLDATASIZE,{ "CALLDATASIZE", 0, 0, 1, false, BaseTier } },
|
||||||
|
{ Instruction::CALLDATACOPY,{ "CALLDATACOPY", 0, 3, 0, true, VeryLowTier } },
|
||||||
|
{ Instruction::CODESIZE, { "CODESIZE", 0, 0, 1, false, BaseTier } },
|
||||||
|
{ Instruction::CODECOPY, { "CODECOPY", 0, 3, 0, true, VeryLowTier } },
|
||||||
|
{ Instruction::GASPRICE, { "GASPRICE", 0, 0, 1, false, BaseTier } },
|
||||||
|
{ Instruction::EXTCODESIZE, { "EXTCODESIZE", 0, 1, 1, false, ExtTier } },
|
||||||
|
{ Instruction::EXTCODECOPY, { "EXTCODECOPY", 0, 4, 0, true, ExtTier } },
|
||||||
|
{ Instruction::BLOCKHASH, { "BLOCKHASH", 0, 1, 1, false, ExtTier } },
|
||||||
|
{ Instruction::COINBASE, { "COINBASE", 0, 0, 1, false, BaseTier } },
|
||||||
|
{ Instruction::TIMESTAMP, { "TIMESTAMP", 0, 0, 1, false, BaseTier } },
|
||||||
|
{ Instruction::NUMBER, { "NUMBER", 0, 0, 1, false, BaseTier } },
|
||||||
|
{ Instruction::DIFFICULTY, { "DIFFICULTY", 0, 0, 1, false, BaseTier } },
|
||||||
|
{ Instruction::GASLIMIT, { "GASLIMIT", 0, 0, 1, false, BaseTier } },
|
||||||
|
{ Instruction::POP, { "POP", 0, 1, 0, false, BaseTier } },
|
||||||
|
{ Instruction::MLOAD, { "MLOAD", 0, 1, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::MSTORE, { "MSTORE", 0, 2, 0, true, VeryLowTier } },
|
||||||
|
{ Instruction::MSTORE8, { "MSTORE8", 0, 2, 0, true, VeryLowTier } },
|
||||||
|
{ Instruction::SLOAD, { "SLOAD", 0, 1, 1, false, SpecialTier } },
|
||||||
|
{ Instruction::SSTORE, { "SSTORE", 0, 2, 0, true, SpecialTier } },
|
||||||
|
{ Instruction::JUMP, { "JUMP", 0, 1, 0, true, MidTier } },
|
||||||
|
{ Instruction::JUMPI, { "JUMPI", 0, 2, 0, true, HighTier } },
|
||||||
|
{ Instruction::PC, { "PC", 0, 0, 1, false, BaseTier } },
|
||||||
|
{ Instruction::MSIZE, { "MSIZE", 0, 0, 1, false, BaseTier } },
|
||||||
|
{ Instruction::GAS, { "GAS", 0, 0, 1, false, BaseTier } },
|
||||||
|
{ Instruction::JUMPDEST, { "JUMPDEST", 0, 0, 0, true, SpecialTier } },
|
||||||
|
{ Instruction::PUSH1, { "PUSH1", 1, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH2, { "PUSH2", 2, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH3, { "PUSH3", 3, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH4, { "PUSH4", 4, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH5, { "PUSH5", 5, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH6, { "PUSH6", 6, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH7, { "PUSH7", 7, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH8, { "PUSH8", 8, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH9, { "PUSH9", 9, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH10, { "PUSH10", 10, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH11, { "PUSH11", 11, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH12, { "PUSH12", 12, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH13, { "PUSH13", 13, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH14, { "PUSH14", 14, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH15, { "PUSH15", 15, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH16, { "PUSH16", 16, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH17, { "PUSH17", 17, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH18, { "PUSH18", 18, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH19, { "PUSH19", 19, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH20, { "PUSH20", 20, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH21, { "PUSH21", 21, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH22, { "PUSH22", 22, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH23, { "PUSH23", 23, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH24, { "PUSH24", 24, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH25, { "PUSH25", 25, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH26, { "PUSH26", 26, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH27, { "PUSH27", 27, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH28, { "PUSH28", 28, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH29, { "PUSH29", 29, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH30, { "PUSH30", 30, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH31, { "PUSH31", 31, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::PUSH32, { "PUSH32", 32, 0, 1, false, VeryLowTier } },
|
||||||
|
{ Instruction::DUP1, { "DUP1", 0, 1, 2, false, VeryLowTier } },
|
||||||
|
{ Instruction::DUP2, { "DUP2", 0, 2, 3, false, VeryLowTier } },
|
||||||
|
{ Instruction::DUP3, { "DUP3", 0, 3, 4, false, VeryLowTier } },
|
||||||
|
{ Instruction::DUP4, { "DUP4", 0, 4, 5, false, VeryLowTier } },
|
||||||
|
{ Instruction::DUP5, { "DUP5", 0, 5, 6, false, VeryLowTier } },
|
||||||
|
{ Instruction::DUP6, { "DUP6", 0, 6, 7, false, VeryLowTier } },
|
||||||
|
{ Instruction::DUP7, { "DUP7", 0, 7, 8, false, VeryLowTier } },
|
||||||
|
{ Instruction::DUP8, { "DUP8", 0, 8, 9, false, VeryLowTier } },
|
||||||
|
{ Instruction::DUP9, { "DUP9", 0, 9, 10, false, VeryLowTier } },
|
||||||
|
{ Instruction::DUP10, { "DUP10", 0, 10, 11, false, VeryLowTier } },
|
||||||
|
{ Instruction::DUP11, { "DUP11", 0, 11, 12, false, VeryLowTier } },
|
||||||
|
{ Instruction::DUP12, { "DUP12", 0, 12, 13, false, VeryLowTier } },
|
||||||
|
{ Instruction::DUP13, { "DUP13", 0, 13, 14, false, VeryLowTier } },
|
||||||
|
{ Instruction::DUP14, { "DUP14", 0, 14, 15, false, VeryLowTier } },
|
||||||
|
{ Instruction::DUP15, { "DUP15", 0, 15, 16, false, VeryLowTier } },
|
||||||
|
{ Instruction::DUP16, { "DUP16", 0, 16, 17, false, VeryLowTier } },
|
||||||
|
{ Instruction::SWAP1, { "SWAP1", 0, 2, 2, false, VeryLowTier } },
|
||||||
|
{ Instruction::SWAP2, { "SWAP2", 0, 3, 3, false, VeryLowTier } },
|
||||||
|
{ Instruction::SWAP3, { "SWAP3", 0, 4, 4, false, VeryLowTier } },
|
||||||
|
{ Instruction::SWAP4, { "SWAP4", 0, 5, 5, false, VeryLowTier } },
|
||||||
|
{ Instruction::SWAP5, { "SWAP5", 0, 6, 6, false, VeryLowTier } },
|
||||||
|
{ Instruction::SWAP6, { "SWAP6", 0, 7, 7, false, VeryLowTier } },
|
||||||
|
{ Instruction::SWAP7, { "SWAP7", 0, 8, 8, false, VeryLowTier } },
|
||||||
|
{ Instruction::SWAP8, { "SWAP8", 0, 9, 9, false, VeryLowTier } },
|
||||||
|
{ Instruction::SWAP9, { "SWAP9", 0, 10, 10, false, VeryLowTier } },
|
||||||
|
{ Instruction::SWAP10, { "SWAP10", 0, 11, 11, false, VeryLowTier } },
|
||||||
|
{ Instruction::SWAP11, { "SWAP11", 0, 12, 12, false, VeryLowTier } },
|
||||||
|
{ Instruction::SWAP12, { "SWAP12", 0, 13, 13, false, VeryLowTier } },
|
||||||
|
{ Instruction::SWAP13, { "SWAP13", 0, 14, 14, false, VeryLowTier } },
|
||||||
|
{ Instruction::SWAP14, { "SWAP14", 0, 15, 15, false, VeryLowTier } },
|
||||||
|
{ Instruction::SWAP15, { "SWAP15", 0, 16, 16, false, VeryLowTier } },
|
||||||
|
{ Instruction::SWAP16, { "SWAP16", 0, 17, 17, false, VeryLowTier } },
|
||||||
|
{ Instruction::LOG0, { "LOG0", 0, 2, 0, true, SpecialTier } },
|
||||||
|
{ Instruction::LOG1, { "LOG1", 0, 3, 0, true, SpecialTier } },
|
||||||
|
{ Instruction::LOG2, { "LOG2", 0, 4, 0, true, SpecialTier } },
|
||||||
|
{ Instruction::LOG3, { "LOG3", 0, 5, 0, true, SpecialTier } },
|
||||||
|
{ Instruction::LOG4, { "LOG4", 0, 6, 0, true, SpecialTier } },
|
||||||
|
{ Instruction::CREATE, { "CREATE", 0, 3, 1, true, SpecialTier } },
|
||||||
|
{ Instruction::CALL, { "CALL", 0, 7, 1, true, SpecialTier } },
|
||||||
|
{ Instruction::CALLCODE, { "CALLCODE", 0, 7, 1, true, SpecialTier } },
|
||||||
|
{ Instruction::RETURN, { "RETURN", 0, 2, 0, true, ZeroTier } },
|
||||||
|
{ Instruction::DELEGATECALL,{ "DELEGATECALL", 0, 6, 1, true, SpecialTier } },
|
||||||
|
{ Instruction::SUICIDE, { "SUICIDE", 0, 1, 0, true, ZeroTier } }
|
||||||
|
};
|
||||||
|
|
||||||
|
void dev::solidity::eachInstruction(
|
||||||
|
bytes const& _mem,
|
||||||
|
function<void(Instruction,u256 const&)> const& _onInstruction
|
||||||
|
)
|
||||||
|
{
|
||||||
|
for (auto it = _mem.begin(); it < _mem.end(); ++it)
|
||||||
|
{
|
||||||
|
Instruction instr = Instruction(*it);
|
||||||
|
size_t additional = 0;
|
||||||
|
if (isValidInstruction(instr))
|
||||||
|
additional = instructionInfo(instr).additional;
|
||||||
|
u256 data;
|
||||||
|
for (size_t i = 0; i < additional; ++i)
|
||||||
|
{
|
||||||
|
data <<= 8;
|
||||||
|
if (++it < _mem.end())
|
||||||
|
data |= *it;
|
||||||
|
}
|
||||||
|
_onInstruction(instr, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string dev::solidity::disassemble(bytes const& _mem)
|
||||||
|
{
|
||||||
|
stringstream ret;
|
||||||
|
eachInstruction(_mem, [&](Instruction _instr, u256 const& _data) {
|
||||||
|
if (!isValidInstruction(_instr))
|
||||||
|
ret << "0x" << hex << int(_instr) << " ";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
InstructionInfo info = instructionInfo(_instr);
|
||||||
|
ret << info.name << " ";
|
||||||
|
if (info.additional)
|
||||||
|
ret << "0x" << hex << _data << " ";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return ret.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
InstructionInfo dev::solidity::instructionInfo(Instruction _inst)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return c_instructionInfo.at(_inst);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
return InstructionInfo({"<INVALID_INSTRUCTION: " + toString((unsigned)_inst) + ">", 0, 0, 0, false, InvalidTier});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool dev::solidity::isValidInstruction(Instruction _inst)
|
||||||
|
{
|
||||||
|
return !!c_instructionInfo.count(_inst);
|
||||||
|
}
|
268
libevmasm/Instruction.h
Normal file
268
libevmasm/Instruction.h
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
/*
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/** @file Instruction.h
|
||||||
|
* @author Gav Wood <i@gavwood.com>
|
||||||
|
* @date 2014
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <libdevcore/Common.h>
|
||||||
|
#include <libdevcore/Assertions.h>
|
||||||
|
#include "Exceptions.h"
|
||||||
|
|
||||||
|
namespace dev
|
||||||
|
{
|
||||||
|
namespace solidity
|
||||||
|
{
|
||||||
|
|
||||||
|
DEV_SIMPLE_EXCEPTION(InvalidDeposit);
|
||||||
|
DEV_SIMPLE_EXCEPTION(InvalidOpcode);
|
||||||
|
|
||||||
|
/// Virtual machine bytecode instruction.
|
||||||
|
enum class Instruction: uint8_t
|
||||||
|
{
|
||||||
|
STOP = 0x00, ///< halts execution
|
||||||
|
ADD, ///< addition operation
|
||||||
|
MUL, ///< mulitplication operation
|
||||||
|
SUB, ///< subtraction operation
|
||||||
|
DIV, ///< integer division operation
|
||||||
|
SDIV, ///< signed integer division operation
|
||||||
|
MOD, ///< modulo remainder operation
|
||||||
|
SMOD, ///< signed modulo remainder operation
|
||||||
|
ADDMOD, ///< unsigned modular addition
|
||||||
|
MULMOD, ///< unsigned modular multiplication
|
||||||
|
EXP, ///< exponential operation
|
||||||
|
SIGNEXTEND, ///< extend length of signed integer
|
||||||
|
|
||||||
|
LT = 0x10, ///< less-than comparision
|
||||||
|
GT, ///< greater-than comparision
|
||||||
|
SLT, ///< signed less-than comparision
|
||||||
|
SGT, ///< signed greater-than comparision
|
||||||
|
EQ, ///< equality comparision
|
||||||
|
ISZERO, ///< simple not operator
|
||||||
|
AND, ///< bitwise AND operation
|
||||||
|
OR, ///< bitwise OR operation
|
||||||
|
XOR, ///< bitwise XOR operation
|
||||||
|
NOT, ///< bitwise NOT opertation
|
||||||
|
BYTE, ///< retrieve single byte from word
|
||||||
|
|
||||||
|
SHA3 = 0x20, ///< compute SHA3-256 hash
|
||||||
|
|
||||||
|
ADDRESS = 0x30, ///< get address of currently executing account
|
||||||
|
BALANCE, ///< get balance of the given account
|
||||||
|
ORIGIN, ///< get execution origination address
|
||||||
|
CALLER, ///< get caller address
|
||||||
|
CALLVALUE, ///< get deposited value by the instruction/transaction responsible for this execution
|
||||||
|
CALLDATALOAD, ///< get input data of current environment
|
||||||
|
CALLDATASIZE, ///< get size of input data in current environment
|
||||||
|
CALLDATACOPY, ///< copy input data in current environment to memory
|
||||||
|
CODESIZE, ///< get size of code running in current environment
|
||||||
|
CODECOPY, ///< copy code running in current environment to memory
|
||||||
|
GASPRICE, ///< get price of gas in current environment
|
||||||
|
EXTCODESIZE, ///< get external code size (from another contract)
|
||||||
|
EXTCODECOPY, ///< copy external code (from another contract)
|
||||||
|
|
||||||
|
BLOCKHASH = 0x40, ///< get hash of most recent complete block
|
||||||
|
COINBASE, ///< get the block's coinbase address
|
||||||
|
TIMESTAMP, ///< get the block's timestamp
|
||||||
|
NUMBER, ///< get the block's number
|
||||||
|
DIFFICULTY, ///< get the block's difficulty
|
||||||
|
GASLIMIT, ///< get the block's gas limit
|
||||||
|
|
||||||
|
POP = 0x50, ///< remove item from stack
|
||||||
|
MLOAD, ///< load word from memory
|
||||||
|
MSTORE, ///< save word to memory
|
||||||
|
MSTORE8, ///< save byte to memory
|
||||||
|
SLOAD, ///< load word from storage
|
||||||
|
SSTORE, ///< save word to storage
|
||||||
|
JUMP, ///< alter the program counter
|
||||||
|
JUMPI, ///< conditionally alter the program counter
|
||||||
|
PC, ///< get the program counter
|
||||||
|
MSIZE, ///< get the size of active memory
|
||||||
|
GAS, ///< get the amount of available gas
|
||||||
|
JUMPDEST, ///< set a potential jump destination
|
||||||
|
|
||||||
|
PUSH1 = 0x60, ///< place 1 byte item on stack
|
||||||
|
PUSH2, ///< place 2 byte item on stack
|
||||||
|
PUSH3, ///< place 3 byte item on stack
|
||||||
|
PUSH4, ///< place 4 byte item on stack
|
||||||
|
PUSH5, ///< place 5 byte item on stack
|
||||||
|
PUSH6, ///< place 6 byte item on stack
|
||||||
|
PUSH7, ///< place 7 byte item on stack
|
||||||
|
PUSH8, ///< place 8 byte item on stack
|
||||||
|
PUSH9, ///< place 9 byte item on stack
|
||||||
|
PUSH10, ///< place 10 byte item on stack
|
||||||
|
PUSH11, ///< place 11 byte item on stack
|
||||||
|
PUSH12, ///< place 12 byte item on stack
|
||||||
|
PUSH13, ///< place 13 byte item on stack
|
||||||
|
PUSH14, ///< place 14 byte item on stack
|
||||||
|
PUSH15, ///< place 15 byte item on stack
|
||||||
|
PUSH16, ///< place 16 byte item on stack
|
||||||
|
PUSH17, ///< place 17 byte item on stack
|
||||||
|
PUSH18, ///< place 18 byte item on stack
|
||||||
|
PUSH19, ///< place 19 byte item on stack
|
||||||
|
PUSH20, ///< place 20 byte item on stack
|
||||||
|
PUSH21, ///< place 21 byte item on stack
|
||||||
|
PUSH22, ///< place 22 byte item on stack
|
||||||
|
PUSH23, ///< place 23 byte item on stack
|
||||||
|
PUSH24, ///< place 24 byte item on stack
|
||||||
|
PUSH25, ///< place 25 byte item on stack
|
||||||
|
PUSH26, ///< place 26 byte item on stack
|
||||||
|
PUSH27, ///< place 27 byte item on stack
|
||||||
|
PUSH28, ///< place 28 byte item on stack
|
||||||
|
PUSH29, ///< place 29 byte item on stack
|
||||||
|
PUSH30, ///< place 30 byte item on stack
|
||||||
|
PUSH31, ///< place 31 byte item on stack
|
||||||
|
PUSH32, ///< place 32 byte item on stack
|
||||||
|
|
||||||
|
DUP1 = 0x80, ///< copies the highest item in the stack to the top of the stack
|
||||||
|
DUP2, ///< copies the second highest item in the stack to the top of the stack
|
||||||
|
DUP3, ///< copies the third highest item in the stack to the top of the stack
|
||||||
|
DUP4, ///< copies the 4th highest item in the stack to the top of the stack
|
||||||
|
DUP5, ///< copies the 5th highest item in the stack to the top of the stack
|
||||||
|
DUP6, ///< copies the 6th highest item in the stack to the top of the stack
|
||||||
|
DUP7, ///< copies the 7th highest item in the stack to the top of the stack
|
||||||
|
DUP8, ///< copies the 8th highest item in the stack to the top of the stack
|
||||||
|
DUP9, ///< copies the 9th highest item in the stack to the top of the stack
|
||||||
|
DUP10, ///< copies the 10th highest item in the stack to the top of the stack
|
||||||
|
DUP11, ///< copies the 11th highest item in the stack to the top of the stack
|
||||||
|
DUP12, ///< copies the 12th highest item in the stack to the top of the stack
|
||||||
|
DUP13, ///< copies the 13th highest item in the stack to the top of the stack
|
||||||
|
DUP14, ///< copies the 14th highest item in the stack to the top of the stack
|
||||||
|
DUP15, ///< copies the 15th highest item in the stack to the top of the stack
|
||||||
|
DUP16, ///< copies the 16th highest item in the stack to the top of the stack
|
||||||
|
|
||||||
|
SWAP1 = 0x90, ///< swaps the highest and second highest value on the stack
|
||||||
|
SWAP2, ///< swaps the highest and third highest value on the stack
|
||||||
|
SWAP3, ///< swaps the highest and 4th highest value on the stack
|
||||||
|
SWAP4, ///< swaps the highest and 5th highest value on the stack
|
||||||
|
SWAP5, ///< swaps the highest and 6th highest value on the stack
|
||||||
|
SWAP6, ///< swaps the highest and 7th highest value on the stack
|
||||||
|
SWAP7, ///< swaps the highest and 8th highest value on the stack
|
||||||
|
SWAP8, ///< swaps the highest and 9th highest value on the stack
|
||||||
|
SWAP9, ///< swaps the highest and 10th highest value on the stack
|
||||||
|
SWAP10, ///< swaps the highest and 11th highest value on the stack
|
||||||
|
SWAP11, ///< swaps the highest and 12th highest value on the stack
|
||||||
|
SWAP12, ///< swaps the highest and 13th highest value on the stack
|
||||||
|
SWAP13, ///< swaps the highest and 14th highest value on the stack
|
||||||
|
SWAP14, ///< swaps the highest and 15th highest value on the stack
|
||||||
|
SWAP15, ///< swaps the highest and 16th highest value on the stack
|
||||||
|
SWAP16, ///< swaps the highest and 17th highest value on the stack
|
||||||
|
|
||||||
|
LOG0 = 0xa0, ///< Makes a log entry; no topics.
|
||||||
|
LOG1, ///< Makes a log entry; 1 topic.
|
||||||
|
LOG2, ///< Makes a log entry; 2 topics.
|
||||||
|
LOG3, ///< Makes a log entry; 3 topics.
|
||||||
|
LOG4, ///< Makes a log entry; 4 topics.
|
||||||
|
|
||||||
|
CREATE = 0xf0, ///< create a new account with associated code
|
||||||
|
CALL, ///< message-call into an account
|
||||||
|
CALLCODE, ///< message-call with another account's code only
|
||||||
|
RETURN, ///< halt execution returning output data
|
||||||
|
DELEGATECALL, ///< like CALLCODE but keeps caller's value and sender
|
||||||
|
SUICIDE = 0xff ///< halt execution and register account for later deletion
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @returns the number of PUSH Instruction _inst
|
||||||
|
inline unsigned getPushNumber(Instruction _inst)
|
||||||
|
{
|
||||||
|
return (byte)_inst - unsigned(Instruction::PUSH1) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @returns the number of DUP Instruction _inst
|
||||||
|
inline unsigned getDupNumber(Instruction _inst)
|
||||||
|
{
|
||||||
|
return (byte)_inst - unsigned(Instruction::DUP1) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @returns the number of SWAP Instruction _inst
|
||||||
|
inline unsigned getSwapNumber(Instruction _inst)
|
||||||
|
{
|
||||||
|
return (byte)_inst - unsigned(Instruction::SWAP1) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @returns the PUSH<_number> instruction
|
||||||
|
inline Instruction pushInstruction(unsigned _number)
|
||||||
|
{
|
||||||
|
assertThrow(1 <= _number && _number <= 32, InvalidOpcode, "Invalid PUSH instruction requested.");
|
||||||
|
return Instruction(unsigned(Instruction::PUSH1) + _number - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @returns the DUP<_number> instruction
|
||||||
|
inline Instruction dupInstruction(unsigned _number)
|
||||||
|
{
|
||||||
|
assertThrow(1 <= _number && _number <= 16, InvalidOpcode, "Invalid DUP instruction requested.");
|
||||||
|
return Instruction(unsigned(Instruction::DUP1) + _number - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @returns the SWAP<_number> instruction
|
||||||
|
inline Instruction swapInstruction(unsigned _number)
|
||||||
|
{
|
||||||
|
assertThrow(1 <= _number && _number <= 16, InvalidOpcode, "Invalid SWAP instruction requested.");
|
||||||
|
return Instruction(unsigned(Instruction::SWAP1) + _number - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @returns the LOG<_number> instruction
|
||||||
|
inline Instruction logInstruction(unsigned _number)
|
||||||
|
{
|
||||||
|
assertThrow(_number <= 4, InvalidOpcode, "Invalid LOG instruction requested.");
|
||||||
|
return Instruction(unsigned(Instruction::LOG0) + _number);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Tier
|
||||||
|
{
|
||||||
|
ZeroTier = 0, // 0, Zero
|
||||||
|
BaseTier, // 2, Quick
|
||||||
|
VeryLowTier, // 3, Fastest
|
||||||
|
LowTier, // 5, Fast
|
||||||
|
MidTier, // 8, Mid
|
||||||
|
HighTier, // 10, Slow
|
||||||
|
ExtTier, // 20, Ext
|
||||||
|
SpecialTier, // multiparam or otherwise special
|
||||||
|
InvalidTier // Invalid.
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Information structure for a particular instruction.
|
||||||
|
struct InstructionInfo
|
||||||
|
{
|
||||||
|
std::string name; ///< The name of the instruction.
|
||||||
|
int additional; ///< Additional items required in memory for this instructions (only for PUSH).
|
||||||
|
int args; ///< Number of items required on the stack for this instruction (and, for the purposes of ret, the number taken from the stack).
|
||||||
|
int ret; ///< Number of items placed (back) on the stack by this instruction, assuming args items were removed.
|
||||||
|
bool sideEffects; ///< false if the only effect on the execution environment (apart from gas usage) is a change to a topmost segment of the stack
|
||||||
|
int gasPriceTier; ///< Tier for gas pricing.
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Information on all the instructions.
|
||||||
|
InstructionInfo instructionInfo(Instruction _inst);
|
||||||
|
|
||||||
|
/// check whether instructions exists
|
||||||
|
bool isValidInstruction(Instruction _inst);
|
||||||
|
|
||||||
|
/// Convert from string mnemonic to Instruction type.
|
||||||
|
extern const std::map<std::string, Instruction> c_instructions;
|
||||||
|
|
||||||
|
/// Iterate through EVM code and call a function on each instruction.
|
||||||
|
void eachInstruction(bytes const& _mem, std::function<void(Instruction,u256 const&)> const& _onInstruction);
|
||||||
|
|
||||||
|
/// Convert from EVM code to simple EVM assembly language.
|
||||||
|
std::string disassemble(bytes const& _mem);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -76,7 +76,7 @@ GasMeter::GasConsumption PathGasMeter::handleQueueItem()
|
|||||||
bool branchStops = false;
|
bool branchStops = false;
|
||||||
jumpTags.clear();
|
jumpTags.clear();
|
||||||
AssemblyItem const& item = m_items.at(index);
|
AssemblyItem const& item = m_items.at(index);
|
||||||
if (item.type() == Tag || item == AssemblyItem(eth::Instruction::JUMPDEST))
|
if (item.type() == Tag || item == AssemblyItem(Instruction::JUMPDEST))
|
||||||
{
|
{
|
||||||
// Do not allow any backwards jump. This is quite restrictive but should work for
|
// Do not allow any backwards jump. This is quite restrictive but should work for
|
||||||
// the simplest things.
|
// the simplest things.
|
||||||
@ -84,14 +84,14 @@ GasMeter::GasConsumption PathGasMeter::handleQueueItem()
|
|||||||
return GasMeter::GasConsumption::infinite();
|
return GasMeter::GasConsumption::infinite();
|
||||||
path->visitedJumpdests.insert(index);
|
path->visitedJumpdests.insert(index);
|
||||||
}
|
}
|
||||||
else if (item == AssemblyItem(eth::Instruction::JUMP))
|
else if (item == AssemblyItem(Instruction::JUMP))
|
||||||
{
|
{
|
||||||
branchStops = true;
|
branchStops = true;
|
||||||
jumpTags = state->tagsInExpression(state->relativeStackElement(0));
|
jumpTags = state->tagsInExpression(state->relativeStackElement(0));
|
||||||
if (jumpTags.empty()) // unknown jump destination
|
if (jumpTags.empty()) // unknown jump destination
|
||||||
return GasMeter::GasConsumption::infinite();
|
return GasMeter::GasConsumption::infinite();
|
||||||
}
|
}
|
||||||
else if (item == AssemblyItem(eth::Instruction::JUMPI))
|
else if (item == AssemblyItem(Instruction::JUMPI))
|
||||||
{
|
{
|
||||||
ExpressionClasses::Id condition = state->relativeStackElement(-1);
|
ExpressionClasses::Id condition = state->relativeStackElement(-1);
|
||||||
if (classes.knownNonZero(condition) || !classes.knownZero(condition))
|
if (classes.knownNonZero(condition) || !classes.knownZero(condition))
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <libevmcore/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
@ -50,9 +50,9 @@ struct SemanticInformation
|
|||||||
/// the information in the current block header, memory, storage or stack.
|
/// the information in the current block header, memory, storage or stack.
|
||||||
static bool isDeterministic(AssemblyItem const& _item);
|
static bool isDeterministic(AssemblyItem const& _item);
|
||||||
/// @returns true if the given instruction modifies memory.
|
/// @returns true if the given instruction modifies memory.
|
||||||
static bool invalidatesMemory(Instruction _instruction);
|
static bool invalidatesMemory(solidity::Instruction _instruction);
|
||||||
/// @returns true if the given instruction modifies storage (even indirectly).
|
/// @returns true if the given instruction modifies storage (even indirectly).
|
||||||
static bool invalidatesStorage(Instruction _instruction);
|
static bool invalidatesStorage(solidity::Instruction _instruction);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
#include <libdevcore/Log.h>
|
#include <libdevcore/Log.h>
|
||||||
#include <libdevcore/CommonIO.h>
|
#include <libdevcore/CommonIO.h>
|
||||||
#include <libevmcore/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
#include "CompilerState.h"
|
#include "CompilerState.h"
|
||||||
#include "Parser.h"
|
#include "Parser.h"
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <libdevcore/Common.h>
|
#include <libdevcore/Common.h>
|
||||||
#include <libevmcore/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
#include <libevmasm/Assembly.h>
|
#include <libevmasm/Assembly.h>
|
||||||
#include "Exceptions.h"
|
#include "Exceptions.h"
|
||||||
|
|
||||||
|
@ -595,7 +595,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
|||||||
if (!varDecl->isLocalVariable())
|
if (!varDecl->isLocalVariable())
|
||||||
return false; // only local variables are inline-assemlby lvalues
|
return false; // only local variables are inline-assemlby lvalues
|
||||||
for (unsigned i = 0; i < declaration->type()->sizeOnStack(); ++i)
|
for (unsigned i = 0; i < declaration->type()->sizeOnStack(); ++i)
|
||||||
_assembly.append(eth::Instruction::POP); // remove value just to verify the stack height
|
_assembly.append(Instruction::POP); // remove value just to verify the stack height
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
#include <libevmasm/SourceLocation.h>
|
#include <libevmasm/SourceLocation.h>
|
||||||
#include <libevmcore/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
#include <libsolidity/interface/Utils.h>
|
#include <libsolidity/interface/Utils.h>
|
||||||
#include <libsolidity/ast/ASTForward.h>
|
#include <libsolidity/ast/ASTForward.h>
|
||||||
#include <libsolidity/parsing/Token.h>
|
#include <libsolidity/parsing/Token.h>
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libsolidity/codegen/ArrayUtils.h>
|
#include <libsolidity/codegen/ArrayUtils.h>
|
||||||
#include <libevmcore/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
#include <libsolidity/codegen/CompilerContext.h>
|
#include <libsolidity/codegen/CompilerContext.h>
|
||||||
#include <libsolidity/codegen/CompilerUtils.h>
|
#include <libsolidity/codegen/CompilerUtils.h>
|
||||||
#include <libsolidity/ast/Types.h>
|
#include <libsolidity/ast/Types.h>
|
||||||
@ -56,7 +56,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
// stack: source_ref [source_length] target_ref
|
// stack: source_ref [source_length] target_ref
|
||||||
// store target_ref
|
// store target_ref
|
||||||
for (unsigned i = _sourceType.sizeOnStack(); i > 0; --i)
|
for (unsigned i = _sourceType.sizeOnStack(); i > 0; --i)
|
||||||
m_context << eth::swapInstruction(i);
|
m_context << swapInstruction(i);
|
||||||
// stack: target_ref source_ref [source_length]
|
// stack: target_ref source_ref [source_length]
|
||||||
// stack: target_ref source_ref [source_length]
|
// stack: target_ref source_ref [source_length]
|
||||||
// retrieve source length
|
// retrieve source length
|
||||||
@ -65,12 +65,12 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
if (_sourceType.location() == DataLocation::Memory && _sourceType.isDynamicallySized())
|
if (_sourceType.location() == DataLocation::Memory && _sourceType.isDynamicallySized())
|
||||||
{
|
{
|
||||||
// increment source pointer to point to data
|
// increment source pointer to point to data
|
||||||
m_context << eth::Instruction::SWAP1 << u256(0x20);
|
m_context << Instruction::SWAP1 << u256(0x20);
|
||||||
m_context << eth::Instruction::ADD << eth::Instruction::SWAP1;
|
m_context << Instruction::ADD << Instruction::SWAP1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// stack: target_ref source_ref source_length
|
// stack: target_ref source_ref source_length
|
||||||
m_context << eth::Instruction::DUP3;
|
m_context << Instruction::DUP3;
|
||||||
// stack: target_ref source_ref source_length target_ref
|
// stack: target_ref source_ref source_length target_ref
|
||||||
retrieveLength(_targetType);
|
retrieveLength(_targetType);
|
||||||
// stack: target_ref source_ref source_length target_ref target_length
|
// stack: target_ref source_ref source_length target_ref target_length
|
||||||
@ -78,28 +78,28 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
// store new target length
|
// store new target length
|
||||||
if (!_targetType.isByteArray())
|
if (!_targetType.isByteArray())
|
||||||
// Otherwise, length will be stored below.
|
// Otherwise, length will be stored below.
|
||||||
m_context << eth::Instruction::DUP3 << eth::Instruction::DUP3 << eth::Instruction::SSTORE;
|
m_context << Instruction::DUP3 << Instruction::DUP3 << Instruction::SSTORE;
|
||||||
if (sourceBaseType->category() == Type::Category::Mapping)
|
if (sourceBaseType->category() == Type::Category::Mapping)
|
||||||
{
|
{
|
||||||
solAssert(targetBaseType->category() == Type::Category::Mapping, "");
|
solAssert(targetBaseType->category() == Type::Category::Mapping, "");
|
||||||
solAssert(_sourceType.location() == DataLocation::Storage, "");
|
solAssert(_sourceType.location() == DataLocation::Storage, "");
|
||||||
// nothing to copy
|
// nothing to copy
|
||||||
m_context
|
m_context
|
||||||
<< eth::Instruction::POP << eth::Instruction::POP
|
<< Instruction::POP << Instruction::POP
|
||||||
<< eth::Instruction::POP << eth::Instruction::POP;
|
<< Instruction::POP << Instruction::POP;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// stack: target_ref source_ref source_length target_ref target_length
|
// stack: target_ref source_ref source_length target_ref target_length
|
||||||
// compute hashes (data positions)
|
// compute hashes (data positions)
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
if (_targetType.isDynamicallySized())
|
if (_targetType.isDynamicallySized())
|
||||||
CompilerUtils(m_context).computeHashStatic();
|
CompilerUtils(m_context).computeHashStatic();
|
||||||
// stack: target_ref source_ref source_length target_length target_data_pos
|
// stack: target_ref source_ref source_length target_length target_data_pos
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
convertLengthToSize(_targetType);
|
convertLengthToSize(_targetType);
|
||||||
m_context << eth::Instruction::DUP2 << eth::Instruction::ADD;
|
m_context << Instruction::DUP2 << Instruction::ADD;
|
||||||
// stack: target_ref source_ref source_length target_data_pos target_data_end
|
// stack: target_ref source_ref source_length target_data_pos target_data_end
|
||||||
m_context << eth::Instruction::SWAP3;
|
m_context << Instruction::SWAP3;
|
||||||
// stack: target_ref target_data_end source_length target_data_pos source_ref
|
// stack: target_ref target_data_end source_length target_data_pos source_ref
|
||||||
|
|
||||||
eth::AssemblyItem copyLoopEndWithoutByteOffset = m_context.newTag();
|
eth::AssemblyItem copyLoopEndWithoutByteOffset = m_context.newTag();
|
||||||
@ -108,47 +108,47 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
if (_targetType.isByteArray())
|
if (_targetType.isByteArray())
|
||||||
{
|
{
|
||||||
// stack: target_ref target_data_end source_length target_data_pos source_ref
|
// stack: target_ref target_data_end source_length target_data_pos source_ref
|
||||||
m_context << eth::Instruction::DUP3 << u256(31) << eth::Instruction::LT;
|
m_context << Instruction::DUP3 << u256(31) << Instruction::LT;
|
||||||
eth::AssemblyItem longByteArray = m_context.appendConditionalJump();
|
eth::AssemblyItem longByteArray = m_context.appendConditionalJump();
|
||||||
// store the short byte array
|
// store the short byte array
|
||||||
solAssert(_sourceType.isByteArray(), "");
|
solAssert(_sourceType.isByteArray(), "");
|
||||||
if (_sourceType.location() == DataLocation::Storage)
|
if (_sourceType.location() == DataLocation::Storage)
|
||||||
{
|
{
|
||||||
// just copy the slot, it contains length and data
|
// just copy the slot, it contains length and data
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
|
m_context << Instruction::DUP1 << Instruction::SLOAD;
|
||||||
m_context << eth::Instruction::DUP6 << eth::Instruction::SSTORE;
|
m_context << Instruction::DUP6 << Instruction::SSTORE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_context << eth::Instruction::DUP1;
|
m_context << Instruction::DUP1;
|
||||||
CompilerUtils(m_context).loadFromMemoryDynamic(*sourceBaseType, fromCalldata, true, false);
|
CompilerUtils(m_context).loadFromMemoryDynamic(*sourceBaseType, fromCalldata, true, false);
|
||||||
// stack: target_ref target_data_end source_length target_data_pos source_ref value
|
// stack: target_ref target_data_end source_length target_data_pos source_ref value
|
||||||
// clear the lower-order byte - which will hold the length
|
// clear the lower-order byte - which will hold the length
|
||||||
m_context << u256(0xff) << eth::Instruction::NOT << eth::Instruction::AND;
|
m_context << u256(0xff) << Instruction::NOT << Instruction::AND;
|
||||||
// fetch the length and shift it left by one
|
// fetch the length and shift it left by one
|
||||||
m_context << eth::Instruction::DUP4 << eth::Instruction::DUP1 << eth::Instruction::ADD;
|
m_context << Instruction::DUP4 << Instruction::DUP1 << Instruction::ADD;
|
||||||
// combine value and length and store them
|
// combine value and length and store them
|
||||||
m_context << eth::Instruction::OR << eth::Instruction::DUP6 << eth::Instruction::SSTORE;
|
m_context << Instruction::OR << Instruction::DUP6 << Instruction::SSTORE;
|
||||||
}
|
}
|
||||||
// end of special case, jump right into cleaning target data area
|
// end of special case, jump right into cleaning target data area
|
||||||
m_context.appendJumpTo(copyLoopEndWithoutByteOffset);
|
m_context.appendJumpTo(copyLoopEndWithoutByteOffset);
|
||||||
m_context << longByteArray;
|
m_context << longByteArray;
|
||||||
// Store length (2*length+1)
|
// Store length (2*length+1)
|
||||||
m_context << eth::Instruction::DUP3 << eth::Instruction::DUP1 << eth::Instruction::ADD;
|
m_context << Instruction::DUP3 << Instruction::DUP1 << Instruction::ADD;
|
||||||
m_context << u256(1) << eth::Instruction::ADD;
|
m_context << u256(1) << Instruction::ADD;
|
||||||
m_context << eth::Instruction::DUP6 << eth::Instruction::SSTORE;
|
m_context << Instruction::DUP6 << Instruction::SSTORE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip copying if source length is zero
|
// skip copying if source length is zero
|
||||||
m_context << eth::Instruction::DUP3 << eth::Instruction::ISZERO;
|
m_context << Instruction::DUP3 << Instruction::ISZERO;
|
||||||
m_context.appendConditionalJumpTo(copyLoopEndWithoutByteOffset);
|
m_context.appendConditionalJumpTo(copyLoopEndWithoutByteOffset);
|
||||||
|
|
||||||
if (_sourceType.location() == DataLocation::Storage && _sourceType.isDynamicallySized())
|
if (_sourceType.location() == DataLocation::Storage && _sourceType.isDynamicallySized())
|
||||||
CompilerUtils(m_context).computeHashStatic();
|
CompilerUtils(m_context).computeHashStatic();
|
||||||
// stack: target_ref target_data_end source_length target_data_pos source_data_pos
|
// stack: target_ref target_data_end source_length target_data_pos source_data_pos
|
||||||
m_context << eth::Instruction::SWAP2;
|
m_context << Instruction::SWAP2;
|
||||||
convertLengthToSize(_sourceType);
|
convertLengthToSize(_sourceType);
|
||||||
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
|
m_context << Instruction::DUP3 << Instruction::ADD;
|
||||||
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end
|
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end
|
||||||
if (haveByteOffsetTarget)
|
if (haveByteOffsetTarget)
|
||||||
m_context << u256(0);
|
m_context << u256(0);
|
||||||
@ -159,8 +159,8 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
m_context << copyLoopStart;
|
m_context << copyLoopStart;
|
||||||
// check for loop condition
|
// check for loop condition
|
||||||
m_context
|
m_context
|
||||||
<< eth::dupInstruction(3 + byteOffsetSize) << eth::dupInstruction(2 + byteOffsetSize)
|
<< dupInstruction(3 + byteOffsetSize) << dupInstruction(2 + byteOffsetSize)
|
||||||
<< eth::Instruction::GT << eth::Instruction::ISZERO;
|
<< Instruction::GT << Instruction::ISZERO;
|
||||||
eth::AssemblyItem copyLoopEnd = m_context.appendConditionalJump();
|
eth::AssemblyItem copyLoopEnd = m_context.appendConditionalJump();
|
||||||
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
|
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
|
||||||
// copy
|
// copy
|
||||||
@ -168,19 +168,19 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
{
|
{
|
||||||
solAssert(byteOffsetSize == 0, "Byte offset for array as base type.");
|
solAssert(byteOffsetSize == 0, "Byte offset for array as base type.");
|
||||||
auto const& sourceBaseArrayType = dynamic_cast<ArrayType const&>(*sourceBaseType);
|
auto const& sourceBaseArrayType = dynamic_cast<ArrayType const&>(*sourceBaseType);
|
||||||
m_context << eth::Instruction::DUP3;
|
m_context << Instruction::DUP3;
|
||||||
if (sourceBaseArrayType.location() == DataLocation::Memory)
|
if (sourceBaseArrayType.location() == DataLocation::Memory)
|
||||||
m_context << eth::Instruction::MLOAD;
|
m_context << Instruction::MLOAD;
|
||||||
m_context << eth::Instruction::DUP3;
|
m_context << Instruction::DUP3;
|
||||||
copyArrayToStorage(dynamic_cast<ArrayType const&>(*targetBaseType), sourceBaseArrayType);
|
copyArrayToStorage(dynamic_cast<ArrayType const&>(*targetBaseType), sourceBaseArrayType);
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
}
|
}
|
||||||
else if (directCopy)
|
else if (directCopy)
|
||||||
{
|
{
|
||||||
solAssert(byteOffsetSize == 0, "Byte offset for direct copy.");
|
solAssert(byteOffsetSize == 0, "Byte offset for direct copy.");
|
||||||
m_context
|
m_context
|
||||||
<< eth::Instruction::DUP3 << eth::Instruction::SLOAD
|
<< Instruction::DUP3 << Instruction::SLOAD
|
||||||
<< eth::Instruction::DUP3 << eth::Instruction::SSTORE;
|
<< Instruction::DUP3 << Instruction::SSTORE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -188,11 +188,11 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
// We might copy too much if there is padding at the last element, but this way end
|
// We might copy too much if there is padding at the last element, but this way end
|
||||||
// checking is easier.
|
// checking is easier.
|
||||||
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
|
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
|
||||||
m_context << eth::dupInstruction(3 + byteOffsetSize);
|
m_context << dupInstruction(3 + byteOffsetSize);
|
||||||
if (_sourceType.location() == DataLocation::Storage)
|
if (_sourceType.location() == DataLocation::Storage)
|
||||||
{
|
{
|
||||||
if (haveByteOffsetSource)
|
if (haveByteOffsetSource)
|
||||||
m_context << eth::Instruction::DUP2;
|
m_context << Instruction::DUP2;
|
||||||
else
|
else
|
||||||
m_context << u256(0);
|
m_context << u256(0);
|
||||||
StorageItem(m_context, *sourceBaseType).retrieveValue(SourceLocation(), true);
|
StorageItem(m_context, *sourceBaseType).retrieveValue(SourceLocation(), true);
|
||||||
@ -207,9 +207,9 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
"Stack too deep, try removing local variables."
|
"Stack too deep, try removing local variables."
|
||||||
);
|
);
|
||||||
// fetch target storage reference
|
// fetch target storage reference
|
||||||
m_context << eth::dupInstruction(2 + byteOffsetSize + sourceBaseType->sizeOnStack());
|
m_context << dupInstruction(2 + byteOffsetSize + sourceBaseType->sizeOnStack());
|
||||||
if (haveByteOffsetTarget)
|
if (haveByteOffsetTarget)
|
||||||
m_context << eth::dupInstruction(1 + byteOffsetSize + sourceBaseType->sizeOnStack());
|
m_context << dupInstruction(1 + byteOffsetSize + sourceBaseType->sizeOnStack());
|
||||||
else
|
else
|
||||||
m_context << u256(0);
|
m_context << u256(0);
|
||||||
StorageItem(m_context, *targetBaseType).storeValue(*sourceBaseType, SourceLocation(), true);
|
StorageItem(m_context, *targetBaseType).storeValue(*sourceBaseType, SourceLocation(), true);
|
||||||
@ -220,7 +220,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
incrementByteOffset(sourceBaseType->storageBytes(), 1, haveByteOffsetTarget ? 5 : 4);
|
incrementByteOffset(sourceBaseType->storageBytes(), 1, haveByteOffsetTarget ? 5 : 4);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_context << eth::swapInstruction(2 + byteOffsetSize);
|
m_context << swapInstruction(2 + byteOffsetSize);
|
||||||
if (sourceIsStorage)
|
if (sourceIsStorage)
|
||||||
m_context << sourceBaseType->storageSize();
|
m_context << sourceBaseType->storageSize();
|
||||||
else if (_sourceType.location() == DataLocation::Memory)
|
else if (_sourceType.location() == DataLocation::Memory)
|
||||||
@ -228,44 +228,44 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
else
|
else
|
||||||
m_context << sourceBaseType->calldataEncodedSize(true);
|
m_context << sourceBaseType->calldataEncodedSize(true);
|
||||||
m_context
|
m_context
|
||||||
<< eth::Instruction::ADD
|
<< Instruction::ADD
|
||||||
<< eth::swapInstruction(2 + byteOffsetSize);
|
<< swapInstruction(2 + byteOffsetSize);
|
||||||
}
|
}
|
||||||
// increment target
|
// increment target
|
||||||
if (haveByteOffsetTarget)
|
if (haveByteOffsetTarget)
|
||||||
incrementByteOffset(targetBaseType->storageBytes(), byteOffsetSize, byteOffsetSize + 2);
|
incrementByteOffset(targetBaseType->storageBytes(), byteOffsetSize, byteOffsetSize + 2);
|
||||||
else
|
else
|
||||||
m_context
|
m_context
|
||||||
<< eth::swapInstruction(1 + byteOffsetSize)
|
<< swapInstruction(1 + byteOffsetSize)
|
||||||
<< targetBaseType->storageSize()
|
<< targetBaseType->storageSize()
|
||||||
<< eth::Instruction::ADD
|
<< Instruction::ADD
|
||||||
<< eth::swapInstruction(1 + byteOffsetSize);
|
<< swapInstruction(1 + byteOffsetSize);
|
||||||
m_context.appendJumpTo(copyLoopStart);
|
m_context.appendJumpTo(copyLoopStart);
|
||||||
m_context << copyLoopEnd;
|
m_context << copyLoopEnd;
|
||||||
if (haveByteOffsetTarget)
|
if (haveByteOffsetTarget)
|
||||||
{
|
{
|
||||||
// clear elements that might be left over in the current slot in target
|
// clear elements that might be left over in the current slot in target
|
||||||
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end target_byte_offset [source_byte_offset]
|
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end target_byte_offset [source_byte_offset]
|
||||||
m_context << eth::dupInstruction(byteOffsetSize) << eth::Instruction::ISZERO;
|
m_context << dupInstruction(byteOffsetSize) << Instruction::ISZERO;
|
||||||
eth::AssemblyItem copyCleanupLoopEnd = m_context.appendConditionalJump();
|
eth::AssemblyItem copyCleanupLoopEnd = m_context.appendConditionalJump();
|
||||||
m_context << eth::dupInstruction(2 + byteOffsetSize) << eth::dupInstruction(1 + byteOffsetSize);
|
m_context << dupInstruction(2 + byteOffsetSize) << dupInstruction(1 + byteOffsetSize);
|
||||||
StorageItem(m_context, *targetBaseType).setToZero(SourceLocation(), true);
|
StorageItem(m_context, *targetBaseType).setToZero(SourceLocation(), true);
|
||||||
incrementByteOffset(targetBaseType->storageBytes(), byteOffsetSize, byteOffsetSize + 2);
|
incrementByteOffset(targetBaseType->storageBytes(), byteOffsetSize, byteOffsetSize + 2);
|
||||||
m_context.appendJumpTo(copyLoopEnd);
|
m_context.appendJumpTo(copyLoopEnd);
|
||||||
|
|
||||||
m_context << copyCleanupLoopEnd;
|
m_context << copyCleanupLoopEnd;
|
||||||
m_context << eth::Instruction::POP; // might pop the source, but then target is popped next
|
m_context << Instruction::POP; // might pop the source, but then target is popped next
|
||||||
}
|
}
|
||||||
if (haveByteOffsetSource)
|
if (haveByteOffsetSource)
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
m_context << copyLoopEndWithoutByteOffset;
|
m_context << copyLoopEndWithoutByteOffset;
|
||||||
|
|
||||||
// zero-out leftovers in target
|
// zero-out leftovers in target
|
||||||
// stack: target_ref target_data_end source_data_pos target_data_pos_updated source_data_end
|
// stack: target_ref target_data_end source_data_pos target_data_pos_updated source_data_end
|
||||||
m_context << eth::Instruction::POP << eth::Instruction::SWAP1 << eth::Instruction::POP;
|
m_context << Instruction::POP << Instruction::SWAP1 << Instruction::POP;
|
||||||
// stack: target_ref target_data_end target_data_pos_updated
|
// stack: target_ref target_data_end target_data_pos_updated
|
||||||
clearStorageLoop(*targetBaseType);
|
clearStorageLoop(*targetBaseType);
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWordBoundaries) const
|
void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWordBoundaries) const
|
||||||
@ -285,13 +285,13 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord
|
|||||||
if (!_sourceType.isDynamicallySized())
|
if (!_sourceType.isDynamicallySized())
|
||||||
m_context << _sourceType.length();
|
m_context << _sourceType.length();
|
||||||
if (baseSize > 1)
|
if (baseSize > 1)
|
||||||
m_context << u256(baseSize) << eth::Instruction::MUL;
|
m_context << u256(baseSize) << Instruction::MUL;
|
||||||
// stack: target source_offset source_len
|
// stack: target source_offset source_len
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::DUP5;
|
m_context << Instruction::DUP1 << Instruction::DUP3 << Instruction::DUP5;
|
||||||
// stack: target source_offset source_len source_len source_offset target
|
// stack: target source_offset source_len source_len source_offset target
|
||||||
m_context << eth::Instruction::CALLDATACOPY;
|
m_context << Instruction::CALLDATACOPY;
|
||||||
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
|
m_context << Instruction::DUP3 << Instruction::ADD;
|
||||||
m_context << eth::Instruction::SWAP2 << eth::Instruction::POP << eth::Instruction::POP;
|
m_context << Instruction::SWAP2 << Instruction::POP << Instruction::POP;
|
||||||
}
|
}
|
||||||
else if (_sourceType.location() == DataLocation::Memory)
|
else if (_sourceType.location() == DataLocation::Memory)
|
||||||
{
|
{
|
||||||
@ -300,25 +300,25 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord
|
|||||||
if (!_sourceType.baseType()->isValueType())
|
if (!_sourceType.baseType()->isValueType())
|
||||||
{
|
{
|
||||||
// copy using a loop
|
// copy using a loop
|
||||||
m_context << u256(0) << eth::Instruction::SWAP3;
|
m_context << u256(0) << Instruction::SWAP3;
|
||||||
// stack: counter source length target
|
// stack: counter source length target
|
||||||
auto repeat = m_context.newTag();
|
auto repeat = m_context.newTag();
|
||||||
m_context << repeat;
|
m_context << repeat;
|
||||||
m_context << eth::Instruction::DUP2 << eth::Instruction::DUP5;
|
m_context << Instruction::DUP2 << Instruction::DUP5;
|
||||||
m_context << eth::Instruction::LT << eth::Instruction::ISZERO;
|
m_context << Instruction::LT << Instruction::ISZERO;
|
||||||
auto loopEnd = m_context.appendConditionalJump();
|
auto loopEnd = m_context.appendConditionalJump();
|
||||||
m_context << eth::Instruction::DUP3 << eth::Instruction::DUP5;
|
m_context << Instruction::DUP3 << Instruction::DUP5;
|
||||||
accessIndex(_sourceType, false);
|
accessIndex(_sourceType, false);
|
||||||
MemoryItem(m_context, *_sourceType.baseType(), true).retrieveValue(SourceLocation(), true);
|
MemoryItem(m_context, *_sourceType.baseType(), true).retrieveValue(SourceLocation(), true);
|
||||||
if (auto baseArray = dynamic_cast<ArrayType const*>(_sourceType.baseType().get()))
|
if (auto baseArray = dynamic_cast<ArrayType const*>(_sourceType.baseType().get()))
|
||||||
copyArrayToMemory(*baseArray, _padToWordBoundaries);
|
copyArrayToMemory(*baseArray, _padToWordBoundaries);
|
||||||
else
|
else
|
||||||
utils.storeInMemoryDynamic(*_sourceType.baseType());
|
utils.storeInMemoryDynamic(*_sourceType.baseType());
|
||||||
m_context << eth::Instruction::SWAP3 << u256(1) << eth::Instruction::ADD;
|
m_context << Instruction::SWAP3 << u256(1) << Instruction::ADD;
|
||||||
m_context << eth::Instruction::SWAP3;
|
m_context << Instruction::SWAP3;
|
||||||
m_context.appendJumpTo(repeat);
|
m_context.appendJumpTo(repeat);
|
||||||
m_context << loopEnd;
|
m_context << loopEnd;
|
||||||
m_context << eth::Instruction::SWAP3;
|
m_context << Instruction::SWAP3;
|
||||||
utils.popStackSlots(3);
|
utils.popStackSlots(3);
|
||||||
// stack: updated_target_pos
|
// stack: updated_target_pos
|
||||||
return;
|
return;
|
||||||
@ -328,18 +328,18 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord
|
|||||||
if (_sourceType.isDynamicallySized())
|
if (_sourceType.isDynamicallySized())
|
||||||
{
|
{
|
||||||
// change pointer to data part
|
// change pointer to data part
|
||||||
m_context << eth::Instruction::SWAP1 << u256(32) << eth::Instruction::ADD;
|
m_context << Instruction::SWAP1 << u256(32) << Instruction::ADD;
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
}
|
}
|
||||||
// convert length to size
|
// convert length to size
|
||||||
if (baseSize > 1)
|
if (baseSize > 1)
|
||||||
m_context << u256(baseSize) << eth::Instruction::MUL;
|
m_context << u256(baseSize) << Instruction::MUL;
|
||||||
// stack: <target> <source> <size>
|
// stack: <target> <source> <size>
|
||||||
//@TODO do not use ::CALL if less than 32 bytes?
|
//@TODO do not use ::CALL if less than 32 bytes?
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP4 << eth::Instruction::DUP4;
|
m_context << Instruction::DUP1 << Instruction::DUP4 << Instruction::DUP4;
|
||||||
utils.memoryCopy();
|
utils.memoryCopy();
|
||||||
|
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
|
m_context << Instruction::SWAP1 << Instruction::POP;
|
||||||
// stack: <target> <size>
|
// stack: <target> <size>
|
||||||
|
|
||||||
bool paddingNeeded = false;
|
bool paddingNeeded = false;
|
||||||
@ -350,43 +350,43 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord
|
|||||||
if (paddingNeeded)
|
if (paddingNeeded)
|
||||||
{
|
{
|
||||||
// stack: <target> <size>
|
// stack: <target> <size>
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2 << eth::Instruction::ADD;
|
m_context << Instruction::SWAP1 << Instruction::DUP2 << Instruction::ADD;
|
||||||
// stack: <length> <target + size>
|
// stack: <length> <target + size>
|
||||||
m_context << eth::Instruction::SWAP1 << u256(31) << eth::Instruction::AND;
|
m_context << Instruction::SWAP1 << u256(31) << Instruction::AND;
|
||||||
// stack: <target + size> <remainder = size % 32>
|
// stack: <target + size> <remainder = size % 32>
|
||||||
eth::AssemblyItem skip = m_context.newTag();
|
eth::AssemblyItem skip = m_context.newTag();
|
||||||
if (_sourceType.isDynamicallySized())
|
if (_sourceType.isDynamicallySized())
|
||||||
{
|
{
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::ISZERO;
|
m_context << Instruction::DUP1 << Instruction::ISZERO;
|
||||||
m_context.appendConditionalJumpTo(skip);
|
m_context.appendConditionalJumpTo(skip);
|
||||||
}
|
}
|
||||||
// round off, load from there.
|
// round off, load from there.
|
||||||
// stack <target + size> <remainder = size % 32>
|
// stack <target + size> <remainder = size % 32>
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3;
|
m_context << Instruction::DUP1 << Instruction::DUP3;
|
||||||
m_context << eth::Instruction::SUB;
|
m_context << Instruction::SUB;
|
||||||
// stack: target+size remainder <target + size - remainder>
|
// stack: target+size remainder <target + size - remainder>
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::MLOAD;
|
m_context << Instruction::DUP1 << Instruction::MLOAD;
|
||||||
// Now we AND it with ~(2**(8 * (32 - remainder)) - 1)
|
// Now we AND it with ~(2**(8 * (32 - remainder)) - 1)
|
||||||
m_context << u256(1);
|
m_context << u256(1);
|
||||||
m_context << eth::Instruction::DUP4 << u256(32) << eth::Instruction::SUB;
|
m_context << Instruction::DUP4 << u256(32) << Instruction::SUB;
|
||||||
// stack: ...<v> 1 <32 - remainder>
|
// stack: ...<v> 1 <32 - remainder>
|
||||||
m_context << u256(0x100) << eth::Instruction::EXP << eth::Instruction::SUB;
|
m_context << u256(0x100) << Instruction::EXP << Instruction::SUB;
|
||||||
m_context << eth::Instruction::NOT << eth::Instruction::AND;
|
m_context << Instruction::NOT << Instruction::AND;
|
||||||
// stack: target+size remainder target+size-remainder <v & ...>
|
// stack: target+size remainder target+size-remainder <v & ...>
|
||||||
m_context << eth::Instruction::DUP2 << eth::Instruction::MSTORE;
|
m_context << Instruction::DUP2 << Instruction::MSTORE;
|
||||||
// stack: target+size remainder target+size-remainder
|
// stack: target+size remainder target+size-remainder
|
||||||
m_context << u256(32) << eth::Instruction::ADD;
|
m_context << u256(32) << Instruction::ADD;
|
||||||
// stack: target+size remainder <new_padded_end>
|
// stack: target+size remainder <new_padded_end>
|
||||||
m_context << eth::Instruction::SWAP2 << eth::Instruction::POP;
|
m_context << Instruction::SWAP2 << Instruction::POP;
|
||||||
|
|
||||||
if (_sourceType.isDynamicallySized())
|
if (_sourceType.isDynamicallySized())
|
||||||
m_context << skip.tag();
|
m_context << skip.tag();
|
||||||
// stack <target + "size"> <remainder = size % 32>
|
// stack <target + "size"> <remainder = size % 32>
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
// stack: <target> <size>
|
// stack: <target> <size>
|
||||||
m_context << eth::Instruction::ADD;
|
m_context << Instruction::ADD;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -398,48 +398,48 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord
|
|||||||
retrieveLength(_sourceType);
|
retrieveLength(_sourceType);
|
||||||
// stack here: memory_offset storage_offset length
|
// stack here: memory_offset storage_offset length
|
||||||
// jump to end if length is zero
|
// jump to end if length is zero
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::ISZERO;
|
m_context << Instruction::DUP1 << Instruction::ISZERO;
|
||||||
eth::AssemblyItem loopEnd = m_context.appendConditionalJump();
|
eth::AssemblyItem loopEnd = m_context.appendConditionalJump();
|
||||||
// Special case for tightly-stored byte arrays
|
// Special case for tightly-stored byte arrays
|
||||||
if (_sourceType.isByteArray())
|
if (_sourceType.isByteArray())
|
||||||
{
|
{
|
||||||
// stack here: memory_offset storage_offset length
|
// stack here: memory_offset storage_offset length
|
||||||
m_context << eth::Instruction::DUP1 << u256(31) << eth::Instruction::LT;
|
m_context << Instruction::DUP1 << u256(31) << Instruction::LT;
|
||||||
eth::AssemblyItem longByteArray = m_context.appendConditionalJump();
|
eth::AssemblyItem longByteArray = m_context.appendConditionalJump();
|
||||||
// store the short byte array (discard lower-order byte)
|
// store the short byte array (discard lower-order byte)
|
||||||
m_context << u256(0x100) << eth::Instruction::DUP1;
|
m_context << u256(0x100) << Instruction::DUP1;
|
||||||
m_context << eth::Instruction::DUP4 << eth::Instruction::SLOAD;
|
m_context << Instruction::DUP4 << Instruction::SLOAD;
|
||||||
m_context << eth::Instruction::DIV << eth::Instruction::MUL;
|
m_context << Instruction::DIV << Instruction::MUL;
|
||||||
m_context << eth::Instruction::DUP4 << eth::Instruction::MSTORE;
|
m_context << Instruction::DUP4 << Instruction::MSTORE;
|
||||||
// stack here: memory_offset storage_offset length
|
// stack here: memory_offset storage_offset length
|
||||||
// add 32 or length to memory offset
|
// add 32 or length to memory offset
|
||||||
m_context << eth::Instruction::SWAP2;
|
m_context << Instruction::SWAP2;
|
||||||
if (_padToWordBoundaries)
|
if (_padToWordBoundaries)
|
||||||
m_context << u256(32);
|
m_context << u256(32);
|
||||||
else
|
else
|
||||||
m_context << eth::Instruction::DUP3;
|
m_context << Instruction::DUP3;
|
||||||
m_context << eth::Instruction::ADD;
|
m_context << Instruction::ADD;
|
||||||
m_context << eth::Instruction::SWAP2;
|
m_context << Instruction::SWAP2;
|
||||||
m_context.appendJumpTo(loopEnd);
|
m_context.appendJumpTo(loopEnd);
|
||||||
m_context << longByteArray;
|
m_context << longByteArray;
|
||||||
}
|
}
|
||||||
// compute memory end offset
|
// compute memory end offset
|
||||||
if (baseSize > 1)
|
if (baseSize > 1)
|
||||||
// convert length to memory size
|
// convert length to memory size
|
||||||
m_context << u256(baseSize) << eth::Instruction::MUL;
|
m_context << u256(baseSize) << Instruction::MUL;
|
||||||
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD << eth::Instruction::SWAP2;
|
m_context << Instruction::DUP3 << Instruction::ADD << Instruction::SWAP2;
|
||||||
if (_sourceType.isDynamicallySized())
|
if (_sourceType.isDynamicallySized())
|
||||||
{
|
{
|
||||||
// actual array data is stored at SHA3(storage_offset)
|
// actual array data is stored at SHA3(storage_offset)
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
utils.computeHashStatic();
|
utils.computeHashStatic();
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// stack here: memory_end_offset storage_data_offset memory_offset
|
// stack here: memory_end_offset storage_data_offset memory_offset
|
||||||
bool haveByteOffset = !_sourceType.isByteArray() && storageBytes <= 16;
|
bool haveByteOffset = !_sourceType.isByteArray() && storageBytes <= 16;
|
||||||
if (haveByteOffset)
|
if (haveByteOffset)
|
||||||
m_context << u256(0) << eth::Instruction::SWAP1;
|
m_context << u256(0) << Instruction::SWAP1;
|
||||||
// stack here: memory_end_offset storage_data_offset [storage_byte_offset] memory_offset
|
// stack here: memory_end_offset storage_data_offset [storage_byte_offset] memory_offset
|
||||||
eth::AssemblyItem loopStart = m_context.newTag();
|
eth::AssemblyItem loopStart = m_context.newTag();
|
||||||
m_context << loopStart;
|
m_context << loopStart;
|
||||||
@ -447,20 +447,20 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord
|
|||||||
if (_sourceType.isByteArray())
|
if (_sourceType.isByteArray())
|
||||||
{
|
{
|
||||||
// Packed both in storage and memory.
|
// Packed both in storage and memory.
|
||||||
m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD;
|
m_context << Instruction::DUP2 << Instruction::SLOAD;
|
||||||
m_context << eth::Instruction::DUP2 << eth::Instruction::MSTORE;
|
m_context << Instruction::DUP2 << Instruction::MSTORE;
|
||||||
// increment storage_data_offset by 1
|
// increment storage_data_offset by 1
|
||||||
m_context << eth::Instruction::SWAP1 << u256(1) << eth::Instruction::ADD;
|
m_context << Instruction::SWAP1 << u256(1) << Instruction::ADD;
|
||||||
// increment memory offset by 32
|
// increment memory offset by 32
|
||||||
m_context << eth::Instruction::SWAP1 << u256(32) << eth::Instruction::ADD;
|
m_context << Instruction::SWAP1 << u256(32) << Instruction::ADD;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// stack here: memory_end_offset storage_data_offset [storage_byte_offset] memory_offset
|
// stack here: memory_end_offset storage_data_offset [storage_byte_offset] memory_offset
|
||||||
if (haveByteOffset)
|
if (haveByteOffset)
|
||||||
m_context << eth::Instruction::DUP3 << eth::Instruction::DUP3;
|
m_context << Instruction::DUP3 << Instruction::DUP3;
|
||||||
else
|
else
|
||||||
m_context << eth::Instruction::DUP2 << u256(0);
|
m_context << Instruction::DUP2 << u256(0);
|
||||||
StorageItem(m_context, *_sourceType.baseType()).retrieveValue(SourceLocation(), true);
|
StorageItem(m_context, *_sourceType.baseType()).retrieveValue(SourceLocation(), true);
|
||||||
if (auto baseArray = dynamic_cast<ArrayType const*>(_sourceType.baseType().get()))
|
if (auto baseArray = dynamic_cast<ArrayType const*>(_sourceType.baseType().get()))
|
||||||
copyArrayToMemory(*baseArray, _padToWordBoundaries);
|
copyArrayToMemory(*baseArray, _padToWordBoundaries);
|
||||||
@ -471,29 +471,29 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord
|
|||||||
incrementByteOffset(storageBytes, 2, 3);
|
incrementByteOffset(storageBytes, 2, 3);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
m_context << storageSize << eth::Instruction::ADD;
|
m_context << storageSize << Instruction::ADD;
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// check for loop condition
|
// check for loop condition
|
||||||
m_context << eth::Instruction::DUP1 << eth::dupInstruction(haveByteOffset ? 5 : 4);
|
m_context << Instruction::DUP1 << dupInstruction(haveByteOffset ? 5 : 4);
|
||||||
m_context << eth::Instruction::GT;
|
m_context << Instruction::GT;
|
||||||
m_context.appendConditionalJumpTo(loopStart);
|
m_context.appendConditionalJumpTo(loopStart);
|
||||||
// stack here: memory_end_offset storage_data_offset [storage_byte_offset] memory_offset
|
// stack here: memory_end_offset storage_data_offset [storage_byte_offset] memory_offset
|
||||||
if (haveByteOffset)
|
if (haveByteOffset)
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
|
m_context << Instruction::SWAP1 << Instruction::POP;
|
||||||
if (_padToWordBoundaries && baseSize % 32 != 0)
|
if (_padToWordBoundaries && baseSize % 32 != 0)
|
||||||
{
|
{
|
||||||
// memory_end_offset - start is the actual length (we want to compute the ceil of).
|
// memory_end_offset - start is the actual length (we want to compute the ceil of).
|
||||||
// memory_offset - start is its next multiple of 32, but it might be off by 32.
|
// memory_offset - start is its next multiple of 32, but it might be off by 32.
|
||||||
// so we compute: memory_end_offset += (memory_offset - memory_end_offest) & 31
|
// so we compute: memory_end_offset += (memory_offset - memory_end_offest) & 31
|
||||||
m_context << eth::Instruction::DUP3 << eth::Instruction::SWAP1 << eth::Instruction::SUB;
|
m_context << Instruction::DUP3 << Instruction::SWAP1 << Instruction::SUB;
|
||||||
m_context << u256(31) << eth::Instruction::AND;
|
m_context << u256(31) << Instruction::AND;
|
||||||
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
|
m_context << Instruction::DUP3 << Instruction::ADD;
|
||||||
m_context << eth::Instruction::SWAP2;
|
m_context << Instruction::SWAP2;
|
||||||
}
|
}
|
||||||
m_context << loopEnd << eth::Instruction::POP << eth::Instruction::POP;
|
m_context << loopEnd << Instruction::POP << Instruction::POP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -509,20 +509,20 @@ void ArrayUtils::clearArray(ArrayType const& _type) const
|
|||||||
if (_type.baseType()->isValueType())
|
if (_type.baseType()->isValueType())
|
||||||
solAssert(_type.baseType()->storageSize() <= 1, "Invalid size for value type.");
|
solAssert(_type.baseType()->storageSize() <= 1, "Invalid size for value type.");
|
||||||
|
|
||||||
m_context << eth::Instruction::POP; // remove byte offset
|
m_context << Instruction::POP; // remove byte offset
|
||||||
if (_type.isDynamicallySized())
|
if (_type.isDynamicallySized())
|
||||||
clearDynamicArray(_type);
|
clearDynamicArray(_type);
|
||||||
else if (_type.length() == 0 || _type.baseType()->category() == Type::Category::Mapping)
|
else if (_type.length() == 0 || _type.baseType()->category() == Type::Category::Mapping)
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
else if (_type.baseType()->isValueType() && _type.storageSize() <= 5)
|
else if (_type.baseType()->isValueType() && _type.storageSize() <= 5)
|
||||||
{
|
{
|
||||||
// unroll loop for small arrays @todo choose a good value
|
// unroll loop for small arrays @todo choose a good value
|
||||||
// Note that we loop over storage slots here, not elements.
|
// Note that we loop over storage slots here, not elements.
|
||||||
for (unsigned i = 1; i < _type.storageSize(); ++i)
|
for (unsigned i = 1; i < _type.storageSize(); ++i)
|
||||||
m_context
|
m_context
|
||||||
<< u256(0) << eth::Instruction::DUP2 << eth::Instruction::SSTORE
|
<< u256(0) << Instruction::DUP2 << Instruction::SSTORE
|
||||||
<< u256(1) << eth::Instruction::ADD;
|
<< u256(1) << Instruction::ADD;
|
||||||
m_context << u256(0) << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
|
m_context << u256(0) << Instruction::SWAP1 << Instruction::SSTORE;
|
||||||
}
|
}
|
||||||
else if (!_type.baseType()->isValueType() && _type.length() <= 4)
|
else if (!_type.baseType()->isValueType() && _type.length() <= 4)
|
||||||
{
|
{
|
||||||
@ -533,22 +533,22 @@ void ArrayUtils::clearArray(ArrayType const& _type) const
|
|||||||
m_context << u256(0);
|
m_context << u256(0);
|
||||||
StorageItem(m_context, *_type.baseType()).setToZero(SourceLocation(), false);
|
StorageItem(m_context, *_type.baseType()).setToZero(SourceLocation(), false);
|
||||||
m_context
|
m_context
|
||||||
<< eth::Instruction::POP
|
<< Instruction::POP
|
||||||
<< u256(_type.baseType()->storageSize()) << eth::Instruction::ADD;
|
<< u256(_type.baseType()->storageSize()) << Instruction::ADD;
|
||||||
}
|
}
|
||||||
m_context << u256(0);
|
m_context << u256(0);
|
||||||
StorageItem(m_context, *_type.baseType()).setToZero(SourceLocation(), true);
|
StorageItem(m_context, *_type.baseType()).setToZero(SourceLocation(), true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_context << eth::Instruction::DUP1 << _type.length();
|
m_context << Instruction::DUP1 << _type.length();
|
||||||
convertLengthToSize(_type);
|
convertLengthToSize(_type);
|
||||||
m_context << eth::Instruction::ADD << eth::Instruction::SWAP1;
|
m_context << Instruction::ADD << Instruction::SWAP1;
|
||||||
if (_type.baseType()->storageBytes() < 32)
|
if (_type.baseType()->storageBytes() < 32)
|
||||||
clearStorageLoop(IntegerType(256));
|
clearStorageLoop(IntegerType(256));
|
||||||
else
|
else
|
||||||
clearStorageLoop(*_type.baseType());
|
clearStorageLoop(*_type.baseType());
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
}
|
}
|
||||||
solAssert(m_context.stackHeight() == stackHeightStart - 2, "");
|
solAssert(m_context.stackHeight() == stackHeightStart - 2, "");
|
||||||
}
|
}
|
||||||
@ -561,15 +561,15 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
|
|||||||
// fetch length
|
// fetch length
|
||||||
retrieveLength(_type);
|
retrieveLength(_type);
|
||||||
// set length to zero
|
// set length to zero
|
||||||
m_context << u256(0) << eth::Instruction::DUP3 << eth::Instruction::SSTORE;
|
m_context << u256(0) << Instruction::DUP3 << Instruction::SSTORE;
|
||||||
// Special case: short byte arrays are stored togeher with their length
|
// Special case: short byte arrays are stored togeher with their length
|
||||||
eth::AssemblyItem endTag = m_context.newTag();
|
eth::AssemblyItem endTag = m_context.newTag();
|
||||||
if (_type.isByteArray())
|
if (_type.isByteArray())
|
||||||
{
|
{
|
||||||
// stack: ref old_length
|
// stack: ref old_length
|
||||||
m_context << eth::Instruction::DUP1 << u256(31) << eth::Instruction::LT;
|
m_context << Instruction::DUP1 << u256(31) << Instruction::LT;
|
||||||
eth::AssemblyItem longByteArray = m_context.appendConditionalJump();
|
eth::AssemblyItem longByteArray = m_context.appendConditionalJump();
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
m_context.appendJumpTo(endTag);
|
m_context.appendJumpTo(endTag);
|
||||||
m_context.adjustStackOffset(1); // needed because of jump
|
m_context.adjustStackOffset(1); // needed because of jump
|
||||||
m_context << longByteArray;
|
m_context << longByteArray;
|
||||||
@ -577,11 +577,11 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
|
|||||||
// stack: ref old_length
|
// stack: ref old_length
|
||||||
convertLengthToSize(_type);
|
convertLengthToSize(_type);
|
||||||
// compute data positions
|
// compute data positions
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
CompilerUtils(m_context).computeHashStatic();
|
CompilerUtils(m_context).computeHashStatic();
|
||||||
// stack: len data_pos
|
// stack: len data_pos
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2 << eth::Instruction::ADD
|
m_context << Instruction::SWAP1 << Instruction::DUP2 << Instruction::ADD
|
||||||
<< eth::Instruction::SWAP1;
|
<< Instruction::SWAP1;
|
||||||
// stack: data_pos_end data_pos
|
// stack: data_pos_end data_pos
|
||||||
if (_type.isByteArray() || _type.baseType()->storageBytes() < 32)
|
if (_type.isByteArray() || _type.baseType()->storageBytes() < 32)
|
||||||
clearStorageLoop(IntegerType(256));
|
clearStorageLoop(IntegerType(256));
|
||||||
@ -589,7 +589,7 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
|
|||||||
clearStorageLoop(*_type.baseType());
|
clearStorageLoop(*_type.baseType());
|
||||||
// cleanup
|
// cleanup
|
||||||
m_context << endTag;
|
m_context << endTag;
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArrayUtils::resizeDynamicArray(ArrayType const& _type) const
|
void ArrayUtils::resizeDynamicArray(ArrayType const& _type) const
|
||||||
@ -614,13 +614,13 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _type) const
|
|||||||
eth::AssemblyItem regularPath = m_context.newTag();
|
eth::AssemblyItem regularPath = m_context.newTag();
|
||||||
// We start by a large case-distinction about the old and new length of the byte array.
|
// We start by a large case-distinction about the old and new length of the byte array.
|
||||||
|
|
||||||
m_context << eth::Instruction::DUP3 << eth::Instruction::SLOAD;
|
m_context << Instruction::DUP3 << Instruction::SLOAD;
|
||||||
// stack: ref new_length current_length ref_value
|
// stack: ref new_length current_length ref_value
|
||||||
|
|
||||||
solAssert(m_context.stackHeight() - stackHeightStart == 4 - 2, "3");
|
solAssert(m_context.stackHeight() - stackHeightStart == 4 - 2, "3");
|
||||||
m_context << eth::Instruction::DUP2 << u256(31) << eth::Instruction::LT;
|
m_context << Instruction::DUP2 << u256(31) << Instruction::LT;
|
||||||
eth::AssemblyItem currentIsLong = m_context.appendConditionalJump();
|
eth::AssemblyItem currentIsLong = m_context.appendConditionalJump();
|
||||||
m_context << eth::Instruction::DUP3 << u256(31) << eth::Instruction::LT;
|
m_context << Instruction::DUP3 << u256(31) << Instruction::LT;
|
||||||
eth::AssemblyItem newIsLong = m_context.appendConditionalJump();
|
eth::AssemblyItem newIsLong = m_context.appendConditionalJump();
|
||||||
|
|
||||||
// Here: short -> short
|
// Here: short -> short
|
||||||
@ -628,17 +628,17 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _type) const
|
|||||||
// Compute 1 << (256 - 8 * new_size)
|
// Compute 1 << (256 - 8 * new_size)
|
||||||
eth::AssemblyItem shortToShort = m_context.newTag();
|
eth::AssemblyItem shortToShort = m_context.newTag();
|
||||||
m_context << shortToShort;
|
m_context << shortToShort;
|
||||||
m_context << eth::Instruction::DUP3 << u256(8) << eth::Instruction::MUL;
|
m_context << Instruction::DUP3 << u256(8) << Instruction::MUL;
|
||||||
m_context << u256(0x100) << eth::Instruction::SUB;
|
m_context << u256(0x100) << Instruction::SUB;
|
||||||
m_context << u256(2) << eth::Instruction::EXP;
|
m_context << u256(2) << Instruction::EXP;
|
||||||
// Divide and multiply by that value, clearing bits.
|
// Divide and multiply by that value, clearing bits.
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::SWAP2;
|
m_context << Instruction::DUP1 << Instruction::SWAP2;
|
||||||
m_context << eth::Instruction::DIV << eth::Instruction::MUL;
|
m_context << Instruction::DIV << Instruction::MUL;
|
||||||
// Insert 2*length.
|
// Insert 2*length.
|
||||||
m_context << eth::Instruction::DUP3 << eth::Instruction::DUP1 << eth::Instruction::ADD;
|
m_context << Instruction::DUP3 << Instruction::DUP1 << Instruction::ADD;
|
||||||
m_context << eth::Instruction::OR;
|
m_context << Instruction::OR;
|
||||||
// Store.
|
// Store.
|
||||||
m_context << eth::Instruction::DUP4 << eth::Instruction::SSTORE;
|
m_context << Instruction::DUP4 << Instruction::SSTORE;
|
||||||
solAssert(m_context.stackHeight() - stackHeightStart == 3 - 2, "3");
|
solAssert(m_context.stackHeight() - stackHeightStart == 3 - 2, "3");
|
||||||
m_context.appendJumpTo(resizeEnd);
|
m_context.appendJumpTo(resizeEnd);
|
||||||
|
|
||||||
@ -649,24 +649,24 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _type) const
|
|||||||
// stack: ref new_length current_length ref_value
|
// stack: ref new_length current_length ref_value
|
||||||
solAssert(m_context.stackHeight() - stackHeightStart == 4 - 2, "3");
|
solAssert(m_context.stackHeight() - stackHeightStart == 4 - 2, "3");
|
||||||
// Zero out lower-order byte.
|
// Zero out lower-order byte.
|
||||||
m_context << u256(0xff) << eth::Instruction::NOT << eth::Instruction::AND;
|
m_context << u256(0xff) << Instruction::NOT << Instruction::AND;
|
||||||
// Store at data location.
|
// Store at data location.
|
||||||
m_context << eth::Instruction::DUP4;
|
m_context << Instruction::DUP4;
|
||||||
CompilerUtils(m_context).computeHashStatic();
|
CompilerUtils(m_context).computeHashStatic();
|
||||||
m_context << eth::Instruction::SSTORE;
|
m_context << Instruction::SSTORE;
|
||||||
// stack: ref new_length current_length
|
// stack: ref new_length current_length
|
||||||
// Store new length: Compule 2*length + 1 and store it.
|
// Store new length: Compule 2*length + 1 and store it.
|
||||||
m_context << eth::Instruction::DUP2 << eth::Instruction::DUP1 << eth::Instruction::ADD;
|
m_context << Instruction::DUP2 << Instruction::DUP1 << Instruction::ADD;
|
||||||
m_context << u256(1) << eth::Instruction::ADD;
|
m_context << u256(1) << Instruction::ADD;
|
||||||
// stack: ref new_length current_length 2*new_length+1
|
// stack: ref new_length current_length 2*new_length+1
|
||||||
m_context << eth::Instruction::DUP4 << eth::Instruction::SSTORE;
|
m_context << Instruction::DUP4 << Instruction::SSTORE;
|
||||||
solAssert(m_context.stackHeight() - stackHeightStart == 3 - 2, "3");
|
solAssert(m_context.stackHeight() - stackHeightStart == 3 - 2, "3");
|
||||||
m_context.appendJumpTo(resizeEnd);
|
m_context.appendJumpTo(resizeEnd);
|
||||||
|
|
||||||
m_context.adjustStackOffset(1); // we have to do that because of the jumps
|
m_context.adjustStackOffset(1); // we have to do that because of the jumps
|
||||||
|
|
||||||
m_context << currentIsLong;
|
m_context << currentIsLong;
|
||||||
m_context << eth::Instruction::DUP3 << u256(31) << eth::Instruction::LT;
|
m_context << Instruction::DUP3 << u256(31) << Instruction::LT;
|
||||||
m_context.appendConditionalJumpTo(regularPath);
|
m_context.appendConditionalJumpTo(regularPath);
|
||||||
|
|
||||||
// Here: long -> short
|
// Here: long -> short
|
||||||
@ -675,51 +675,51 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _type) const
|
|||||||
|
|
||||||
// stack: ref new_length current_length ref_value
|
// stack: ref new_length current_length ref_value
|
||||||
solAssert(m_context.stackHeight() - stackHeightStart == 4 - 2, "3");
|
solAssert(m_context.stackHeight() - stackHeightStart == 4 - 2, "3");
|
||||||
m_context << eth::Instruction::POP << eth::Instruction::DUP3;
|
m_context << Instruction::POP << Instruction::DUP3;
|
||||||
CompilerUtils(m_context).computeHashStatic();
|
CompilerUtils(m_context).computeHashStatic();
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1;
|
m_context << Instruction::DUP1 << Instruction::SLOAD << Instruction::SWAP1;
|
||||||
// stack: ref new_length current_length first_word data_location
|
// stack: ref new_length current_length first_word data_location
|
||||||
m_context << eth::Instruction::DUP3;
|
m_context << Instruction::DUP3;
|
||||||
convertLengthToSize(_type);
|
convertLengthToSize(_type);
|
||||||
m_context << eth::Instruction::DUP2 << eth::Instruction::ADD << eth::Instruction::SWAP1;
|
m_context << Instruction::DUP2 << Instruction::ADD << Instruction::SWAP1;
|
||||||
// stack: ref new_length current_length first_word data_location_end data_location
|
// stack: ref new_length current_length first_word data_location_end data_location
|
||||||
clearStorageLoop(IntegerType(256));
|
clearStorageLoop(IntegerType(256));
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
// stack: ref new_length current_length first_word
|
// stack: ref new_length current_length first_word
|
||||||
solAssert(m_context.stackHeight() - stackHeightStart == 4 - 2, "3");
|
solAssert(m_context.stackHeight() - stackHeightStart == 4 - 2, "3");
|
||||||
m_context.appendJumpTo(shortToShort);
|
m_context.appendJumpTo(shortToShort);
|
||||||
|
|
||||||
m_context << regularPath;
|
m_context << regularPath;
|
||||||
// stack: ref new_length current_length ref_value
|
// stack: ref new_length current_length ref_value
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change of length for a regular array (i.e. length at location, data at sha3(location)).
|
// Change of length for a regular array (i.e. length at location, data at sha3(location)).
|
||||||
// stack: ref new_length old_length
|
// stack: ref new_length old_length
|
||||||
// store new length
|
// store new length
|
||||||
m_context << eth::Instruction::DUP2;
|
m_context << Instruction::DUP2;
|
||||||
if (_type.isByteArray())
|
if (_type.isByteArray())
|
||||||
// For a "long" byte array, store length as 2*length+1
|
// For a "long" byte array, store length as 2*length+1
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::ADD << u256(1) << eth::Instruction::ADD;
|
m_context << Instruction::DUP1 << Instruction::ADD << u256(1) << Instruction::ADD;
|
||||||
m_context<< eth::Instruction::DUP4 << eth::Instruction::SSTORE;
|
m_context<< Instruction::DUP4 << Instruction::SSTORE;
|
||||||
// skip if size is not reduced
|
// skip if size is not reduced
|
||||||
m_context << eth::Instruction::DUP2 << eth::Instruction::DUP2
|
m_context << Instruction::DUP2 << Instruction::DUP2
|
||||||
<< eth::Instruction::ISZERO << eth::Instruction::GT;
|
<< Instruction::ISZERO << Instruction::GT;
|
||||||
m_context.appendConditionalJumpTo(resizeEnd);
|
m_context.appendConditionalJumpTo(resizeEnd);
|
||||||
|
|
||||||
// size reduced, clear the end of the array
|
// size reduced, clear the end of the array
|
||||||
// stack: ref new_length old_length
|
// stack: ref new_length old_length
|
||||||
convertLengthToSize(_type);
|
convertLengthToSize(_type);
|
||||||
m_context << eth::Instruction::DUP2;
|
m_context << Instruction::DUP2;
|
||||||
convertLengthToSize(_type);
|
convertLengthToSize(_type);
|
||||||
// stack: ref new_length old_size new_size
|
// stack: ref new_length old_size new_size
|
||||||
// compute data positions
|
// compute data positions
|
||||||
m_context << eth::Instruction::DUP4;
|
m_context << Instruction::DUP4;
|
||||||
CompilerUtils(m_context).computeHashStatic();
|
CompilerUtils(m_context).computeHashStatic();
|
||||||
// stack: ref new_length old_size new_size data_pos
|
// stack: ref new_length old_size new_size data_pos
|
||||||
m_context << eth::Instruction::SWAP2 << eth::Instruction::DUP3 << eth::Instruction::ADD;
|
m_context << Instruction::SWAP2 << Instruction::DUP3 << Instruction::ADD;
|
||||||
// stack: ref new_length data_pos new_size delete_end
|
// stack: ref new_length data_pos new_size delete_end
|
||||||
m_context << eth::Instruction::SWAP2 << eth::Instruction::ADD;
|
m_context << Instruction::SWAP2 << Instruction::ADD;
|
||||||
// stack: ref new_length delete_end delete_start
|
// stack: ref new_length delete_end delete_start
|
||||||
if (_type.isByteArray() || _type.baseType()->storageBytes() < 32)
|
if (_type.isByteArray() || _type.baseType()->storageBytes() < 32)
|
||||||
clearStorageLoop(IntegerType(256));
|
clearStorageLoop(IntegerType(256));
|
||||||
@ -728,7 +728,7 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _type) const
|
|||||||
|
|
||||||
m_context << resizeEnd;
|
m_context << resizeEnd;
|
||||||
// cleanup
|
// cleanup
|
||||||
m_context << eth::Instruction::POP << eth::Instruction::POP << eth::Instruction::POP;
|
m_context << Instruction::POP << Instruction::POP << Instruction::POP;
|
||||||
solAssert(m_context.stackHeight() == stackHeightStart - 2, "");
|
solAssert(m_context.stackHeight() == stackHeightStart - 2, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -737,35 +737,35 @@ void ArrayUtils::clearStorageLoop(Type const& _type) const
|
|||||||
unsigned stackHeightStart = m_context.stackHeight();
|
unsigned stackHeightStart = m_context.stackHeight();
|
||||||
if (_type.category() == Type::Category::Mapping)
|
if (_type.category() == Type::Category::Mapping)
|
||||||
{
|
{
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// stack: end_pos pos
|
// stack: end_pos pos
|
||||||
|
|
||||||
// jump to and return from the loop to allow for duplicate code removal
|
// jump to and return from the loop to allow for duplicate code removal
|
||||||
eth::AssemblyItem returnTag = m_context.pushNewTag();
|
eth::AssemblyItem returnTag = m_context.pushNewTag();
|
||||||
m_context << eth::Instruction::SWAP2 << eth::Instruction::SWAP1;
|
m_context << Instruction::SWAP2 << Instruction::SWAP1;
|
||||||
|
|
||||||
// stack: <return tag> end_pos pos
|
// stack: <return tag> end_pos pos
|
||||||
eth::AssemblyItem loopStart = m_context.appendJumpToNew();
|
eth::AssemblyItem loopStart = m_context.appendJumpToNew();
|
||||||
m_context << loopStart;
|
m_context << loopStart;
|
||||||
// check for loop condition
|
// check for loop condition
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3
|
m_context << Instruction::DUP1 << Instruction::DUP3
|
||||||
<< eth::Instruction::GT << eth::Instruction::ISZERO;
|
<< Instruction::GT << Instruction::ISZERO;
|
||||||
eth::AssemblyItem zeroLoopEnd = m_context.newTag();
|
eth::AssemblyItem zeroLoopEnd = m_context.newTag();
|
||||||
m_context.appendConditionalJumpTo(zeroLoopEnd);
|
m_context.appendConditionalJumpTo(zeroLoopEnd);
|
||||||
// delete
|
// delete
|
||||||
m_context << u256(0);
|
m_context << u256(0);
|
||||||
StorageItem(m_context, _type).setToZero(SourceLocation(), false);
|
StorageItem(m_context, _type).setToZero(SourceLocation(), false);
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
// increment
|
// increment
|
||||||
m_context << u256(1) << eth::Instruction::ADD;
|
m_context << u256(1) << Instruction::ADD;
|
||||||
m_context.appendJumpTo(loopStart);
|
m_context.appendJumpTo(loopStart);
|
||||||
// cleanup
|
// cleanup
|
||||||
m_context << zeroLoopEnd;
|
m_context << zeroLoopEnd;
|
||||||
m_context << eth::Instruction::POP << eth::Instruction::SWAP1;
|
m_context << Instruction::POP << Instruction::SWAP1;
|
||||||
// "return"
|
// "return"
|
||||||
m_context << eth::Instruction::JUMP;
|
m_context << Instruction::JUMP;
|
||||||
|
|
||||||
m_context << returnTag;
|
m_context << returnTag;
|
||||||
solAssert(m_context.stackHeight() == stackHeightStart - 1, "");
|
solAssert(m_context.stackHeight() == stackHeightStart - 1, "");
|
||||||
@ -779,17 +779,17 @@ void ArrayUtils::convertLengthToSize(ArrayType const& _arrayType, bool _pad) con
|
|||||||
{
|
{
|
||||||
unsigned baseBytes = _arrayType.baseType()->storageBytes();
|
unsigned baseBytes = _arrayType.baseType()->storageBytes();
|
||||||
if (baseBytes == 0)
|
if (baseBytes == 0)
|
||||||
m_context << eth::Instruction::POP << u256(1);
|
m_context << Instruction::POP << u256(1);
|
||||||
else if (baseBytes <= 16)
|
else if (baseBytes <= 16)
|
||||||
{
|
{
|
||||||
unsigned itemsPerSlot = 32 / baseBytes;
|
unsigned itemsPerSlot = 32 / baseBytes;
|
||||||
m_context
|
m_context
|
||||||
<< u256(itemsPerSlot - 1) << eth::Instruction::ADD
|
<< u256(itemsPerSlot - 1) << Instruction::ADD
|
||||||
<< u256(itemsPerSlot) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
|
<< u256(itemsPerSlot) << Instruction::SWAP1 << Instruction::DIV;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_context << _arrayType.baseType()->storageSize() << eth::Instruction::MUL;
|
m_context << _arrayType.baseType()->storageSize() << Instruction::MUL;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -799,12 +799,12 @@ void ArrayUtils::convertLengthToSize(ArrayType const& _arrayType, bool _pad) con
|
|||||||
m_context << _arrayType.baseType()->memoryHeadSize();
|
m_context << _arrayType.baseType()->memoryHeadSize();
|
||||||
else
|
else
|
||||||
m_context << _arrayType.baseType()->calldataEncodedSize();
|
m_context << _arrayType.baseType()->calldataEncodedSize();
|
||||||
m_context << eth::Instruction::MUL;
|
m_context << Instruction::MUL;
|
||||||
}
|
}
|
||||||
else if (_pad)
|
else if (_pad)
|
||||||
m_context << u256(31) << eth::Instruction::ADD
|
m_context << u256(31) << Instruction::ADD
|
||||||
<< u256(32) << eth::Instruction::DUP1
|
<< u256(32) << Instruction::DUP1
|
||||||
<< eth::Instruction::SWAP2 << eth::Instruction::DIV << eth::Instruction::MUL;
|
<< Instruction::SWAP2 << Instruction::DIV << Instruction::MUL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -814,27 +814,27 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType, unsigned _stackDept
|
|||||||
m_context << _arrayType.length();
|
m_context << _arrayType.length();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_context << eth::dupInstruction(1 + _stackDepth);
|
m_context << dupInstruction(1 + _stackDepth);
|
||||||
switch (_arrayType.location())
|
switch (_arrayType.location())
|
||||||
{
|
{
|
||||||
case DataLocation::CallData:
|
case DataLocation::CallData:
|
||||||
// length is stored on the stack
|
// length is stored on the stack
|
||||||
break;
|
break;
|
||||||
case DataLocation::Memory:
|
case DataLocation::Memory:
|
||||||
m_context << eth::Instruction::MLOAD;
|
m_context << Instruction::MLOAD;
|
||||||
break;
|
break;
|
||||||
case DataLocation::Storage:
|
case DataLocation::Storage:
|
||||||
m_context << eth::Instruction::SLOAD;
|
m_context << Instruction::SLOAD;
|
||||||
if (_arrayType.isByteArray())
|
if (_arrayType.isByteArray())
|
||||||
{
|
{
|
||||||
// Retrieve length both for in-place strings and off-place strings:
|
// Retrieve length both for in-place strings and off-place strings:
|
||||||
// Computes (x & (0x100 * (ISZERO (x & 1)) - 1)) / 2
|
// Computes (x & (0x100 * (ISZERO (x & 1)) - 1)) / 2
|
||||||
// i.e. for short strings (x & 1 == 0) it does (x & 0xff) / 2 and for long strings it
|
// i.e. for short strings (x & 1 == 0) it does (x & 0xff) / 2 and for long strings it
|
||||||
// computes (x & (-1)) / 2, which is equivalent to just x / 2.
|
// computes (x & (-1)) / 2, which is equivalent to just x / 2.
|
||||||
m_context << u256(1) << eth::Instruction::DUP2 << u256(1) << eth::Instruction::AND;
|
m_context << u256(1) << Instruction::DUP2 << u256(1) << Instruction::AND;
|
||||||
m_context << eth::Instruction::ISZERO << u256(0x100) << eth::Instruction::MUL;
|
m_context << Instruction::ISZERO << u256(0x100) << Instruction::MUL;
|
||||||
m_context << eth::Instruction::SUB << eth::Instruction::AND;
|
m_context << Instruction::SUB << Instruction::AND;
|
||||||
m_context << u256(2) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
|
m_context << u256(2) << Instruction::SWAP1 << Instruction::DIV;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -852,34 +852,34 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType, bool _doBoundsCheck) c
|
|||||||
ArrayUtils::retrieveLength(_arrayType, 1);
|
ArrayUtils::retrieveLength(_arrayType, 1);
|
||||||
// Stack: ref [length] index length
|
// Stack: ref [length] index length
|
||||||
// check out-of-bounds access
|
// check out-of-bounds access
|
||||||
m_context << eth::Instruction::DUP2 << eth::Instruction::LT << eth::Instruction::ISZERO;
|
m_context << Instruction::DUP2 << Instruction::LT << Instruction::ISZERO;
|
||||||
// out-of-bounds access throws exception
|
// out-of-bounds access throws exception
|
||||||
m_context.appendConditionalJumpTo(m_context.errorTag());
|
m_context.appendConditionalJumpTo(m_context.errorTag());
|
||||||
}
|
}
|
||||||
if (location == DataLocation::CallData && _arrayType.isDynamicallySized())
|
if (location == DataLocation::CallData && _arrayType.isDynamicallySized())
|
||||||
// remove length if present
|
// remove length if present
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
|
m_context << Instruction::SWAP1 << Instruction::POP;
|
||||||
|
|
||||||
// stack: <base_ref> <index>
|
// stack: <base_ref> <index>
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
// stack: <index> <base_ref>
|
// stack: <index> <base_ref>
|
||||||
switch (location)
|
switch (location)
|
||||||
{
|
{
|
||||||
case DataLocation::Memory:
|
case DataLocation::Memory:
|
||||||
if (_arrayType.isDynamicallySized())
|
if (_arrayType.isDynamicallySized())
|
||||||
m_context << u256(32) << eth::Instruction::ADD;
|
m_context << u256(32) << Instruction::ADD;
|
||||||
// fall-through
|
// fall-through
|
||||||
case DataLocation::CallData:
|
case DataLocation::CallData:
|
||||||
if (!_arrayType.isByteArray())
|
if (!_arrayType.isByteArray())
|
||||||
{
|
{
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
if (location == DataLocation::CallData)
|
if (location == DataLocation::CallData)
|
||||||
m_context << _arrayType.baseType()->calldataEncodedSize();
|
m_context << _arrayType.baseType()->calldataEncodedSize();
|
||||||
else
|
else
|
||||||
m_context << u256(_arrayType.memoryHeadSize());
|
m_context << u256(_arrayType.memoryHeadSize());
|
||||||
m_context << eth::Instruction::MUL;
|
m_context << Instruction::MUL;
|
||||||
}
|
}
|
||||||
m_context << eth::Instruction::ADD;
|
m_context << Instruction::ADD;
|
||||||
break;
|
break;
|
||||||
case DataLocation::Storage:
|
case DataLocation::Storage:
|
||||||
{
|
{
|
||||||
@ -887,16 +887,16 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType, bool _doBoundsCheck) c
|
|||||||
if (_arrayType.isByteArray())
|
if (_arrayType.isByteArray())
|
||||||
{
|
{
|
||||||
// Special case of short byte arrays.
|
// Special case of short byte arrays.
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD;
|
m_context << Instruction::DUP2 << Instruction::SLOAD;
|
||||||
m_context << u256(1) << eth::Instruction::AND << eth::Instruction::ISZERO;
|
m_context << u256(1) << Instruction::AND << Instruction::ISZERO;
|
||||||
// No action needed for short byte arrays.
|
// No action needed for short byte arrays.
|
||||||
m_context.appendConditionalJumpTo(endTag);
|
m_context.appendConditionalJumpTo(endTag);
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
}
|
}
|
||||||
if (_arrayType.isDynamicallySized())
|
if (_arrayType.isDynamicallySized())
|
||||||
CompilerUtils(m_context).computeHashStatic();
|
CompilerUtils(m_context).computeHashStatic();
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
if (_arrayType.baseType()->storageBytes() <= 16)
|
if (_arrayType.baseType()->storageBytes() <= 16)
|
||||||
{
|
{
|
||||||
// stack: <data_ref> <index>
|
// stack: <data_ref> <index>
|
||||||
@ -905,22 +905,22 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType, bool _doBoundsCheck) c
|
|||||||
unsigned byteSize = _arrayType.baseType()->storageBytes();
|
unsigned byteSize = _arrayType.baseType()->storageBytes();
|
||||||
solAssert(byteSize != 0, "");
|
solAssert(byteSize != 0, "");
|
||||||
unsigned itemsPerSlot = 32 / byteSize;
|
unsigned itemsPerSlot = 32 / byteSize;
|
||||||
m_context << u256(itemsPerSlot) << eth::Instruction::SWAP2;
|
m_context << u256(itemsPerSlot) << Instruction::SWAP2;
|
||||||
// stack: itemsPerSlot index data_ref
|
// stack: itemsPerSlot index data_ref
|
||||||
m_context
|
m_context
|
||||||
<< eth::Instruction::DUP3 << eth::Instruction::DUP3
|
<< Instruction::DUP3 << Instruction::DUP3
|
||||||
<< eth::Instruction::DIV << eth::Instruction::ADD
|
<< Instruction::DIV << Instruction::ADD
|
||||||
// stack: itemsPerSlot index (data_ref + index / itemsPerSlot)
|
// stack: itemsPerSlot index (data_ref + index / itemsPerSlot)
|
||||||
<< eth::Instruction::SWAP2 << eth::Instruction::SWAP1
|
<< Instruction::SWAP2 << Instruction::SWAP1
|
||||||
<< eth::Instruction::MOD;
|
<< Instruction::MOD;
|
||||||
if (byteSize != 1)
|
if (byteSize != 1)
|
||||||
m_context << u256(byteSize) << eth::Instruction::MUL;
|
m_context << u256(byteSize) << Instruction::MUL;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (_arrayType.baseType()->storageSize() != 1)
|
if (_arrayType.baseType()->storageSize() != 1)
|
||||||
m_context << _arrayType.baseType()->storageSize() << eth::Instruction::MUL;
|
m_context << _arrayType.baseType()->storageSize() << Instruction::MUL;
|
||||||
m_context << eth::Instruction::ADD << u256(0);
|
m_context << Instruction::ADD << u256(0);
|
||||||
}
|
}
|
||||||
m_context << endTag;
|
m_context << endTag;
|
||||||
break;
|
break;
|
||||||
@ -942,27 +942,27 @@ void ArrayUtils::incrementByteOffset(unsigned _byteSize, unsigned _byteOffsetPos
|
|||||||
// byteOffset = 0;
|
// byteOffset = 0;
|
||||||
// }
|
// }
|
||||||
if (_byteOffsetPosition > 1)
|
if (_byteOffsetPosition > 1)
|
||||||
m_context << eth::swapInstruction(_byteOffsetPosition - 1);
|
m_context << swapInstruction(_byteOffsetPosition - 1);
|
||||||
m_context << u256(_byteSize) << eth::Instruction::ADD;
|
m_context << u256(_byteSize) << Instruction::ADD;
|
||||||
if (_byteOffsetPosition > 1)
|
if (_byteOffsetPosition > 1)
|
||||||
m_context << eth::swapInstruction(_byteOffsetPosition - 1);
|
m_context << swapInstruction(_byteOffsetPosition - 1);
|
||||||
// compute, X := (byteOffset + byteSize - 1) / 32, should be 1 iff byteOffset + bytesize > 32
|
// compute, X := (byteOffset + byteSize - 1) / 32, should be 1 iff byteOffset + bytesize > 32
|
||||||
m_context
|
m_context
|
||||||
<< u256(32) << eth::dupInstruction(1 + _byteOffsetPosition) << u256(_byteSize - 1)
|
<< u256(32) << dupInstruction(1 + _byteOffsetPosition) << u256(_byteSize - 1)
|
||||||
<< eth::Instruction::ADD << eth::Instruction::DIV;
|
<< Instruction::ADD << Instruction::DIV;
|
||||||
// increment storage offset if X == 1 (just add X to it)
|
// increment storage offset if X == 1 (just add X to it)
|
||||||
// stack: X
|
// stack: X
|
||||||
m_context
|
m_context
|
||||||
<< eth::swapInstruction(_storageOffsetPosition) << eth::dupInstruction(_storageOffsetPosition + 1)
|
<< swapInstruction(_storageOffsetPosition) << dupInstruction(_storageOffsetPosition + 1)
|
||||||
<< eth::Instruction::ADD << eth::swapInstruction(_storageOffsetPosition);
|
<< Instruction::ADD << swapInstruction(_storageOffsetPosition);
|
||||||
// stack: X
|
// stack: X
|
||||||
// set source_byte_offset to zero if X == 1 (using source_byte_offset *= 1 - X)
|
// set source_byte_offset to zero if X == 1 (using source_byte_offset *= 1 - X)
|
||||||
m_context << u256(1) << eth::Instruction::SUB;
|
m_context << u256(1) << Instruction::SUB;
|
||||||
// stack: 1 - X
|
// stack: 1 - X
|
||||||
if (_byteOffsetPosition == 1)
|
if (_byteOffsetPosition == 1)
|
||||||
m_context << eth::Instruction::MUL;
|
m_context << Instruction::MUL;
|
||||||
else
|
else
|
||||||
m_context
|
m_context
|
||||||
<< eth::dupInstruction(_byteOffsetPosition + 1) << eth::Instruction::MUL
|
<< dupInstruction(_byteOffsetPosition + 1) << Instruction::MUL
|
||||||
<< eth::swapInstruction(_byteOffsetPosition) << eth::Instruction::POP;
|
<< swapInstruction(_byteOffsetPosition) << Instruction::POP;
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
#include <libsolidity/codegen/Compiler.h>
|
#include <libsolidity/codegen/Compiler.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
#include <libevmcore/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
#include <libethcore/ChainOperationParams.h>
|
#include <libethcore/ChainOperationParams.h>
|
||||||
#include <libevmasm/Assembly.h>
|
#include <libevmasm/Assembly.h>
|
||||||
#include <libsolidity/inlineasm/AsmCodeGen.h>
|
#include <libsolidity/inlineasm/AsmCodeGen.h>
|
||||||
@ -92,8 +92,8 @@ void Compiler::compileClone(
|
|||||||
m_runtimeSub = size_t(runtimeSub.data());
|
m_runtimeSub = size_t(runtimeSub.data());
|
||||||
|
|
||||||
// stack contains sub size
|
// stack contains sub size
|
||||||
m_context << eth::Instruction::DUP1 << runtimeSub << u256(0) << eth::Instruction::CODECOPY;
|
m_context << Instruction::DUP1 << runtimeSub << u256(0) << Instruction::CODECOPY;
|
||||||
m_context << u256(0) << eth::Instruction::RETURN;
|
m_context << u256(0) << Instruction::RETURN;
|
||||||
|
|
||||||
appendFunctionsWithoutCode();
|
appendFunctionsWithoutCode();
|
||||||
|
|
||||||
@ -164,8 +164,8 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract, Comp
|
|||||||
m_runtimeSub = size_t(runtimeSub.data());
|
m_runtimeSub = size_t(runtimeSub.data());
|
||||||
|
|
||||||
// stack contains sub size
|
// stack contains sub size
|
||||||
m_context << eth::Instruction::DUP1 << runtimeSub << u256(0) << eth::Instruction::CODECOPY;
|
m_context << Instruction::DUP1 << runtimeSub << u256(0) << Instruction::CODECOPY;
|
||||||
m_context << u256(0) << eth::Instruction::RETURN;
|
m_context << u256(0) << Instruction::RETURN;
|
||||||
|
|
||||||
// note that we have to include the functions again because of absolute jump labels
|
// note that we have to include the functions again because of absolute jump labels
|
||||||
appendFunctionsWithoutCode();
|
appendFunctionsWithoutCode();
|
||||||
@ -208,15 +208,15 @@ void Compiler::appendConstructor(FunctionDefinition const& _constructor)
|
|||||||
// argument size is dynamic, use CODESIZE to determine it
|
// argument size is dynamic, use CODESIZE to determine it
|
||||||
m_context.appendProgramSize(); // program itself
|
m_context.appendProgramSize(); // program itself
|
||||||
// CODESIZE is program plus manually added arguments
|
// CODESIZE is program plus manually added arguments
|
||||||
m_context << eth::Instruction::CODESIZE << eth::Instruction::SUB;
|
m_context << Instruction::CODESIZE << Instruction::SUB;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_context << u256(argumentSize);
|
m_context << u256(argumentSize);
|
||||||
// stack: <memptr> <argument size>
|
// stack: <memptr> <argument size>
|
||||||
m_context << eth::Instruction::DUP1;
|
m_context << Instruction::DUP1;
|
||||||
m_context.appendProgramSize();
|
m_context.appendProgramSize();
|
||||||
m_context << eth::Instruction::DUP4 << eth::Instruction::CODECOPY;
|
m_context << Instruction::DUP4 << Instruction::CODECOPY;
|
||||||
m_context << eth::Instruction::DUP2 << eth::Instruction::ADD;
|
m_context << Instruction::DUP2 << Instruction::ADD;
|
||||||
CompilerUtils(m_context).storeFreeMemoryPointer();
|
CompilerUtils(m_context).storeFreeMemoryPointer();
|
||||||
// stack: <memptr>
|
// stack: <memptr>
|
||||||
appendCalldataUnpacker(FunctionType(_constructor).parameterTypes(), true);
|
appendCalldataUnpacker(FunctionType(_constructor).parameterTypes(), true);
|
||||||
@ -235,7 +235,7 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
|
|||||||
// ether with constant gas
|
// ether with constant gas
|
||||||
if (interfaceFunctions.size() > 5 || fallback)
|
if (interfaceFunctions.size() > 5 || fallback)
|
||||||
{
|
{
|
||||||
m_context << eth::Instruction::CALLDATASIZE << eth::Instruction::ISZERO;
|
m_context << Instruction::CALLDATASIZE << Instruction::ISZERO;
|
||||||
m_context.appendConditionalJumpTo(notFound);
|
m_context.appendConditionalJumpTo(notFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,7 +247,7 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
|
|||||||
for (auto const& it: interfaceFunctions)
|
for (auto const& it: interfaceFunctions)
|
||||||
{
|
{
|
||||||
callDataUnpackerEntryPoints.insert(std::make_pair(it.first, m_context.newTag()));
|
callDataUnpackerEntryPoints.insert(std::make_pair(it.first, m_context.newTag()));
|
||||||
m_context << eth::dupInstruction(1) << u256(FixedHash<4>::Arith(it.first)) << eth::Instruction::EQ;
|
m_context << dupInstruction(1) << u256(FixedHash<4>::Arith(it.first)) << Instruction::EQ;
|
||||||
m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first));
|
m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first));
|
||||||
}
|
}
|
||||||
m_context.appendJumpTo(notFound);
|
m_context.appendJumpTo(notFound);
|
||||||
@ -264,7 +264,7 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
|
|||||||
// Reject invalid library calls and ether sent to a library.
|
// Reject invalid library calls and ether sent to a library.
|
||||||
m_context.appendJumpTo(m_context.errorTag());
|
m_context.appendJumpTo(m_context.errorTag());
|
||||||
else
|
else
|
||||||
m_context << eth::Instruction::STOP; // function not found
|
m_context << Instruction::STOP; // function not found
|
||||||
|
|
||||||
for (auto const& it: interfaceFunctions)
|
for (auto const& it: interfaceFunctions)
|
||||||
{
|
{
|
||||||
@ -288,7 +288,7 @@ void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool
|
|||||||
//@todo this does not yet support nested dynamic arrays
|
//@todo this does not yet support nested dynamic arrays
|
||||||
|
|
||||||
// Retain the offset pointer as base_offset, the point from which the data offsets are computed.
|
// Retain the offset pointer as base_offset, the point from which the data offsets are computed.
|
||||||
m_context << eth::Instruction::DUP1;
|
m_context << Instruction::DUP1;
|
||||||
for (TypePointer const& parameterType: _typeParameters)
|
for (TypePointer const& parameterType: _typeParameters)
|
||||||
{
|
{
|
||||||
// stack: v1 v2 ... v(k-1) base_offset current_offset
|
// stack: v1 v2 ... v(k-1) base_offset current_offset
|
||||||
@ -309,15 +309,15 @@ void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool
|
|||||||
if (arrayType.isDynamicallySized())
|
if (arrayType.isDynamicallySized())
|
||||||
{
|
{
|
||||||
// compute data pointer
|
// compute data pointer
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::MLOAD;
|
m_context << Instruction::DUP1 << Instruction::MLOAD;
|
||||||
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
|
m_context << Instruction::DUP3 << Instruction::ADD;
|
||||||
m_context << eth::Instruction::SWAP2 << eth::Instruction::SWAP1;
|
m_context << Instruction::SWAP2 << Instruction::SWAP1;
|
||||||
m_context << u256(0x20) << eth::Instruction::ADD;
|
m_context << u256(0x20) << Instruction::ADD;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_context << eth::Instruction::DUP1;
|
m_context << Instruction::DUP1;
|
||||||
m_context << u256(arrayType.calldataEncodedSize(true)) << eth::Instruction::ADD;
|
m_context << u256(arrayType.calldataEncodedSize(true)) << Instruction::ADD;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -329,19 +329,19 @@ void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool
|
|||||||
// put on stack: data_pointer length
|
// put on stack: data_pointer length
|
||||||
CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory);
|
CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory);
|
||||||
// stack: base_offset data_offset next_pointer
|
// stack: base_offset data_offset next_pointer
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP3 << eth::Instruction::ADD;
|
m_context << Instruction::SWAP1 << Instruction::DUP3 << Instruction::ADD;
|
||||||
// stack: base_offset next_pointer data_pointer
|
// stack: base_offset next_pointer data_pointer
|
||||||
// retrieve length
|
// retrieve length
|
||||||
CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true);
|
CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true);
|
||||||
// stack: base_offset next_pointer length data_pointer
|
// stack: base_offset next_pointer length data_pointer
|
||||||
m_context << eth::Instruction::SWAP2;
|
m_context << Instruction::SWAP2;
|
||||||
// stack: base_offset data_pointer length next_pointer
|
// stack: base_offset data_pointer length next_pointer
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// leave the pointer on the stack
|
// leave the pointer on the stack
|
||||||
m_context << eth::Instruction::DUP1;
|
m_context << Instruction::DUP1;
|
||||||
m_context << u256(calldataType->calldataEncodedSize()) << eth::Instruction::ADD;
|
m_context << u256(calldataType->calldataEncodedSize()) << Instruction::ADD;
|
||||||
}
|
}
|
||||||
if (arrayType.location() == DataLocation::Memory)
|
if (arrayType.location() == DataLocation::Memory)
|
||||||
{
|
{
|
||||||
@ -355,7 +355,7 @@ void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool
|
|||||||
}
|
}
|
||||||
// move base_offset up
|
// move base_offset up
|
||||||
CompilerUtils(m_context).moveToStackTop(1 + arrayType.sizeOnStack());
|
CompilerUtils(m_context).moveToStackTop(1 + arrayType.sizeOnStack());
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -363,18 +363,18 @@ void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool
|
|||||||
solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString());
|
solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString());
|
||||||
CompilerUtils(m_context).loadFromMemoryDynamic(*type, !_fromMemory, true);
|
CompilerUtils(m_context).loadFromMemoryDynamic(*type, !_fromMemory, true);
|
||||||
CompilerUtils(m_context).moveToStackTop(1 + type->sizeOnStack());
|
CompilerUtils(m_context).moveToStackTop(1 + type->sizeOnStack());
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
}
|
}
|
||||||
// stack: v1 v2 ... v(k-1) v(k) base_offset mem_offset
|
// stack: v1 v2 ... v(k-1) v(k) base_offset mem_offset
|
||||||
}
|
}
|
||||||
m_context << eth::Instruction::POP << eth::Instruction::POP;
|
m_context << Instruction::POP << Instruction::POP;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters, bool _isLibrary)
|
void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters, bool _isLibrary)
|
||||||
{
|
{
|
||||||
CompilerUtils utils(m_context);
|
CompilerUtils utils(m_context);
|
||||||
if (_typeParameters.empty())
|
if (_typeParameters.empty())
|
||||||
m_context << eth::Instruction::STOP;
|
m_context << Instruction::STOP;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
utils.fetchFreeMemoryPointer();
|
utils.fetchFreeMemoryPointer();
|
||||||
@ -382,7 +382,7 @@ void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters, bool
|
|||||||
// its data to add the needed parts and we avoid a memory copy.
|
// its data to add the needed parts and we avoid a memory copy.
|
||||||
utils.encodeToMemory(_typeParameters, _typeParameters, true, false, _isLibrary);
|
utils.encodeToMemory(_typeParameters, _typeParameters, true, false, _isLibrary);
|
||||||
utils.toSizeAfterFreeMemoryPointer();
|
utils.toSizeAfterFreeMemoryPointer();
|
||||||
m_context << eth::Instruction::RETURN;
|
m_context << Instruction::RETURN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -476,12 +476,12 @@ bool Compiler::visit(FunctionDefinition const& _function)
|
|||||||
while (stackLayout.back() != int(stackLayout.size() - 1))
|
while (stackLayout.back() != int(stackLayout.size() - 1))
|
||||||
if (stackLayout.back() < 0)
|
if (stackLayout.back() < 0)
|
||||||
{
|
{
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
stackLayout.pop_back();
|
stackLayout.pop_back();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_context << eth::swapInstruction(stackLayout.size() - stackLayout.back() - 1);
|
m_context << swapInstruction(stackLayout.size() - stackLayout.back() - 1);
|
||||||
swap(stackLayout[stackLayout.back()], stackLayout.back());
|
swap(stackLayout[stackLayout.back()], stackLayout.back());
|
||||||
}
|
}
|
||||||
//@todo assert that everything is in place now
|
//@todo assert that everything is in place now
|
||||||
@ -532,7 +532,7 @@ bool Compiler::visit(InlineAssembly const& _inlineAssembly)
|
|||||||
errinfo_comment("Stack too deep, try removing local variables.")
|
errinfo_comment("Stack too deep, try removing local variables.")
|
||||||
);
|
);
|
||||||
for (unsigned i = 0; i < variable->type()->sizeOnStack(); ++i)
|
for (unsigned i = 0; i < variable->type()->sizeOnStack(); ++i)
|
||||||
_assembly.append(eth::dupInstruction(stackDiff));
|
_assembly.append(dupInstruction(stackDiff));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -572,8 +572,8 @@ bool Compiler::visit(InlineAssembly const& _inlineAssembly)
|
|||||||
errinfo_comment("Stack too deep, try removing local variables.")
|
errinfo_comment("Stack too deep, try removing local variables.")
|
||||||
);
|
);
|
||||||
for (unsigned i = 0; i < size; ++i) {
|
for (unsigned i = 0; i < size; ++i) {
|
||||||
_assembly.append(eth::swapInstruction(stackDiff));
|
_assembly.append(swapInstruction(stackDiff));
|
||||||
_assembly.append(eth::Instruction::POP);
|
_assembly.append(Instruction::POP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -588,7 +588,7 @@ bool Compiler::visit(IfStatement const& _ifStatement)
|
|||||||
StackHeightChecker checker(m_context);
|
StackHeightChecker checker(m_context);
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, _ifStatement);
|
CompilerContext::LocationSetter locationSetter(m_context, _ifStatement);
|
||||||
compileExpression(_ifStatement.condition());
|
compileExpression(_ifStatement.condition());
|
||||||
m_context << eth::Instruction::ISZERO;
|
m_context << Instruction::ISZERO;
|
||||||
eth::AssemblyItem falseTag = m_context.appendConditionalJump();
|
eth::AssemblyItem falseTag = m_context.appendConditionalJump();
|
||||||
eth::AssemblyItem endTag = falseTag;
|
eth::AssemblyItem endTag = falseTag;
|
||||||
_ifStatement.trueStatement().accept(*this);
|
_ifStatement.trueStatement().accept(*this);
|
||||||
@ -615,7 +615,7 @@ bool Compiler::visit(WhileStatement const& _whileStatement)
|
|||||||
|
|
||||||
m_context << loopStart;
|
m_context << loopStart;
|
||||||
compileExpression(_whileStatement.condition());
|
compileExpression(_whileStatement.condition());
|
||||||
m_context << eth::Instruction::ISZERO;
|
m_context << Instruction::ISZERO;
|
||||||
m_context.appendConditionalJumpTo(loopEnd);
|
m_context.appendConditionalJumpTo(loopEnd);
|
||||||
|
|
||||||
_whileStatement.body().accept(*this);
|
_whileStatement.body().accept(*this);
|
||||||
@ -649,7 +649,7 @@ bool Compiler::visit(ForStatement const& _forStatement)
|
|||||||
if (_forStatement.condition())
|
if (_forStatement.condition())
|
||||||
{
|
{
|
||||||
compileExpression(*_forStatement.condition());
|
compileExpression(*_forStatement.condition());
|
||||||
m_context << eth::Instruction::ISZERO;
|
m_context << Instruction::ISZERO;
|
||||||
m_context.appendConditionalJumpTo(loopEnd);
|
m_context.appendConditionalJumpTo(loopEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -710,7 +710,7 @@ bool Compiler::visit(Return const& _return)
|
|||||||
CompilerUtils(m_context).moveToStackVariable(*retVariable);
|
CompilerUtils(m_context).moveToStackVariable(*retVariable);
|
||||||
}
|
}
|
||||||
for (unsigned i = 0; i < m_stackCleanupForReturn; ++i)
|
for (unsigned i = 0; i < m_stackCleanupForReturn; ++i)
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
m_context.appendJumpTo(m_returnTag);
|
m_context.appendJumpTo(m_returnTag);
|
||||||
m_context.adjustStackOffset(m_stackCleanupForReturn);
|
m_context.adjustStackOffset(m_stackCleanupForReturn);
|
||||||
return false;
|
return false;
|
||||||
@ -831,7 +831,7 @@ void Compiler::appendModifierOrFunctionCode()
|
|||||||
modifier.body().accept(*this);
|
modifier.body().accept(*this);
|
||||||
|
|
||||||
for (unsigned i = 0; i < c_stackSurplus; ++i)
|
for (unsigned i = 0; i < c_stackSurplus; ++i)
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
m_stackCleanupForReturn -= c_stackSurplus;
|
m_stackCleanupForReturn -= c_stackSurplus;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -855,20 +855,20 @@ eth::Assembly Compiler::cloneRuntime()
|
|||||||
{
|
{
|
||||||
eth::EVMSchedule schedule;
|
eth::EVMSchedule schedule;
|
||||||
eth::Assembly a;
|
eth::Assembly a;
|
||||||
a << eth::Instruction::CALLDATASIZE;
|
a << Instruction::CALLDATASIZE;
|
||||||
a << u256(0) << eth::Instruction::DUP1 << eth::Instruction::CALLDATACOPY;
|
a << u256(0) << Instruction::DUP1 << Instruction::CALLDATACOPY;
|
||||||
//@todo adjust for larger return values, make this dynamic.
|
//@todo adjust for larger return values, make this dynamic.
|
||||||
a << u256(0x20) << u256(0) << eth::Instruction::CALLDATASIZE;
|
a << u256(0x20) << u256(0) << Instruction::CALLDATASIZE;
|
||||||
a << u256(0);
|
a << u256(0);
|
||||||
// this is the address which has to be substituted by the linker.
|
// this is the address which has to be substituted by the linker.
|
||||||
//@todo implement as special "marker" AssemblyItem.
|
//@todo implement as special "marker" AssemblyItem.
|
||||||
a << u256("0xcafecafecafecafecafecafecafecafecafecafe");
|
a << u256("0xcafecafecafecafecafecafecafecafecafecafe");
|
||||||
a << u256(schedule.callGas + 10) << eth::Instruction::GAS << eth::Instruction::SUB;
|
a << u256(schedule.callGas + 10) << Instruction::GAS << Instruction::SUB;
|
||||||
a << eth::Instruction::DELEGATECALL;
|
a << Instruction::DELEGATECALL;
|
||||||
//Propagate error condition (if DELEGATECALL pushes 0 on stack).
|
//Propagate error condition (if DELEGATECALL pushes 0 on stack).
|
||||||
a << eth::Instruction::ISZERO;
|
a << Instruction::ISZERO;
|
||||||
a.appendJumpI(a.errorTag());
|
a.appendJumpI(a.errorTag());
|
||||||
//@todo adjust for larger return values, make this dynamic.
|
//@todo adjust for larger return values, make this dynamic.
|
||||||
a << u256(0x20) << u256(0) << eth::Instruction::RETURN;
|
a << u256(0x20) << u256(0) << Instruction::RETURN;
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
@ -166,7 +166,7 @@ pair<u256, unsigned> CompilerContext::storageLocationOfVariable(const Declaratio
|
|||||||
|
|
||||||
CompilerContext& CompilerContext::appendJump(eth::AssemblyItem::JumpType _jumpType)
|
CompilerContext& CompilerContext::appendJump(eth::AssemblyItem::JumpType _jumpType)
|
||||||
{
|
{
|
||||||
eth::AssemblyItem item(eth::Instruction::JUMP);
|
eth::AssemblyItem item(Instruction::JUMP);
|
||||||
item.setJumpType(_jumpType);
|
item.setJumpType(_jumpType);
|
||||||
return *this << item;
|
return *this << item;
|
||||||
}
|
}
|
||||||
@ -182,7 +182,7 @@ void CompilerContext::resetVisitedNodes(ASTNode const* _node)
|
|||||||
void CompilerContext::injectVersionStampIntoSub(size_t _subIndex)
|
void CompilerContext::injectVersionStampIntoSub(size_t _subIndex)
|
||||||
{
|
{
|
||||||
eth::Assembly& sub = m_asm.sub(_subIndex);
|
eth::Assembly& sub = m_asm.sub(_subIndex);
|
||||||
sub.injectStart(eth::Instruction::POP);
|
sub.injectStart(Instruction::POP);
|
||||||
sub.injectStart(fromBigEndian<u256>(binaryVersion()));
|
sub.injectStart(fromBigEndian<u256>(binaryVersion()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <libevmcore/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
#include <libevmasm/Assembly.h>
|
#include <libevmasm/Assembly.h>
|
||||||
#include <libsolidity/ast/ASTForward.h>
|
#include <libsolidity/ast/ASTForward.h>
|
||||||
#include <libsolidity/ast/Types.h>
|
#include <libsolidity/ast/Types.h>
|
||||||
@ -126,7 +126,7 @@ public:
|
|||||||
|
|
||||||
/// Append elements to the current instruction list and adjust @a m_stackOffset.
|
/// Append elements to the current instruction list and adjust @a m_stackOffset.
|
||||||
CompilerContext& operator<<(eth::AssemblyItem const& _item) { m_asm.append(_item); return *this; }
|
CompilerContext& operator<<(eth::AssemblyItem const& _item) { m_asm.append(_item); return *this; }
|
||||||
CompilerContext& operator<<(eth::Instruction _instruction) { m_asm.append(_instruction); return *this; }
|
CompilerContext& operator<<(Instruction _instruction) { m_asm.append(_instruction); return *this; }
|
||||||
CompilerContext& operator<<(u256 const& _value) { m_asm.append(_value); return *this; }
|
CompilerContext& operator<<(u256 const& _value) { m_asm.append(_value); return *this; }
|
||||||
CompilerContext& operator<<(bytes const& _data) { m_asm.append(_data); return *this; }
|
CompilerContext& operator<<(bytes const& _data) { m_asm.append(_data); return *this; }
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
#include <libsolidity/codegen/CompilerUtils.h>
|
#include <libsolidity/codegen/CompilerUtils.h>
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
#include <libevmcore/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
#include <libsolidity/codegen/ArrayUtils.h>
|
#include <libsolidity/codegen/ArrayUtils.h>
|
||||||
#include <libsolidity/codegen/LValue.h>
|
#include <libsolidity/codegen/LValue.h>
|
||||||
|
|
||||||
@ -45,26 +45,26 @@ void CompilerUtils::initialiseFreeMemoryPointer()
|
|||||||
|
|
||||||
void CompilerUtils::fetchFreeMemoryPointer()
|
void CompilerUtils::fetchFreeMemoryPointer()
|
||||||
{
|
{
|
||||||
m_context << u256(freeMemoryPointer) << eth::Instruction::MLOAD;
|
m_context << u256(freeMemoryPointer) << Instruction::MLOAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerUtils::storeFreeMemoryPointer()
|
void CompilerUtils::storeFreeMemoryPointer()
|
||||||
{
|
{
|
||||||
m_context << u256(freeMemoryPointer) << eth::Instruction::MSTORE;
|
m_context << u256(freeMemoryPointer) << Instruction::MSTORE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerUtils::allocateMemory()
|
void CompilerUtils::allocateMemory()
|
||||||
{
|
{
|
||||||
fetchFreeMemoryPointer();
|
fetchFreeMemoryPointer();
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2 << eth::Instruction::ADD;
|
m_context << Instruction::SWAP1 << Instruction::DUP2 << Instruction::ADD;
|
||||||
storeFreeMemoryPointer();
|
storeFreeMemoryPointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerUtils::toSizeAfterFreeMemoryPointer()
|
void CompilerUtils::toSizeAfterFreeMemoryPointer()
|
||||||
{
|
{
|
||||||
fetchFreeMemoryPointer();
|
fetchFreeMemoryPointer();
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::SWAP2 << eth::Instruction::SUB;
|
m_context << Instruction::DUP1 << Instruction::SWAP2 << Instruction::SUB;
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned CompilerUtils::loadFromMemory(
|
unsigned CompilerUtils::loadFromMemory(
|
||||||
@ -87,7 +87,7 @@ void CompilerUtils::loadFromMemoryDynamic(
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (_keepUpdatedMemoryOffset)
|
if (_keepUpdatedMemoryOffset)
|
||||||
m_context << eth::Instruction::DUP1;
|
m_context << Instruction::DUP1;
|
||||||
|
|
||||||
if (auto arrayType = dynamic_cast<ArrayType const*>(&_type))
|
if (auto arrayType = dynamic_cast<ArrayType const*>(&_type))
|
||||||
{
|
{
|
||||||
@ -95,7 +95,7 @@ void CompilerUtils::loadFromMemoryDynamic(
|
|||||||
solAssert(!_fromCalldata, "");
|
solAssert(!_fromCalldata, "");
|
||||||
solAssert(_padToWordBoundaries, "");
|
solAssert(_padToWordBoundaries, "");
|
||||||
if (_keepUpdatedMemoryOffset)
|
if (_keepUpdatedMemoryOffset)
|
||||||
m_context << arrayType->memorySize() << eth::Instruction::ADD;
|
m_context << arrayType->memorySize() << Instruction::ADD;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -104,7 +104,7 @@ void CompilerUtils::loadFromMemoryDynamic(
|
|||||||
{
|
{
|
||||||
// update memory counter
|
// update memory counter
|
||||||
moveToStackTop(_type.sizeOnStack());
|
moveToStackTop(_type.sizeOnStack());
|
||||||
m_context << u256(numBytes) << eth::Instruction::ADD;
|
m_context << u256(numBytes) << Instruction::ADD;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -113,7 +113,7 @@ void CompilerUtils::storeInMemory(unsigned _offset)
|
|||||||
{
|
{
|
||||||
unsigned numBytes = prepareMemoryStore(IntegerType(256), true);
|
unsigned numBytes = prepareMemoryStore(IntegerType(256), true);
|
||||||
if (numBytes > 0)
|
if (numBytes > 0)
|
||||||
m_context << u256(_offset) << eth::Instruction::MSTORE;
|
m_context << u256(_offset) << Instruction::MSTORE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries)
|
void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries)
|
||||||
@ -125,13 +125,13 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
|
|||||||
}
|
}
|
||||||
else if (auto str = dynamic_cast<StringLiteralType const*>(&_type))
|
else if (auto str = dynamic_cast<StringLiteralType const*>(&_type))
|
||||||
{
|
{
|
||||||
m_context << eth::Instruction::DUP1;
|
m_context << Instruction::DUP1;
|
||||||
storeStringData(bytesConstRef(str->value()));
|
storeStringData(bytesConstRef(str->value()));
|
||||||
if (_padToWordBoundaries)
|
if (_padToWordBoundaries)
|
||||||
m_context << u256(((str->value().size() + 31) / 32) * 32);
|
m_context << u256(((str->value().size() + 31) / 32) * 32);
|
||||||
else
|
else
|
||||||
m_context << u256(str->value().size());
|
m_context << u256(str->value().size());
|
||||||
m_context << eth::Instruction::ADD;
|
m_context << Instruction::ADD;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -142,8 +142,8 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
|
|||||||
_type.sizeOnStack() == 1,
|
_type.sizeOnStack() == 1,
|
||||||
"Memory store of types with stack size != 1 not implemented."
|
"Memory store of types with stack size != 1 not implemented."
|
||||||
);
|
);
|
||||||
m_context << eth::Instruction::DUP2 << eth::Instruction::MSTORE;
|
m_context << Instruction::DUP2 << Instruction::MSTORE;
|
||||||
m_context << u256(numBytes) << eth::Instruction::ADD;
|
m_context << u256(numBytes) << Instruction::ADD;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,7 +168,7 @@ void CompilerUtils::encodeToMemory(
|
|||||||
// of the ith dynamic parameter, which is filled once the dynamic parts are processed.
|
// of the ith dynamic parameter, which is filled once the dynamic parts are processed.
|
||||||
|
|
||||||
// store memory start pointer
|
// store memory start pointer
|
||||||
m_context << eth::Instruction::DUP1;
|
m_context << Instruction::DUP1;
|
||||||
|
|
||||||
unsigned argSize = CompilerUtils::sizeOnStack(_givenTypes);
|
unsigned argSize = CompilerUtils::sizeOnStack(_givenTypes);
|
||||||
unsigned stackPos = 0; // advances through the argument values
|
unsigned stackPos = 0; // advances through the argument values
|
||||||
@ -180,7 +180,7 @@ void CompilerUtils::encodeToMemory(
|
|||||||
if (targetType->isDynamicallySized() && !_copyDynamicDataInPlace)
|
if (targetType->isDynamicallySized() && !_copyDynamicDataInPlace)
|
||||||
{
|
{
|
||||||
// leave end_of_mem as dyn head pointer
|
// leave end_of_mem as dyn head pointer
|
||||||
m_context << eth::Instruction::DUP1 << u256(32) << eth::Instruction::ADD;
|
m_context << Instruction::DUP1 << u256(32) << Instruction::ADD;
|
||||||
dynPointers++;
|
dynPointers++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -222,10 +222,10 @@ void CompilerUtils::encodeToMemory(
|
|||||||
if (targetType->isDynamicallySized() && !_copyDynamicDataInPlace)
|
if (targetType->isDynamicallySized() && !_copyDynamicDataInPlace)
|
||||||
{
|
{
|
||||||
// copy tail pointer (=mem_end - mem_start) to memory
|
// copy tail pointer (=mem_end - mem_start) to memory
|
||||||
m_context << eth::dupInstruction(2 + dynPointers) << eth::Instruction::DUP2;
|
m_context << dupInstruction(2 + dynPointers) << Instruction::DUP2;
|
||||||
m_context << eth::Instruction::SUB;
|
m_context << Instruction::SUB;
|
||||||
m_context << eth::dupInstruction(2 + dynPointers - thisDynPointer);
|
m_context << dupInstruction(2 + dynPointers - thisDynPointer);
|
||||||
m_context << eth::Instruction::MSTORE;
|
m_context << Instruction::MSTORE;
|
||||||
// stack: ... <end_of_mem>
|
// stack: ... <end_of_mem>
|
||||||
if (_givenTypes[i]->category() == Type::Category::StringLiteral)
|
if (_givenTypes[i]->category() == Type::Category::StringLiteral)
|
||||||
{
|
{
|
||||||
@ -243,13 +243,13 @@ void CompilerUtils::encodeToMemory(
|
|||||||
copyToStackTop(argSize - stackPos + dynPointers + 2, arrayType.sizeOnStack());
|
copyToStackTop(argSize - stackPos + dynPointers + 2, arrayType.sizeOnStack());
|
||||||
// stack: ... <end_of_mem> <value...>
|
// stack: ... <end_of_mem> <value...>
|
||||||
// copy length to memory
|
// copy length to memory
|
||||||
m_context << eth::dupInstruction(1 + arrayType.sizeOnStack());
|
m_context << dupInstruction(1 + arrayType.sizeOnStack());
|
||||||
ArrayUtils(m_context).retrieveLength(arrayType, 1);
|
ArrayUtils(m_context).retrieveLength(arrayType, 1);
|
||||||
// stack: ... <end_of_mem> <value...> <end_of_mem'> <length>
|
// stack: ... <end_of_mem> <value...> <end_of_mem'> <length>
|
||||||
storeInMemoryDynamic(IntegerType(256), true);
|
storeInMemoryDynamic(IntegerType(256), true);
|
||||||
// stack: ... <end_of_mem> <value...> <end_of_mem''>
|
// stack: ... <end_of_mem> <value...> <end_of_mem''>
|
||||||
// copy the new memory pointer
|
// copy the new memory pointer
|
||||||
m_context << eth::swapInstruction(arrayType.sizeOnStack() + 1) << eth::Instruction::POP;
|
m_context << swapInstruction(arrayType.sizeOnStack() + 1) << Instruction::POP;
|
||||||
// stack: ... <end_of_mem''> <value...>
|
// stack: ... <end_of_mem''> <value...>
|
||||||
// copy data part
|
// copy data part
|
||||||
ArrayUtils(m_context).copyArrayToMemory(arrayType, _padToWordBoundaries);
|
ArrayUtils(m_context).copyArrayToMemory(arrayType, _padToWordBoundaries);
|
||||||
@ -262,7 +262,7 @@ void CompilerUtils::encodeToMemory(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remove unneeded stack elements (and retain memory pointer)
|
// remove unneeded stack elements (and retain memory pointer)
|
||||||
m_context << eth::swapInstruction(argSize + dynPointers + 1);
|
m_context << swapInstruction(argSize + dynPointers + 1);
|
||||||
popStackSlots(argSize + dynPointers + 1);
|
popStackSlots(argSize + dynPointers + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,11 +272,11 @@ void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type)
|
|||||||
m_context << repeat;
|
m_context << repeat;
|
||||||
pushZeroValue(*_type.baseType());
|
pushZeroValue(*_type.baseType());
|
||||||
storeInMemoryDynamic(*_type.baseType());
|
storeInMemoryDynamic(*_type.baseType());
|
||||||
m_context << eth::Instruction::SWAP1 << u256(1) << eth::Instruction::SWAP1;
|
m_context << Instruction::SWAP1 << u256(1) << Instruction::SWAP1;
|
||||||
m_context << eth::Instruction::SUB << eth::Instruction::SWAP1;
|
m_context << Instruction::SUB << Instruction::SWAP1;
|
||||||
m_context << eth::Instruction::DUP2;
|
m_context << Instruction::DUP2;
|
||||||
m_context.appendConditionalJumpTo(repeat);
|
m_context.appendConditionalJumpTo(repeat);
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
|
m_context << Instruction::SWAP1 << Instruction::POP;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerUtils::memoryCopy()
|
void CompilerUtils::memoryCopy()
|
||||||
@ -284,16 +284,16 @@ void CompilerUtils::memoryCopy()
|
|||||||
// Stack here: size target source
|
// Stack here: size target source
|
||||||
// stack for call: outsize target size source value contract gas
|
// stack for call: outsize target size source value contract gas
|
||||||
//@TODO do not use ::CALL if less than 32 bytes?
|
//@TODO do not use ::CALL if less than 32 bytes?
|
||||||
m_context << eth::Instruction::DUP3 << eth::Instruction::SWAP1;
|
m_context << Instruction::DUP3 << Instruction::SWAP1;
|
||||||
m_context << u256(0) << u256(identityContractAddress);
|
m_context << u256(0) << u256(identityContractAddress);
|
||||||
// compute gas costs
|
// compute gas costs
|
||||||
m_context << u256(32) << eth::Instruction::DUP5 << u256(31) << eth::Instruction::ADD;
|
m_context << u256(32) << Instruction::DUP5 << u256(31) << Instruction::ADD;
|
||||||
static unsigned c_identityGas = 3;
|
static unsigned c_identityGas = 3;
|
||||||
static unsigned c_identityWordGas = 15;
|
static unsigned c_identityWordGas = 15;
|
||||||
m_context << eth::Instruction::DIV << u256(c_identityWordGas) << eth::Instruction::MUL;
|
m_context << Instruction::DIV << u256(c_identityWordGas) << Instruction::MUL;
|
||||||
m_context << u256(c_identityGas) << eth::Instruction::ADD;
|
m_context << u256(c_identityGas) << Instruction::ADD;
|
||||||
m_context << eth::Instruction::CALL;
|
m_context << Instruction::CALL;
|
||||||
m_context << eth::Instruction::POP; // ignore return value
|
m_context << Instruction::POP; // ignore return value
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded)
|
void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded)
|
||||||
@ -317,7 +317,7 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
|
|||||||
// conversion from bytes to integer. no need to clean the high bit
|
// conversion from bytes to integer. no need to clean the high bit
|
||||||
// only to shift right because of opposite alignment
|
// only to shift right because of opposite alignment
|
||||||
IntegerType const& targetIntegerType = dynamic_cast<IntegerType const&>(_targetType);
|
IntegerType const& targetIntegerType = dynamic_cast<IntegerType const&>(_targetType);
|
||||||
m_context << (u256(1) << (256 - typeOnStack.numBytes() * 8)) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
|
m_context << (u256(1) << (256 - typeOnStack.numBytes() * 8)) << Instruction::SWAP1 << Instruction::DIV;
|
||||||
if (targetIntegerType.numBits() < typeOnStack.numBytes() * 8)
|
if (targetIntegerType.numBits() < typeOnStack.numBytes() * 8)
|
||||||
convertType(IntegerType(typeOnStack.numBytes() * 8), _targetType, _cleanupNeeded);
|
convertType(IntegerType(typeOnStack.numBytes() * 8), _targetType, _cleanupNeeded);
|
||||||
}
|
}
|
||||||
@ -329,12 +329,12 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
|
|||||||
if (targetType.numBytes() < typeOnStack.numBytes())
|
if (targetType.numBytes() < typeOnStack.numBytes())
|
||||||
{
|
{
|
||||||
if (targetType.numBytes() == 0)
|
if (targetType.numBytes() == 0)
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::XOR;
|
m_context << Instruction::DUP1 << Instruction::XOR;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_context << (u256(1) << (256 - targetType.numBytes() * 8));
|
m_context << (u256(1) << (256 - targetType.numBytes() * 8));
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::SWAP2;
|
m_context << Instruction::DUP1 << Instruction::SWAP2;
|
||||||
m_context << eth::Instruction::DIV << eth::Instruction::MUL;
|
m_context << Instruction::DIV << Instruction::MUL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -356,7 +356,7 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
|
|||||||
if (auto typeOnStack = dynamic_cast<IntegerType const*>(&_typeOnStack))
|
if (auto typeOnStack = dynamic_cast<IntegerType const*>(&_typeOnStack))
|
||||||
if (targetBytesType.numBytes() * 8 > typeOnStack->numBits())
|
if (targetBytesType.numBytes() * 8 > typeOnStack->numBits())
|
||||||
cleanHigherOrderBits(*typeOnStack);
|
cleanHigherOrderBits(*typeOnStack);
|
||||||
m_context << (u256(1) << (256 - targetBytesType.numBytes() * 8)) << eth::Instruction::MUL;
|
m_context << (u256(1) << (256 - targetBytesType.numBytes() * 8)) << Instruction::MUL;
|
||||||
}
|
}
|
||||||
else if (targetTypeCategory == Type::Category::Enum)
|
else if (targetTypeCategory == Type::Category::Enum)
|
||||||
// just clean
|
// just clean
|
||||||
@ -406,7 +406,7 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
|
|||||||
m_context << storageSize;
|
m_context << storageSize;
|
||||||
allocateMemory();
|
allocateMemory();
|
||||||
// stack: mempos
|
// stack: mempos
|
||||||
m_context << eth::Instruction::DUP1 << u256(data.size());
|
m_context << Instruction::DUP1 << u256(data.size());
|
||||||
storeInMemoryDynamic(IntegerType(256));
|
storeInMemoryDynamic(IntegerType(256));
|
||||||
// stack: mempos datapos
|
// stack: mempos datapos
|
||||||
storeStringData(data);
|
storeStringData(data);
|
||||||
@ -445,18 +445,18 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
|
|||||||
|
|
||||||
// allocate memory
|
// allocate memory
|
||||||
// stack: <source ref> (variably sized) <length>
|
// stack: <source ref> (variably sized) <length>
|
||||||
m_context << eth::Instruction::DUP1;
|
m_context << Instruction::DUP1;
|
||||||
ArrayUtils(m_context).convertLengthToSize(targetType, true);
|
ArrayUtils(m_context).convertLengthToSize(targetType, true);
|
||||||
// stack: <source ref> (variably sized) <length> <size>
|
// stack: <source ref> (variably sized) <length> <size>
|
||||||
if (targetType.isDynamicallySized())
|
if (targetType.isDynamicallySized())
|
||||||
m_context << u256(0x20) << eth::Instruction::ADD;
|
m_context << u256(0x20) << Instruction::ADD;
|
||||||
allocateMemory();
|
allocateMemory();
|
||||||
// stack: <source ref> (variably sized) <length> <mem start>
|
// stack: <source ref> (variably sized) <length> <mem start>
|
||||||
m_context << eth::Instruction::DUP1;
|
m_context << Instruction::DUP1;
|
||||||
moveIntoStack(2 + stackSize);
|
moveIntoStack(2 + stackSize);
|
||||||
if (targetType.isDynamicallySized())
|
if (targetType.isDynamicallySized())
|
||||||
{
|
{
|
||||||
m_context << eth::Instruction::DUP2;
|
m_context << Instruction::DUP2;
|
||||||
storeInMemoryDynamic(IntegerType(256));
|
storeInMemoryDynamic(IntegerType(256));
|
||||||
}
|
}
|
||||||
// stack: <mem start> <source ref> (variably sized) <length> <mem data pos>
|
// stack: <mem start> <source ref> (variably sized) <length> <mem data pos>
|
||||||
@ -468,12 +468,12 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_context << u256(0) << eth::Instruction::SWAP1;
|
m_context << u256(0) << Instruction::SWAP1;
|
||||||
// stack: <mem start> <source ref> (variably sized) <length> <counter> <mem data pos>
|
// stack: <mem start> <source ref> (variably sized) <length> <counter> <mem data pos>
|
||||||
auto repeat = m_context.newTag();
|
auto repeat = m_context.newTag();
|
||||||
m_context << repeat;
|
m_context << repeat;
|
||||||
m_context << eth::Instruction::DUP3 << eth::Instruction::DUP3;
|
m_context << Instruction::DUP3 << Instruction::DUP3;
|
||||||
m_context << eth::Instruction::LT << eth::Instruction::ISZERO;
|
m_context << Instruction::LT << Instruction::ISZERO;
|
||||||
auto loopEnd = m_context.appendConditionalJump();
|
auto loopEnd = m_context.appendConditionalJump();
|
||||||
copyToStackTop(3 + stackSize, stackSize);
|
copyToStackTop(3 + stackSize, stackSize);
|
||||||
copyToStackTop(2 + stackSize, 1);
|
copyToStackTop(2 + stackSize, 1);
|
||||||
@ -482,11 +482,11 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
|
|||||||
StorageItem(m_context, *typeOnStack.baseType()).retrieveValue(SourceLocation(), true);
|
StorageItem(m_context, *typeOnStack.baseType()).retrieveValue(SourceLocation(), true);
|
||||||
convertType(*typeOnStack.baseType(), *targetType.baseType(), _cleanupNeeded);
|
convertType(*typeOnStack.baseType(), *targetType.baseType(), _cleanupNeeded);
|
||||||
storeInMemoryDynamic(*targetType.baseType(), true);
|
storeInMemoryDynamic(*targetType.baseType(), true);
|
||||||
m_context << eth::Instruction::SWAP1 << u256(1) << eth::Instruction::ADD;
|
m_context << Instruction::SWAP1 << u256(1) << Instruction::ADD;
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
m_context.appendJumpTo(repeat);
|
m_context.appendJumpTo(repeat);
|
||||||
m_context << loopEnd;
|
m_context << loopEnd;
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
}
|
}
|
||||||
// stack: <mem start> <source ref> (variably sized) <length> <mem data pos updated>
|
// stack: <mem start> <source ref> (variably sized) <length> <mem data pos updated>
|
||||||
popStackSlots(2 + stackSize);
|
popStackSlots(2 + stackSize);
|
||||||
@ -540,14 +540,14 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
|
|||||||
// stack: <source ref>
|
// stack: <source ref>
|
||||||
m_context << typeOnStack.memorySize();
|
m_context << typeOnStack.memorySize();
|
||||||
allocateMemory();
|
allocateMemory();
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2;
|
m_context << Instruction::SWAP1 << Instruction::DUP2;
|
||||||
// stack: <memory ptr> <source ref> <memory ptr>
|
// stack: <memory ptr> <source ref> <memory ptr>
|
||||||
for (auto const& member: typeOnStack.members(nullptr))
|
for (auto const& member: typeOnStack.members(nullptr))
|
||||||
{
|
{
|
||||||
if (!member.type->canLiveOutsideStorage())
|
if (!member.type->canLiveOutsideStorage())
|
||||||
continue;
|
continue;
|
||||||
pair<u256, unsigned> const& offsets = typeOnStack.storageOffsetsOfMember(member.name);
|
pair<u256, unsigned> const& offsets = typeOnStack.storageOffsetsOfMember(member.name);
|
||||||
m_context << offsets.first << eth::Instruction::DUP3 << eth::Instruction::ADD;
|
m_context << offsets.first << Instruction::DUP3 << Instruction::ADD;
|
||||||
m_context << u256(offsets.second);
|
m_context << u256(offsets.second);
|
||||||
StorageItem(m_context, *member.type).retrieveValue(SourceLocation(), true);
|
StorageItem(m_context, *member.type).retrieveValue(SourceLocation(), true);
|
||||||
TypePointer targetMemberType = targetType.memberType(member.name);
|
TypePointer targetMemberType = targetType.memberType(member.name);
|
||||||
@ -555,7 +555,7 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
|
|||||||
convertType(*member.type, *targetMemberType, true);
|
convertType(*member.type, *targetMemberType, true);
|
||||||
storeInMemoryDynamic(*targetMemberType, true);
|
storeInMemoryDynamic(*targetMemberType, true);
|
||||||
}
|
}
|
||||||
m_context << eth::Instruction::POP << eth::Instruction::POP;
|
m_context << Instruction::POP << Instruction::POP;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DataLocation::CallData:
|
case DataLocation::CallData:
|
||||||
@ -602,13 +602,13 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
|
|||||||
// Move it back into its place.
|
// Move it back into its place.
|
||||||
for (unsigned j = 0; j < min(sourceSize, targetSize); ++j)
|
for (unsigned j = 0; j < min(sourceSize, targetSize); ++j)
|
||||||
m_context <<
|
m_context <<
|
||||||
eth::swapInstruction(depth + targetSize - sourceSize) <<
|
swapInstruction(depth + targetSize - sourceSize) <<
|
||||||
eth::Instruction::POP;
|
Instruction::POP;
|
||||||
// Value shrank
|
// Value shrank
|
||||||
for (unsigned j = targetSize; j < sourceSize; ++j)
|
for (unsigned j = targetSize; j < sourceSize; ++j)
|
||||||
{
|
{
|
||||||
moveToStackTop(depth - 1, 1);
|
moveToStackTop(depth - 1, 1);
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
}
|
}
|
||||||
// Value grew
|
// Value grew
|
||||||
if (targetSize > sourceSize)
|
if (targetSize > sourceSize)
|
||||||
@ -639,7 +639,7 @@ void CompilerUtils::pushZeroValue(Type const& _type)
|
|||||||
|
|
||||||
m_context << u256(max(32u, _type.calldataEncodedSize()));
|
m_context << u256(max(32u, _type.calldataEncodedSize()));
|
||||||
allocateMemory();
|
allocateMemory();
|
||||||
m_context << eth::Instruction::DUP1;
|
m_context << Instruction::DUP1;
|
||||||
|
|
||||||
if (auto structType = dynamic_cast<StructType const*>(&_type))
|
if (auto structType = dynamic_cast<StructType const*>(&_type))
|
||||||
for (auto const& member: structType->members(nullptr))
|
for (auto const& member: structType->members(nullptr))
|
||||||
@ -657,7 +657,7 @@ void CompilerUtils::pushZeroValue(Type const& _type)
|
|||||||
}
|
}
|
||||||
else if (arrayType->length() > 0)
|
else if (arrayType->length() > 0)
|
||||||
{
|
{
|
||||||
m_context << arrayType->length() << eth::Instruction::SWAP1;
|
m_context << arrayType->length() << Instruction::SWAP1;
|
||||||
// stack: items_to_do memory_pos
|
// stack: items_to_do memory_pos
|
||||||
zeroInitialiseMemoryArray(*arrayType);
|
zeroInitialiseMemoryArray(*arrayType);
|
||||||
// stack: updated_memory_pos
|
// stack: updated_memory_pos
|
||||||
@ -667,7 +667,7 @@ void CompilerUtils::pushZeroValue(Type const& _type)
|
|||||||
solAssert(false, "Requested initialisation for unknown type: " + _type.toString());
|
solAssert(false, "Requested initialisation for unknown type: " + _type.toString());
|
||||||
|
|
||||||
// remove the updated memory pointer
|
// remove the updated memory pointer
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
|
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
|
||||||
@ -683,14 +683,14 @@ void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
|
|||||||
errinfo_comment("Stack too deep, try removing local variables.")
|
errinfo_comment("Stack too deep, try removing local variables.")
|
||||||
);
|
);
|
||||||
for (unsigned i = 0; i < size; ++i)
|
for (unsigned i = 0; i < size; ++i)
|
||||||
m_context << eth::swapInstruction(stackPosition - size + 1) << eth::Instruction::POP;
|
m_context << swapInstruction(stackPosition - size + 1) << Instruction::POP;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize)
|
void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize)
|
||||||
{
|
{
|
||||||
solAssert(_stackDepth <= 16, "Stack too deep, try removing local variables.");
|
solAssert(_stackDepth <= 16, "Stack too deep, try removing local variables.");
|
||||||
for (unsigned i = 0; i < _itemSize; ++i)
|
for (unsigned i = 0; i < _itemSize; ++i)
|
||||||
m_context << eth::dupInstruction(_stackDepth);
|
m_context << dupInstruction(_stackDepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerUtils::moveToStackTop(unsigned _stackDepth, unsigned _itemSize)
|
void CompilerUtils::moveToStackTop(unsigned _stackDepth, unsigned _itemSize)
|
||||||
@ -712,14 +712,14 @@ void CompilerUtils::rotateStackUp(unsigned _items)
|
|||||||
{
|
{
|
||||||
solAssert(_items - 1 <= 16, "Stack too deep, try removing local variables.");
|
solAssert(_items - 1 <= 16, "Stack too deep, try removing local variables.");
|
||||||
for (unsigned i = 1; i < _items; ++i)
|
for (unsigned i = 1; i < _items; ++i)
|
||||||
m_context << eth::swapInstruction(_items - i);
|
m_context << swapInstruction(_items - i);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerUtils::rotateStackDown(unsigned _items)
|
void CompilerUtils::rotateStackDown(unsigned _items)
|
||||||
{
|
{
|
||||||
solAssert(_items - 1 <= 16, "Stack too deep, try removing local variables.");
|
solAssert(_items - 1 <= 16, "Stack too deep, try removing local variables.");
|
||||||
for (unsigned i = 1; i < _items; ++i)
|
for (unsigned i = 1; i < _items; ++i)
|
||||||
m_context << eth::swapInstruction(i);
|
m_context << swapInstruction(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerUtils::popStackElement(Type const& _type)
|
void CompilerUtils::popStackElement(Type const& _type)
|
||||||
@ -730,7 +730,7 @@ void CompilerUtils::popStackElement(Type const& _type)
|
|||||||
void CompilerUtils::popStackSlots(size_t _amount)
|
void CompilerUtils::popStackSlots(size_t _amount)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < _amount; ++i)
|
for (size_t i = 0; i < _amount; ++i)
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned CompilerUtils::sizeOnStack(vector<shared_ptr<Type const>> const& _variableTypes)
|
unsigned CompilerUtils::sizeOnStack(vector<shared_ptr<Type const>> const& _variableTypes)
|
||||||
@ -744,7 +744,7 @@ unsigned CompilerUtils::sizeOnStack(vector<shared_ptr<Type const>> const& _varia
|
|||||||
void CompilerUtils::computeHashStatic()
|
void CompilerUtils::computeHashStatic()
|
||||||
{
|
{
|
||||||
storeInMemory(0);
|
storeInMemory(0);
|
||||||
m_context << u256(32) << u256(0) << eth::Instruction::SHA3;
|
m_context << u256(32) << u256(0) << Instruction::SHA3;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerUtils::storeStringData(bytesConstRef _data)
|
void CompilerUtils::storeStringData(bytesConstRef _data)
|
||||||
@ -758,14 +758,14 @@ void CompilerUtils::storeStringData(bytesConstRef _data)
|
|||||||
m_context << h256::Arith(h256(_data.cropped(i), h256::AlignLeft));
|
m_context << h256::Arith(h256(_data.cropped(i), h256::AlignLeft));
|
||||||
storeInMemoryDynamic(IntegerType(256));
|
storeInMemoryDynamic(IntegerType(256));
|
||||||
}
|
}
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// stack: mempos mempos_data
|
// stack: mempos mempos_data
|
||||||
m_context.appendData(_data.toBytes());
|
m_context.appendData(_data.toBytes());
|
||||||
m_context << u256(_data.size()) << eth::Instruction::SWAP2;
|
m_context << u256(_data.size()) << Instruction::SWAP2;
|
||||||
m_context << eth::Instruction::CODECOPY;
|
m_context << Instruction::CODECOPY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -774,18 +774,18 @@ unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCallda
|
|||||||
unsigned numBytes = _type.calldataEncodedSize(_padToWordBoundaries);
|
unsigned numBytes = _type.calldataEncodedSize(_padToWordBoundaries);
|
||||||
bool leftAligned = _type.category() == Type::Category::FixedBytes;
|
bool leftAligned = _type.category() == Type::Category::FixedBytes;
|
||||||
if (numBytes == 0)
|
if (numBytes == 0)
|
||||||
m_context << eth::Instruction::POP << u256(0);
|
m_context << Instruction::POP << u256(0);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
solAssert(numBytes <= 32, "Static memory load of more than 32 bytes requested.");
|
solAssert(numBytes <= 32, "Static memory load of more than 32 bytes requested.");
|
||||||
m_context << (_fromCalldata ? eth::Instruction::CALLDATALOAD : eth::Instruction::MLOAD);
|
m_context << (_fromCalldata ? Instruction::CALLDATALOAD : Instruction::MLOAD);
|
||||||
if (numBytes != 32)
|
if (numBytes != 32)
|
||||||
{
|
{
|
||||||
// add leading or trailing zeros by dividing/multiplying depending on alignment
|
// add leading or trailing zeros by dividing/multiplying depending on alignment
|
||||||
u256 shiftFactor = u256(1) << ((32 - numBytes) * 8);
|
u256 shiftFactor = u256(1) << ((32 - numBytes) * 8);
|
||||||
m_context << shiftFactor << eth::Instruction::SWAP1 << eth::Instruction::DIV;
|
m_context << shiftFactor << Instruction::SWAP1 << Instruction::DIV;
|
||||||
if (leftAligned)
|
if (leftAligned)
|
||||||
m_context << shiftFactor << eth::Instruction::MUL;
|
m_context << shiftFactor << Instruction::MUL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -797,9 +797,9 @@ void CompilerUtils::cleanHigherOrderBits(IntegerType const& _typeOnStack)
|
|||||||
if (_typeOnStack.numBits() == 256)
|
if (_typeOnStack.numBits() == 256)
|
||||||
return;
|
return;
|
||||||
else if (_typeOnStack.isSigned())
|
else if (_typeOnStack.isSigned())
|
||||||
m_context << u256(_typeOnStack.numBits() / 8 - 1) << eth::Instruction::SIGNEXTEND;
|
m_context << u256(_typeOnStack.numBits() / 8 - 1) << Instruction::SIGNEXTEND;
|
||||||
else
|
else
|
||||||
m_context << ((u256(1) << _typeOnStack.numBits()) - 1) << eth::Instruction::AND;
|
m_context << ((u256(1) << _typeOnStack.numBits()) - 1) << Instruction::AND;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const
|
unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const
|
||||||
@ -807,13 +807,13 @@ unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBou
|
|||||||
unsigned numBytes = _type.calldataEncodedSize(_padToWordBoundaries);
|
unsigned numBytes = _type.calldataEncodedSize(_padToWordBoundaries);
|
||||||
bool leftAligned = _type.category() == Type::Category::FixedBytes;
|
bool leftAligned = _type.category() == Type::Category::FixedBytes;
|
||||||
if (numBytes == 0)
|
if (numBytes == 0)
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
solAssert(numBytes <= 32, "Memory store of more than 32 bytes requested.");
|
solAssert(numBytes <= 32, "Memory store of more than 32 bytes requested.");
|
||||||
if (numBytes != 32 && !leftAligned && !_padToWordBoundaries)
|
if (numBytes != 32 && !leftAligned && !_padToWordBoundaries)
|
||||||
// shift the value accordingly before storing
|
// shift the value accordingly before storing
|
||||||
m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
|
m_context << (u256(1) << ((32 - numBytes) * 8)) << Instruction::MUL;
|
||||||
}
|
}
|
||||||
return numBytes;
|
return numBytes;
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ void ExpressionCompiler::appendConstStateVariableAccessor(VariableDeclaration co
|
|||||||
utils().convertType(*_varDecl.value()->annotation().type, *_varDecl.annotation().type);
|
utils().convertType(*_varDecl.value()->annotation().type, *_varDecl.annotation().type);
|
||||||
|
|
||||||
// append return
|
// append return
|
||||||
m_context << eth::dupInstruction(_varDecl.annotation().type->sizeOnStack() + 1);
|
m_context << dupInstruction(_varDecl.annotation().type->sizeOnStack() + 1);
|
||||||
m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction);
|
m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,13 +103,13 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
|
|||||||
"Accessors for mapping with dynamically-sized keys not yet implemented."
|
"Accessors for mapping with dynamically-sized keys not yet implemented."
|
||||||
);
|
);
|
||||||
// pop offset
|
// pop offset
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
// move storage offset to memory.
|
// move storage offset to memory.
|
||||||
utils().storeInMemory(32);
|
utils().storeInMemory(32);
|
||||||
// move key to memory.
|
// move key to memory.
|
||||||
utils().copyToStackTop(paramTypes.size() - i, 1);
|
utils().copyToStackTop(paramTypes.size() - i, 1);
|
||||||
utils().storeInMemory(0);
|
utils().storeInMemory(0);
|
||||||
m_context << u256(64) << u256(0) << eth::Instruction::SHA3;
|
m_context << u256(64) << u256(0) << Instruction::SHA3;
|
||||||
// push offset
|
// push offset
|
||||||
m_context << u256(0);
|
m_context << u256(0);
|
||||||
returnType = mappingType->valueType();
|
returnType = mappingType->valueType();
|
||||||
@ -117,7 +117,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
|
|||||||
else if (auto arrayType = dynamic_cast<ArrayType const*>(returnType.get()))
|
else if (auto arrayType = dynamic_cast<ArrayType const*>(returnType.get()))
|
||||||
{
|
{
|
||||||
// pop offset
|
// pop offset
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
utils().copyToStackTop(paramTypes.size() - i + 1, 1);
|
utils().copyToStackTop(paramTypes.size() - i + 1, 1);
|
||||||
ArrayUtils(m_context).accessIndex(*arrayType);
|
ArrayUtils(m_context).accessIndex(*arrayType);
|
||||||
returnType = arrayType->baseType();
|
returnType = arrayType->baseType();
|
||||||
@ -127,12 +127,12 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
|
|||||||
}
|
}
|
||||||
// remove index arguments.
|
// remove index arguments.
|
||||||
if (paramTypes.size() == 1)
|
if (paramTypes.size() == 1)
|
||||||
m_context << eth::Instruction::SWAP2 << eth::Instruction::POP << eth::Instruction::SWAP1;
|
m_context << Instruction::SWAP2 << Instruction::POP << Instruction::SWAP1;
|
||||||
else if (paramTypes.size() >= 2)
|
else if (paramTypes.size() >= 2)
|
||||||
{
|
{
|
||||||
m_context << eth::swapInstruction(paramTypes.size());
|
m_context << swapInstruction(paramTypes.size());
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
m_context << eth::swapInstruction(paramTypes.size());
|
m_context << swapInstruction(paramTypes.size());
|
||||||
utils().popStackSlots(paramTypes.size() - 1);
|
utils().popStackSlots(paramTypes.size() - 1);
|
||||||
}
|
}
|
||||||
unsigned retSizeOnStack = 0;
|
unsigned retSizeOnStack = 0;
|
||||||
@ -141,7 +141,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
|
|||||||
if (StructType const* structType = dynamic_cast<StructType const*>(returnType.get()))
|
if (StructType const* structType = dynamic_cast<StructType const*>(returnType.get()))
|
||||||
{
|
{
|
||||||
// remove offset
|
// remove offset
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
auto const& names = accessorType.returnParameterNames();
|
auto const& names = accessorType.returnParameterNames();
|
||||||
// struct
|
// struct
|
||||||
for (size_t i = 0; i < names.size(); ++i)
|
for (size_t i = 0; i < names.size(); ++i)
|
||||||
@ -152,7 +152,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
|
|||||||
if (!arrayType->isByteArray())
|
if (!arrayType->isByteArray())
|
||||||
continue;
|
continue;
|
||||||
pair<u256, unsigned> const& offsets = structType->storageOffsetsOfMember(names[i]);
|
pair<u256, unsigned> const& offsets = structType->storageOffsetsOfMember(names[i]);
|
||||||
m_context << eth::Instruction::DUP1 << u256(offsets.first) << eth::Instruction::ADD << u256(offsets.second);
|
m_context << Instruction::DUP1 << u256(offsets.first) << Instruction::ADD << u256(offsets.second);
|
||||||
TypePointer memberType = structType->memberType(names[i]);
|
TypePointer memberType = structType->memberType(names[i]);
|
||||||
StorageItem(m_context, *memberType).retrieveValue(SourceLocation(), true);
|
StorageItem(m_context, *memberType).retrieveValue(SourceLocation(), true);
|
||||||
utils().convertType(*memberType, *returnTypes[i]);
|
utils().convertType(*memberType, *returnTypes[i]);
|
||||||
@ -160,7 +160,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
|
|||||||
retSizeOnStack += returnTypes[i]->sizeOnStack();
|
retSizeOnStack += returnTypes[i]->sizeOnStack();
|
||||||
}
|
}
|
||||||
// remove slot
|
// remove slot
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -172,7 +172,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
|
|||||||
}
|
}
|
||||||
solAssert(retSizeOnStack == utils().sizeOnStack(returnTypes), "");
|
solAssert(retSizeOnStack == utils().sizeOnStack(returnTypes), "");
|
||||||
solAssert(retSizeOnStack <= 15, "Stack is too deep.");
|
solAssert(retSizeOnStack <= 15, "Stack is too deep.");
|
||||||
m_context << eth::dupInstruction(retSizeOnStack + 1);
|
m_context << dupInstruction(retSizeOnStack + 1);
|
||||||
m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction);
|
m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,7 +226,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
|
|||||||
solAssert(itemSize + lvalueSize <= 16, "Stack too deep, try removing local variables.");
|
solAssert(itemSize + lvalueSize <= 16, "Stack too deep, try removing local variables.");
|
||||||
// value [lvalue_ref] updated_value
|
// value [lvalue_ref] updated_value
|
||||||
for (unsigned i = 0; i < itemSize; ++i)
|
for (unsigned i = 0; i < itemSize; ++i)
|
||||||
m_context << eth::swapInstruction(itemSize + lvalueSize) << eth::Instruction::POP;
|
m_context << swapInstruction(itemSize + lvalueSize) << Instruction::POP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_currentLValue->storeValue(*type, _assignment.location());
|
m_currentLValue->storeValue(*type, _assignment.location());
|
||||||
@ -243,7 +243,7 @@ bool ExpressionCompiler::visit(TupleExpression const& _tuple)
|
|||||||
solAssert(!arrayType.isDynamicallySized(), "Cannot create dynamically sized inline array.");
|
solAssert(!arrayType.isDynamicallySized(), "Cannot create dynamically sized inline array.");
|
||||||
m_context << max(u256(32u), arrayType.memorySize());
|
m_context << max(u256(32u), arrayType.memorySize());
|
||||||
utils().allocateMemory();
|
utils().allocateMemory();
|
||||||
m_context << eth::Instruction::DUP1;
|
m_context << Instruction::DUP1;
|
||||||
|
|
||||||
for (auto const& component: _tuple.components())
|
for (auto const& component: _tuple.components())
|
||||||
{
|
{
|
||||||
@ -252,7 +252,7 @@ bool ExpressionCompiler::visit(TupleExpression const& _tuple)
|
|||||||
utils().storeInMemoryDynamic(*arrayType.baseType(), true);
|
utils().storeInMemoryDynamic(*arrayType.baseType(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -298,13 +298,13 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
|
|||||||
switch (_unaryOperation.getOperator())
|
switch (_unaryOperation.getOperator())
|
||||||
{
|
{
|
||||||
case Token::Not: // !
|
case Token::Not: // !
|
||||||
m_context << eth::Instruction::ISZERO;
|
m_context << Instruction::ISZERO;
|
||||||
break;
|
break;
|
||||||
case Token::BitNot: // ~
|
case Token::BitNot: // ~
|
||||||
m_context << eth::Instruction::NOT;
|
m_context << Instruction::NOT;
|
||||||
break;
|
break;
|
||||||
case Token::After: // after
|
case Token::After: // after
|
||||||
m_context << eth::Instruction::TIMESTAMP << eth::Instruction::ADD;
|
m_context << Instruction::TIMESTAMP << Instruction::ADD;
|
||||||
break;
|
break;
|
||||||
case Token::Delete: // delete
|
case Token::Delete: // delete
|
||||||
solAssert(!!m_currentLValue, "LValue not retrieved.");
|
solAssert(!!m_currentLValue, "LValue not retrieved.");
|
||||||
@ -319,20 +319,20 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
|
|||||||
{
|
{
|
||||||
// store value for later
|
// store value for later
|
||||||
solAssert(_unaryOperation.annotation().type->sizeOnStack() == 1, "Stack size != 1 not implemented.");
|
solAssert(_unaryOperation.annotation().type->sizeOnStack() == 1, "Stack size != 1 not implemented.");
|
||||||
m_context << eth::Instruction::DUP1;
|
m_context << Instruction::DUP1;
|
||||||
if (m_currentLValue->sizeOnStack() > 0)
|
if (m_currentLValue->sizeOnStack() > 0)
|
||||||
for (unsigned i = 1 + m_currentLValue->sizeOnStack(); i > 0; --i)
|
for (unsigned i = 1 + m_currentLValue->sizeOnStack(); i > 0; --i)
|
||||||
m_context << eth::swapInstruction(i);
|
m_context << swapInstruction(i);
|
||||||
}
|
}
|
||||||
m_context << u256(1);
|
m_context << u256(1);
|
||||||
if (_unaryOperation.getOperator() == Token::Inc)
|
if (_unaryOperation.getOperator() == Token::Inc)
|
||||||
m_context << eth::Instruction::ADD;
|
m_context << Instruction::ADD;
|
||||||
else
|
else
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::SUB;
|
m_context << Instruction::SWAP1 << Instruction::SUB;
|
||||||
// Stack for prefix: [ref...] (*ref)+-1
|
// Stack for prefix: [ref...] (*ref)+-1
|
||||||
// Stack for postfix: *ref [ref...] (*ref)+-1
|
// Stack for postfix: *ref [ref...] (*ref)+-1
|
||||||
for (unsigned i = m_currentLValue->sizeOnStack(); i > 0; --i)
|
for (unsigned i = m_currentLValue->sizeOnStack(); i > 0; --i)
|
||||||
m_context << eth::swapInstruction(i);
|
m_context << swapInstruction(i);
|
||||||
m_currentLValue->storeValue(
|
m_currentLValue->storeValue(
|
||||||
*_unaryOperation.annotation().type, _unaryOperation.location(),
|
*_unaryOperation.annotation().type, _unaryOperation.location(),
|
||||||
!_unaryOperation.isPrefixOperation());
|
!_unaryOperation.isPrefixOperation());
|
||||||
@ -342,7 +342,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
|
|||||||
// unary add, so basically no-op
|
// unary add, so basically no-op
|
||||||
break;
|
break;
|
||||||
case Token::Sub: // -
|
case Token::Sub: // -
|
||||||
m_context << u256(0) << eth::Instruction::SUB;
|
m_context << u256(0) << Instruction::SUB;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid unary operator: " +
|
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid unary operator: " +
|
||||||
@ -452,7 +452,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
|
|
||||||
m_context << max(u256(32u), structType.memorySize());
|
m_context << max(u256(32u), structType.memorySize());
|
||||||
utils().allocateMemory();
|
utils().allocateMemory();
|
||||||
m_context << eth::Instruction::DUP1;
|
m_context << Instruction::DUP1;
|
||||||
|
|
||||||
for (unsigned i = 0; i < arguments.size(); ++i)
|
for (unsigned i = 0; i < arguments.size(); ++i)
|
||||||
{
|
{
|
||||||
@ -460,7 +460,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
utils().convertType(*arguments[i]->annotation().type, *functionType->parameterTypes()[i]);
|
utils().convertType(*arguments[i]->annotation().type, *functionType->parameterTypes()[i]);
|
||||||
utils().storeInMemoryDynamic(*functionType->parameterTypes()[i]);
|
utils().storeInMemoryDynamic(*functionType->parameterTypes()[i]);
|
||||||
}
|
}
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -518,21 +518,21 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
utils().fetchFreeMemoryPointer();
|
utils().fetchFreeMemoryPointer();
|
||||||
// pushes size
|
// pushes size
|
||||||
eth::AssemblyItem subroutine = m_context.addSubroutine(assembly);
|
eth::AssemblyItem subroutine = m_context.addSubroutine(assembly);
|
||||||
m_context << eth::Instruction::DUP1 << subroutine;
|
m_context << Instruction::DUP1 << subroutine;
|
||||||
m_context << eth::Instruction::DUP4 << eth::Instruction::CODECOPY;
|
m_context << Instruction::DUP4 << Instruction::CODECOPY;
|
||||||
|
|
||||||
m_context << eth::Instruction::ADD;
|
m_context << Instruction::ADD;
|
||||||
utils().encodeToMemory(argumentTypes, function.parameterTypes());
|
utils().encodeToMemory(argumentTypes, function.parameterTypes());
|
||||||
// now on stack: memory_end_ptr
|
// now on stack: memory_end_ptr
|
||||||
// need: size, offset, endowment
|
// need: size, offset, endowment
|
||||||
utils().toSizeAfterFreeMemoryPointer();
|
utils().toSizeAfterFreeMemoryPointer();
|
||||||
if (function.valueSet())
|
if (function.valueSet())
|
||||||
m_context << eth::dupInstruction(3);
|
m_context << dupInstruction(3);
|
||||||
else
|
else
|
||||||
m_context << u256(0);
|
m_context << u256(0);
|
||||||
m_context << eth::Instruction::CREATE;
|
m_context << Instruction::CREATE;
|
||||||
if (function.valueSet())
|
if (function.valueSet())
|
||||||
m_context << eth::swapInstruction(1) << eth::Instruction::POP;
|
m_context << swapInstruction(1) << Instruction::POP;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Location::SetGas:
|
case Location::SetGas:
|
||||||
@ -546,9 +546,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
// Its values of gasSet and valueSet is equal to the original function's though.
|
// Its values of gasSet and valueSet is equal to the original function's though.
|
||||||
unsigned stackDepth = (function.gasSet() ? 1 : 0) + (function.valueSet() ? 1 : 0);
|
unsigned stackDepth = (function.gasSet() ? 1 : 0) + (function.valueSet() ? 1 : 0);
|
||||||
if (stackDepth > 0)
|
if (stackDepth > 0)
|
||||||
m_context << eth::swapInstruction(stackDepth);
|
m_context << swapInstruction(stackDepth);
|
||||||
if (function.gasSet())
|
if (function.gasSet())
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Location::SetValue:
|
case Location::SetValue:
|
||||||
@ -557,7 +557,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
// Note that function is not the original function, but the ".value" function.
|
// Note that function is not the original function, but the ".value" function.
|
||||||
// Its values of gasSet and valueSet is equal to the original function's though.
|
// Its values of gasSet and valueSet is equal to the original function's though.
|
||||||
if (function.valueSet())
|
if (function.valueSet())
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
arguments.front()->accept(*this);
|
arguments.front()->accept(*this);
|
||||||
break;
|
break;
|
||||||
case Location::Send:
|
case Location::Send:
|
||||||
@ -586,7 +586,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
case Location::Selfdestruct:
|
case Location::Selfdestruct:
|
||||||
arguments.front()->accept(*this);
|
arguments.front()->accept(*this);
|
||||||
utils().convertType(*arguments.front()->annotation().type, *function.parameterTypes().front(), true);
|
utils().convertType(*arguments.front()->annotation().type, *function.parameterTypes().front(), true);
|
||||||
m_context << eth::Instruction::SUICIDE;
|
m_context << Instruction::SUICIDE;
|
||||||
break;
|
break;
|
||||||
case Location::SHA3:
|
case Location::SHA3:
|
||||||
{
|
{
|
||||||
@ -599,7 +599,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
utils().fetchFreeMemoryPointer();
|
utils().fetchFreeMemoryPointer();
|
||||||
utils().encodeToMemory(argumentTypes, TypePointers(), function.padArguments(), true);
|
utils().encodeToMemory(argumentTypes, TypePointers(), function.padArguments(), true);
|
||||||
utils().toSizeAfterFreeMemoryPointer();
|
utils().toSizeAfterFreeMemoryPointer();
|
||||||
m_context << eth::Instruction::SHA3;
|
m_context << Instruction::SHA3;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Location::Log0:
|
case Location::Log0:
|
||||||
@ -622,7 +622,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
false,
|
false,
|
||||||
true);
|
true);
|
||||||
utils().toSizeAfterFreeMemoryPointer();
|
utils().toSizeAfterFreeMemoryPointer();
|
||||||
m_context << eth::logInstruction(logNumber);
|
m_context << logInstruction(logNumber);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Location::Event:
|
case Location::Event:
|
||||||
@ -646,7 +646,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
true
|
true
|
||||||
);
|
);
|
||||||
utils().toSizeAfterFreeMemoryPointer();
|
utils().toSizeAfterFreeMemoryPointer();
|
||||||
m_context << eth::Instruction::SHA3;
|
m_context << Instruction::SHA3;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
utils().convertType(
|
utils().convertType(
|
||||||
@ -676,14 +676,14 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
utils().encodeToMemory(nonIndexedArgTypes, nonIndexedParamTypes);
|
utils().encodeToMemory(nonIndexedArgTypes, nonIndexedParamTypes);
|
||||||
// need: topic1 ... topicn memsize memstart
|
// need: topic1 ... topicn memsize memstart
|
||||||
utils().toSizeAfterFreeMemoryPointer();
|
utils().toSizeAfterFreeMemoryPointer();
|
||||||
m_context << eth::logInstruction(numIndexed);
|
m_context << logInstruction(numIndexed);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Location::BlockHash:
|
case Location::BlockHash:
|
||||||
{
|
{
|
||||||
arguments[0]->accept(*this);
|
arguments[0]->accept(*this);
|
||||||
utils().convertType(*arguments[0]->annotation().type, *function.parameterTypes()[0], true);
|
utils().convertType(*arguments[0]->annotation().type, *function.parameterTypes()[0], true);
|
||||||
m_context << eth::Instruction::BLOCKHASH;
|
m_context << Instruction::BLOCKHASH;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Location::AddMod:
|
case Location::AddMod:
|
||||||
@ -695,9 +695,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
utils().convertType(*arguments[2 - i]->annotation().type, IntegerType(256));
|
utils().convertType(*arguments[2 - i]->annotation().type, IntegerType(256));
|
||||||
}
|
}
|
||||||
if (function.location() == Location::AddMod)
|
if (function.location() == Location::AddMod)
|
||||||
m_context << eth::Instruction::ADDMOD;
|
m_context << Instruction::ADDMOD;
|
||||||
else
|
else
|
||||||
m_context << eth::Instruction::MULMOD;
|
m_context << Instruction::MULMOD;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Location::ECRecover:
|
case Location::ECRecover:
|
||||||
@ -710,7 +710,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
{Location::RIPEMD160, 3}};
|
{Location::RIPEMD160, 3}};
|
||||||
m_context << contractAddresses.find(function.location())->second;
|
m_context << contractAddresses.find(function.location())->second;
|
||||||
for (unsigned i = function.sizeOnStack(); i > 0; --i)
|
for (unsigned i = function.sizeOnStack(); i > 0; --i)
|
||||||
m_context << eth::swapInstruction(i);
|
m_context << swapInstruction(i);
|
||||||
appendExternalFunctionCall(function, arguments);
|
appendExternalFunctionCall(function, arguments);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -727,13 +727,13 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
make_shared<ArrayType>(DataLocation::Storage);
|
make_shared<ArrayType>(DataLocation::Storage);
|
||||||
// get the current length
|
// get the current length
|
||||||
ArrayUtils(m_context).retrieveLength(*arrayType);
|
ArrayUtils(m_context).retrieveLength(*arrayType);
|
||||||
m_context << eth::Instruction::DUP1;
|
m_context << Instruction::DUP1;
|
||||||
// stack: ArrayReference currentLength currentLength
|
// stack: ArrayReference currentLength currentLength
|
||||||
m_context << u256(1) << eth::Instruction::ADD;
|
m_context << u256(1) << Instruction::ADD;
|
||||||
// stack: ArrayReference currentLength newLength
|
// stack: ArrayReference currentLength newLength
|
||||||
m_context << eth::Instruction::DUP3 << eth::Instruction::DUP2;
|
m_context << Instruction::DUP3 << Instruction::DUP2;
|
||||||
ArrayUtils(m_context).resizeDynamicArray(*arrayType);
|
ArrayUtils(m_context).resizeDynamicArray(*arrayType);
|
||||||
m_context << eth::Instruction::SWAP2 << eth::Instruction::SWAP1;
|
m_context << Instruction::SWAP2 << Instruction::SWAP1;
|
||||||
// stack: newLength ArrayReference oldLength
|
// stack: newLength ArrayReference oldLength
|
||||||
ArrayUtils(m_context).accessIndex(*arrayType, false);
|
ArrayUtils(m_context).accessIndex(*arrayType, false);
|
||||||
|
|
||||||
@ -766,36 +766,36 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
// Stack: requested_length
|
// Stack: requested_length
|
||||||
// Allocate at max(MSIZE, freeMemoryPointer)
|
// Allocate at max(MSIZE, freeMemoryPointer)
|
||||||
utils().fetchFreeMemoryPointer();
|
utils().fetchFreeMemoryPointer();
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::MSIZE;
|
m_context << Instruction::DUP1 << Instruction::MSIZE;
|
||||||
m_context << eth::Instruction::LT;
|
m_context << Instruction::LT;
|
||||||
auto initialise = m_context.appendConditionalJump();
|
auto initialise = m_context.appendConditionalJump();
|
||||||
// Free memory pointer does not point to empty memory, use MSIZE.
|
// Free memory pointer does not point to empty memory, use MSIZE.
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
m_context << eth::Instruction::MSIZE;
|
m_context << Instruction::MSIZE;
|
||||||
m_context << initialise;
|
m_context << initialise;
|
||||||
|
|
||||||
// Stack: requested_length memptr
|
// Stack: requested_length memptr
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
// Stack: memptr requested_length
|
// Stack: memptr requested_length
|
||||||
// store length
|
// store length
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::MSTORE;
|
m_context << Instruction::DUP1 << Instruction::DUP3 << Instruction::MSTORE;
|
||||||
// Stack: memptr requested_length
|
// Stack: memptr requested_length
|
||||||
// update free memory pointer
|
// update free memory pointer
|
||||||
m_context << eth::Instruction::DUP1 << arrayType.baseType()->memoryHeadSize();
|
m_context << Instruction::DUP1 << arrayType.baseType()->memoryHeadSize();
|
||||||
m_context << eth::Instruction::MUL << u256(32) << eth::Instruction::ADD;
|
m_context << Instruction::MUL << u256(32) << Instruction::ADD;
|
||||||
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
|
m_context << Instruction::DUP3 << Instruction::ADD;
|
||||||
utils().storeFreeMemoryPointer();
|
utils().storeFreeMemoryPointer();
|
||||||
// Stack: memptr requested_length
|
// Stack: memptr requested_length
|
||||||
|
|
||||||
// We only have to initialise if the base type is a not a value type.
|
// We only have to initialise if the base type is a not a value type.
|
||||||
if (dynamic_cast<ReferenceType const*>(arrayType.baseType().get()))
|
if (dynamic_cast<ReferenceType const*>(arrayType.baseType().get()))
|
||||||
{
|
{
|
||||||
m_context << eth::Instruction::DUP2 << u256(32) << eth::Instruction::ADD;
|
m_context << Instruction::DUP2 << u256(32) << Instruction::ADD;
|
||||||
utils().zeroInitialiseMemoryArray(arrayType);
|
utils().zeroInitialiseMemoryArray(arrayType);
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -876,7 +876,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
|
|||||||
IntegerType(0, IntegerType::Modifier::Address),
|
IntegerType(0, IntegerType::Modifier::Address),
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
m_context << eth::Instruction::BALANCE;
|
m_context << Instruction::BALANCE;
|
||||||
}
|
}
|
||||||
else if ((set<string>{"send", "call", "callcode", "delegatecall"}).count(member))
|
else if ((set<string>{"send", "call", "callcode", "delegatecall"}).count(member))
|
||||||
utils().convertType(
|
utils().convertType(
|
||||||
@ -894,30 +894,30 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
|
|||||||
case Type::Category::Magic:
|
case Type::Category::Magic:
|
||||||
// we can ignore the kind of magic and only look at the name of the member
|
// we can ignore the kind of magic and only look at the name of the member
|
||||||
if (member == "coinbase")
|
if (member == "coinbase")
|
||||||
m_context << eth::Instruction::COINBASE;
|
m_context << Instruction::COINBASE;
|
||||||
else if (member == "timestamp")
|
else if (member == "timestamp")
|
||||||
m_context << eth::Instruction::TIMESTAMP;
|
m_context << Instruction::TIMESTAMP;
|
||||||
else if (member == "difficulty")
|
else if (member == "difficulty")
|
||||||
m_context << eth::Instruction::DIFFICULTY;
|
m_context << Instruction::DIFFICULTY;
|
||||||
else if (member == "number")
|
else if (member == "number")
|
||||||
m_context << eth::Instruction::NUMBER;
|
m_context << Instruction::NUMBER;
|
||||||
else if (member == "gaslimit")
|
else if (member == "gaslimit")
|
||||||
m_context << eth::Instruction::GASLIMIT;
|
m_context << Instruction::GASLIMIT;
|
||||||
else if (member == "sender")
|
else if (member == "sender")
|
||||||
m_context << eth::Instruction::CALLER;
|
m_context << Instruction::CALLER;
|
||||||
else if (member == "value")
|
else if (member == "value")
|
||||||
m_context << eth::Instruction::CALLVALUE;
|
m_context << Instruction::CALLVALUE;
|
||||||
else if (member == "origin")
|
else if (member == "origin")
|
||||||
m_context << eth::Instruction::ORIGIN;
|
m_context << Instruction::ORIGIN;
|
||||||
else if (member == "gas")
|
else if (member == "gas")
|
||||||
m_context << eth::Instruction::GAS;
|
m_context << Instruction::GAS;
|
||||||
else if (member == "gasprice")
|
else if (member == "gasprice")
|
||||||
m_context << eth::Instruction::GASPRICE;
|
m_context << Instruction::GASPRICE;
|
||||||
else if (member == "data")
|
else if (member == "data")
|
||||||
m_context << u256(0) << eth::Instruction::CALLDATASIZE;
|
m_context << u256(0) << Instruction::CALLDATASIZE;
|
||||||
else if (member == "sig")
|
else if (member == "sig")
|
||||||
m_context << u256(0) << eth::Instruction::CALLDATALOAD
|
m_context << u256(0) << Instruction::CALLDATALOAD
|
||||||
<< (u256(0xffffffff) << (256 - 32)) << eth::Instruction::AND;
|
<< (u256(0xffffffff) << (256 - 32)) << Instruction::AND;
|
||||||
else
|
else
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown magic member."));
|
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown magic member."));
|
||||||
break;
|
break;
|
||||||
@ -929,13 +929,13 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
|
|||||||
case DataLocation::Storage:
|
case DataLocation::Storage:
|
||||||
{
|
{
|
||||||
pair<u256, unsigned> const& offsets = type.storageOffsetsOfMember(member);
|
pair<u256, unsigned> const& offsets = type.storageOffsetsOfMember(member);
|
||||||
m_context << offsets.first << eth::Instruction::ADD << u256(offsets.second);
|
m_context << offsets.first << Instruction::ADD << u256(offsets.second);
|
||||||
setLValueToStorageItem(_memberAccess);
|
setLValueToStorageItem(_memberAccess);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DataLocation::Memory:
|
case DataLocation::Memory:
|
||||||
{
|
{
|
||||||
m_context << type.memoryOffsetOfMember(member) << eth::Instruction::ADD;
|
m_context << type.memoryOffsetOfMember(member) << Instruction::ADD;
|
||||||
setLValue<MemoryItem>(_memberAccess, *_memberAccess.annotation().type);
|
setLValue<MemoryItem>(_memberAccess, *_memberAccess.annotation().type);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -986,13 +986,13 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
|
|||||||
switch (type.location())
|
switch (type.location())
|
||||||
{
|
{
|
||||||
case DataLocation::CallData:
|
case DataLocation::CallData:
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
|
m_context << Instruction::SWAP1 << Instruction::POP;
|
||||||
break;
|
break;
|
||||||
case DataLocation::Storage:
|
case DataLocation::Storage:
|
||||||
setLValue<StorageArrayLength>(_memberAccess, type);
|
setLValue<StorageArrayLength>(_memberAccess, type);
|
||||||
break;
|
break;
|
||||||
case DataLocation::Memory:
|
case DataLocation::Memory:
|
||||||
m_context << eth::Instruction::MLOAD;
|
m_context << Instruction::MLOAD;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1046,7 +1046,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
|
|||||||
false,
|
false,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
utils().storeInMemoryDynamic(IntegerType(256));
|
utils().storeInMemoryDynamic(IntegerType(256));
|
||||||
utils().toSizeAfterFreeMemoryPointer();
|
utils().toSizeAfterFreeMemoryPointer();
|
||||||
}
|
}
|
||||||
@ -1054,12 +1054,12 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
|
|||||||
{
|
{
|
||||||
m_context << u256(0); // memory position
|
m_context << u256(0); // memory position
|
||||||
appendExpressionCopyToMemory(*keyType, *_indexAccess.indexExpression());
|
appendExpressionCopyToMemory(*keyType, *_indexAccess.indexExpression());
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
solAssert(CompilerUtils::freeMemoryPointer >= 0x40, "");
|
solAssert(CompilerUtils::freeMemoryPointer >= 0x40, "");
|
||||||
utils().storeInMemoryDynamic(IntegerType(256));
|
utils().storeInMemoryDynamic(IntegerType(256));
|
||||||
m_context << u256(0);
|
m_context << u256(0);
|
||||||
}
|
}
|
||||||
m_context << eth::Instruction::SHA3;
|
m_context << Instruction::SHA3;
|
||||||
m_context << u256(0);
|
m_context << u256(0);
|
||||||
setLValueToStorageItem(_indexAccess);
|
setLValueToStorageItem(_indexAccess);
|
||||||
}
|
}
|
||||||
@ -1109,12 +1109,12 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
|
|||||||
// stack layout: <value> <index>
|
// stack layout: <value> <index>
|
||||||
// check out-of-bounds access
|
// check out-of-bounds access
|
||||||
m_context << u256(fixedBytesType.numBytes());
|
m_context << u256(fixedBytesType.numBytes());
|
||||||
m_context << eth::Instruction::DUP2 << eth::Instruction::LT << eth::Instruction::ISZERO;
|
m_context << Instruction::DUP2 << Instruction::LT << Instruction::ISZERO;
|
||||||
// out-of-bounds access throws exception
|
// out-of-bounds access throws exception
|
||||||
m_context.appendConditionalJumpTo(m_context.errorTag());
|
m_context.appendConditionalJumpTo(m_context.errorTag());
|
||||||
|
|
||||||
m_context << eth::Instruction::BYTE;
|
m_context << Instruction::BYTE;
|
||||||
m_context << (u256(1) << (256 - 8)) << eth::Instruction::MUL;
|
m_context << (u256(1) << (256 - 8)) << Instruction::MUL;
|
||||||
}
|
}
|
||||||
else if (baseType.category() == Type::Category::TypeType)
|
else if (baseType.category() == Type::Category::TypeType)
|
||||||
{
|
{
|
||||||
@ -1139,11 +1139,11 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
|
|||||||
case Type::Category::Contract:
|
case Type::Category::Contract:
|
||||||
// "this" or "super"
|
// "this" or "super"
|
||||||
if (!dynamic_cast<ContractType const&>(*magicVar->type()).isSuper())
|
if (!dynamic_cast<ContractType const&>(*magicVar->type()).isSuper())
|
||||||
m_context << eth::Instruction::ADDRESS;
|
m_context << Instruction::ADDRESS;
|
||||||
break;
|
break;
|
||||||
case Type::Category::Integer:
|
case Type::Category::Integer:
|
||||||
// "now"
|
// "now"
|
||||||
m_context << eth::Instruction::TIMESTAMP;
|
m_context << Instruction::TIMESTAMP;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -1208,11 +1208,11 @@ void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryO
|
|||||||
solAssert(c_op == Token::Or || c_op == Token::And, "");
|
solAssert(c_op == Token::Or || c_op == Token::And, "");
|
||||||
|
|
||||||
_binaryOperation.leftExpression().accept(*this);
|
_binaryOperation.leftExpression().accept(*this);
|
||||||
m_context << eth::Instruction::DUP1;
|
m_context << Instruction::DUP1;
|
||||||
if (c_op == Token::And)
|
if (c_op == Token::And)
|
||||||
m_context << eth::Instruction::ISZERO;
|
m_context << Instruction::ISZERO;
|
||||||
eth::AssemblyItem endLabel = m_context.appendConditionalJump();
|
eth::AssemblyItem endLabel = m_context.appendConditionalJump();
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
_binaryOperation.rightExpression().accept(*this);
|
_binaryOperation.rightExpression().accept(*this);
|
||||||
m_context << endLabel;
|
m_context << endLabel;
|
||||||
}
|
}
|
||||||
@ -1221,9 +1221,9 @@ void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type
|
|||||||
{
|
{
|
||||||
if (_operator == Token::Equal || _operator == Token::NotEqual)
|
if (_operator == Token::Equal || _operator == Token::NotEqual)
|
||||||
{
|
{
|
||||||
m_context << eth::Instruction::EQ;
|
m_context << Instruction::EQ;
|
||||||
if (_operator == Token::NotEqual)
|
if (_operator == Token::NotEqual)
|
||||||
m_context << eth::Instruction::ISZERO;
|
m_context << Instruction::ISZERO;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1235,19 +1235,19 @@ void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type
|
|||||||
{
|
{
|
||||||
case Token::GreaterThanOrEqual:
|
case Token::GreaterThanOrEqual:
|
||||||
m_context <<
|
m_context <<
|
||||||
(isSigned ? eth::Instruction::SLT : eth::Instruction::LT) <<
|
(isSigned ? Instruction::SLT : Instruction::LT) <<
|
||||||
eth::Instruction::ISZERO;
|
Instruction::ISZERO;
|
||||||
break;
|
break;
|
||||||
case Token::LessThanOrEqual:
|
case Token::LessThanOrEqual:
|
||||||
m_context <<
|
m_context <<
|
||||||
(isSigned ? eth::Instruction::SGT : eth::Instruction::GT) <<
|
(isSigned ? Instruction::SGT : Instruction::GT) <<
|
||||||
eth::Instruction::ISZERO;
|
Instruction::ISZERO;
|
||||||
break;
|
break;
|
||||||
case Token::GreaterThan:
|
case Token::GreaterThan:
|
||||||
m_context << (isSigned ? eth::Instruction::SGT : eth::Instruction::GT);
|
m_context << (isSigned ? Instruction::SGT : Instruction::GT);
|
||||||
break;
|
break;
|
||||||
case Token::LessThan:
|
case Token::LessThan:
|
||||||
m_context << (isSigned ? eth::Instruction::SLT : eth::Instruction::LT);
|
m_context << (isSigned ? Instruction::SLT : Instruction::LT);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown comparison operator."));
|
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown comparison operator."));
|
||||||
@ -1275,22 +1275,22 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Ty
|
|||||||
switch (_operator)
|
switch (_operator)
|
||||||
{
|
{
|
||||||
case Token::Add:
|
case Token::Add:
|
||||||
m_context << eth::Instruction::ADD;
|
m_context << Instruction::ADD;
|
||||||
break;
|
break;
|
||||||
case Token::Sub:
|
case Token::Sub:
|
||||||
m_context << eth::Instruction::SUB;
|
m_context << Instruction::SUB;
|
||||||
break;
|
break;
|
||||||
case Token::Mul:
|
case Token::Mul:
|
||||||
m_context << eth::Instruction::MUL;
|
m_context << Instruction::MUL;
|
||||||
break;
|
break;
|
||||||
case Token::Div:
|
case Token::Div:
|
||||||
m_context << (c_isSigned ? eth::Instruction::SDIV : eth::Instruction::DIV);
|
m_context << (c_isSigned ? Instruction::SDIV : Instruction::DIV);
|
||||||
break;
|
break;
|
||||||
case Token::Mod:
|
case Token::Mod:
|
||||||
m_context << (c_isSigned ? eth::Instruction::SMOD : eth::Instruction::MOD);
|
m_context << (c_isSigned ? Instruction::SMOD : Instruction::MOD);
|
||||||
break;
|
break;
|
||||||
case Token::Exp:
|
case Token::Exp:
|
||||||
m_context << eth::Instruction::EXP;
|
m_context << Instruction::EXP;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown arithmetic operator."));
|
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown arithmetic operator."));
|
||||||
@ -1302,13 +1302,13 @@ void ExpressionCompiler::appendBitOperatorCode(Token::Value _operator)
|
|||||||
switch (_operator)
|
switch (_operator)
|
||||||
{
|
{
|
||||||
case Token::BitOr:
|
case Token::BitOr:
|
||||||
m_context << eth::Instruction::OR;
|
m_context << Instruction::OR;
|
||||||
break;
|
break;
|
||||||
case Token::BitAnd:
|
case Token::BitAnd:
|
||||||
m_context << eth::Instruction::AND;
|
m_context << Instruction::AND;
|
||||||
break;
|
break;
|
||||||
case Token::BitXor:
|
case Token::BitXor:
|
||||||
m_context << eth::Instruction::XOR;
|
m_context << Instruction::XOR;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown bit operator."));
|
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown bit operator."));
|
||||||
@ -1392,7 +1392,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
|||||||
true
|
true
|
||||||
);
|
);
|
||||||
for (unsigned i = 0; i < gasValueSize; ++i)
|
for (unsigned i = 0; i < gasValueSize; ++i)
|
||||||
m_context << eth::swapInstruction(gasValueSize - i);
|
m_context << swapInstruction(gasValueSize - i);
|
||||||
gasStackPos++;
|
gasStackPos++;
|
||||||
valueStackPos++;
|
valueStackPos++;
|
||||||
}
|
}
|
||||||
@ -1411,7 +1411,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
|||||||
utils().fetchFreeMemoryPointer();
|
utils().fetchFreeMemoryPointer();
|
||||||
if (!_functionType.isBareCall() || manualFunctionId)
|
if (!_functionType.isBareCall() || manualFunctionId)
|
||||||
{
|
{
|
||||||
m_context << eth::dupInstruction(2 + gasValueSize + CompilerUtils::sizeOnStack(argumentTypes));
|
m_context << dupInstruction(2 + gasValueSize + CompilerUtils::sizeOnStack(argumentTypes));
|
||||||
utils().storeInMemoryDynamic(IntegerType(8 * CompilerUtils::dataStartOffset), false);
|
utils().storeInMemoryDynamic(IntegerType(8 * CompilerUtils::dataStartOffset), false);
|
||||||
}
|
}
|
||||||
// If the function takes arbitrary parameters, copy dynamic length data in place.
|
// If the function takes arbitrary parameters, copy dynamic length data in place.
|
||||||
@ -1437,21 +1437,21 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
|||||||
// put on stack: <size of output> <memory pos of output> <size of input> <memory pos of input>
|
// put on stack: <size of output> <memory pos of output> <size of input> <memory pos of input>
|
||||||
m_context << u256(retSize);
|
m_context << u256(retSize);
|
||||||
utils().fetchFreeMemoryPointer();
|
utils().fetchFreeMemoryPointer();
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP4 << eth::Instruction::SUB;
|
m_context << Instruction::DUP1 << Instruction::DUP4 << Instruction::SUB;
|
||||||
m_context << eth::Instruction::DUP2;
|
m_context << Instruction::DUP2;
|
||||||
|
|
||||||
// CALL arguments: outSize, outOff, inSize, inOff (already present up to here)
|
// CALL arguments: outSize, outOff, inSize, inOff (already present up to here)
|
||||||
// [value,] addr, gas (stack top)
|
// [value,] addr, gas (stack top)
|
||||||
if (isDelegateCall)
|
if (isDelegateCall)
|
||||||
solAssert(!_functionType.valueSet(), "Value set for delegatecall");
|
solAssert(!_functionType.valueSet(), "Value set for delegatecall");
|
||||||
else if (_functionType.valueSet())
|
else if (_functionType.valueSet())
|
||||||
m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos));
|
m_context << dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos));
|
||||||
else
|
else
|
||||||
m_context << u256(0);
|
m_context << u256(0);
|
||||||
m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(contractStackPos));
|
m_context << dupInstruction(m_context.baseToCurrentStackOffset(contractStackPos));
|
||||||
|
|
||||||
if (_functionType.gasSet())
|
if (_functionType.gasSet())
|
||||||
m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos));
|
m_context << dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
eth::EVMSchedule schedule;
|
eth::EVMSchedule schedule;
|
||||||
@ -1464,15 +1464,15 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
|||||||
gasNeededByCaller += schedule.callNewAccountGas; // we never know
|
gasNeededByCaller += schedule.callNewAccountGas; // we never know
|
||||||
m_context <<
|
m_context <<
|
||||||
gasNeededByCaller <<
|
gasNeededByCaller <<
|
||||||
eth::Instruction::GAS <<
|
Instruction::GAS <<
|
||||||
eth::Instruction::SUB;
|
Instruction::SUB;
|
||||||
}
|
}
|
||||||
if (isDelegateCall)
|
if (isDelegateCall)
|
||||||
m_context << eth::Instruction::DELEGATECALL;
|
m_context << Instruction::DELEGATECALL;
|
||||||
else if (isCallCode)
|
else if (isCallCode)
|
||||||
m_context << eth::Instruction::CALLCODE;
|
m_context << Instruction::CALLCODE;
|
||||||
else
|
else
|
||||||
m_context << eth::Instruction::CALL;
|
m_context << Instruction::CALL;
|
||||||
|
|
||||||
unsigned remainsSize =
|
unsigned remainsSize =
|
||||||
2 + // contract address, input_memory_end
|
2 + // contract address, input_memory_end
|
||||||
@ -1481,11 +1481,11 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
|||||||
(!_functionType.isBareCall() || manualFunctionId);
|
(!_functionType.isBareCall() || manualFunctionId);
|
||||||
|
|
||||||
if (returnSuccessCondition)
|
if (returnSuccessCondition)
|
||||||
m_context << eth::swapInstruction(remainsSize);
|
m_context << swapInstruction(remainsSize);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//Propagate error condition (if CALL pushes 0 on stack).
|
//Propagate error condition (if CALL pushes 0 on stack).
|
||||||
m_context << eth::Instruction::ISZERO;
|
m_context << Instruction::ISZERO;
|
||||||
m_context.appendConditionalJumpTo(m_context.errorTag());
|
m_context.appendConditionalJumpTo(m_context.errorTag());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1515,7 +1515,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
|||||||
if (memoryNeeded)
|
if (memoryNeeded)
|
||||||
utils().storeFreeMemoryPointer();
|
utils().storeFreeMemoryPointer();
|
||||||
else
|
else
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libsolidity/codegen/LValue.h>
|
#include <libsolidity/codegen/LValue.h>
|
||||||
#include <libevmcore/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
#include <libsolidity/ast/Types.h>
|
#include <libsolidity/ast/Types.h>
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
#include <libsolidity/codegen/CompilerUtils.h>
|
#include <libsolidity/codegen/CompilerUtils.h>
|
||||||
@ -49,7 +49,7 @@ void StackVariable::retrieveValue(SourceLocation const& _location, bool) const
|
|||||||
);
|
);
|
||||||
solAssert(stackPos + 1 >= m_size, "Size and stack pos mismatch.");
|
solAssert(stackPos + 1 >= m_size, "Size and stack pos mismatch.");
|
||||||
for (unsigned i = 0; i < m_size; ++i)
|
for (unsigned i = 0; i < m_size; ++i)
|
||||||
m_context << eth::dupInstruction(stackPos + 1);
|
m_context << dupInstruction(stackPos + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StackVariable::storeValue(Type const&, SourceLocation const& _location, bool _move) const
|
void StackVariable::storeValue(Type const&, SourceLocation const& _location, bool _move) const
|
||||||
@ -63,7 +63,7 @@ void StackVariable::storeValue(Type const&, SourceLocation const& _location, boo
|
|||||||
);
|
);
|
||||||
else if (stackDiff > 0)
|
else if (stackDiff > 0)
|
||||||
for (unsigned i = 0; i < m_size; ++i)
|
for (unsigned i = 0; i < m_size; ++i)
|
||||||
m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP;
|
m_context << swapInstruction(stackDiff) << Instruction::POP;
|
||||||
if (!_move)
|
if (!_move)
|
||||||
retrieveValue(_location);
|
retrieveValue(_location);
|
||||||
}
|
}
|
||||||
@ -85,11 +85,11 @@ void MemoryItem::retrieveValue(SourceLocation const&, bool _remove) const
|
|||||||
if (m_dataType->isValueType())
|
if (m_dataType->isValueType())
|
||||||
{
|
{
|
||||||
if (!_remove)
|
if (!_remove)
|
||||||
m_context << eth::Instruction::DUP1;
|
m_context << Instruction::DUP1;
|
||||||
CompilerUtils(m_context).loadFromMemoryDynamic(*m_dataType, false, m_padded, false);
|
CompilerUtils(m_context).loadFromMemoryDynamic(*m_dataType, false, m_padded, false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_context << eth::Instruction::MLOAD;
|
m_context << Instruction::MLOAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemoryItem::storeValue(Type const& _sourceType, SourceLocation const&, bool _move) const
|
void MemoryItem::storeValue(Type const& _sourceType, SourceLocation const&, bool _move) const
|
||||||
@ -109,13 +109,13 @@ void MemoryItem::storeValue(Type const& _sourceType, SourceLocation const&, bool
|
|||||||
{
|
{
|
||||||
solAssert(m_dataType->calldataEncodedSize(false) == 1, "Invalid non-padded type.");
|
solAssert(m_dataType->calldataEncodedSize(false) == 1, "Invalid non-padded type.");
|
||||||
if (m_dataType->category() == Type::Category::FixedBytes)
|
if (m_dataType->category() == Type::Category::FixedBytes)
|
||||||
m_context << u256(0) << eth::Instruction::BYTE;
|
m_context << u256(0) << Instruction::BYTE;
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::MSTORE8;
|
m_context << Instruction::SWAP1 << Instruction::MSTORE8;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
utils.storeInMemoryDynamic(*m_dataType, m_padded);
|
utils.storeInMemoryDynamic(*m_dataType, m_padded);
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -124,10 +124,10 @@ void MemoryItem::storeValue(Type const& _sourceType, SourceLocation const&, bool
|
|||||||
|
|
||||||
solAssert(m_dataType->sizeOnStack() == 1, "");
|
solAssert(m_dataType->sizeOnStack() == 1, "");
|
||||||
if (!_move)
|
if (!_move)
|
||||||
m_context << eth::Instruction::DUP2 << eth::Instruction::SWAP1;
|
m_context << Instruction::DUP2 << Instruction::SWAP1;
|
||||||
// stack: [value] value lvalue
|
// stack: [value] value lvalue
|
||||||
// only store the reference
|
// only store the reference
|
||||||
m_context << eth::Instruction::MSTORE;
|
m_context << Instruction::MSTORE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,10 +135,10 @@ void MemoryItem::setToZero(SourceLocation const&, bool _removeReference) const
|
|||||||
{
|
{
|
||||||
CompilerUtils utils(m_context);
|
CompilerUtils utils(m_context);
|
||||||
if (!_removeReference)
|
if (!_removeReference)
|
||||||
m_context << eth::Instruction::DUP1;
|
m_context << Instruction::DUP1;
|
||||||
utils.pushZeroValue(*m_dataType);
|
utils.pushZeroValue(*m_dataType);
|
||||||
utils.storeInMemoryDynamic(*m_dataType, m_padded);
|
utils.storeInMemoryDynamic(*m_dataType, m_padded);
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
}
|
}
|
||||||
|
|
||||||
StorageItem::StorageItem(CompilerContext& _compilerContext, VariableDeclaration const& _declaration):
|
StorageItem::StorageItem(CompilerContext& _compilerContext, VariableDeclaration const& _declaration):
|
||||||
@ -165,29 +165,29 @@ void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const
|
|||||||
{
|
{
|
||||||
solAssert(m_dataType->sizeOnStack() == 1, "Invalid storage ref size.");
|
solAssert(m_dataType->sizeOnStack() == 1, "Invalid storage ref size.");
|
||||||
if (_remove)
|
if (_remove)
|
||||||
m_context << eth::Instruction::POP; // remove byte offset
|
m_context << Instruction::POP; // remove byte offset
|
||||||
else
|
else
|
||||||
m_context << eth::Instruction::DUP2;
|
m_context << Instruction::DUP2;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!_remove)
|
if (!_remove)
|
||||||
CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack());
|
CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack());
|
||||||
if (m_dataType->storageBytes() == 32)
|
if (m_dataType->storageBytes() == 32)
|
||||||
m_context << eth::Instruction::POP << eth::Instruction::SLOAD;
|
m_context << Instruction::POP << Instruction::SLOAD;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_context
|
m_context
|
||||||
<< eth::Instruction::SWAP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1
|
<< Instruction::SWAP1 << Instruction::SLOAD << Instruction::SWAP1
|
||||||
<< u256(0x100) << eth::Instruction::EXP << eth::Instruction::SWAP1 << eth::Instruction::DIV;
|
<< u256(0x100) << Instruction::EXP << Instruction::SWAP1 << Instruction::DIV;
|
||||||
if (m_dataType->category() == Type::Category::FixedBytes)
|
if (m_dataType->category() == Type::Category::FixedBytes)
|
||||||
m_context << (u256(0x1) << (256 - 8 * m_dataType->storageBytes())) << eth::Instruction::MUL;
|
m_context << (u256(0x1) << (256 - 8 * m_dataType->storageBytes())) << Instruction::MUL;
|
||||||
else if (
|
else if (
|
||||||
m_dataType->category() == Type::Category::Integer &&
|
m_dataType->category() == Type::Category::Integer &&
|
||||||
dynamic_cast<IntegerType const&>(*m_dataType).isSigned()
|
dynamic_cast<IntegerType const&>(*m_dataType).isSigned()
|
||||||
)
|
)
|
||||||
m_context << u256(m_dataType->storageBytes() - 1) << eth::Instruction::SIGNEXTEND;
|
m_context << u256(m_dataType->storageBytes() - 1) << Instruction::SIGNEXTEND;
|
||||||
else
|
else
|
||||||
m_context << ((u256(0x1) << (8 * m_dataType->storageBytes())) - 1) << eth::Instruction::AND;
|
m_context << ((u256(0x1) << (8 * m_dataType->storageBytes())) - 1) << Instruction::AND;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,32 +202,32 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
|
|||||||
if (m_dataType->storageBytes() == 32)
|
if (m_dataType->storageBytes() == 32)
|
||||||
{
|
{
|
||||||
// offset should be zero
|
// offset should be zero
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
if (!_move)
|
if (!_move)
|
||||||
m_context << eth::Instruction::DUP2 << eth::Instruction::SWAP1;
|
m_context << Instruction::DUP2 << Instruction::SWAP1;
|
||||||
m_context << eth::Instruction::SSTORE;
|
m_context << Instruction::SSTORE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// OR the value into the other values in the storage slot
|
// OR the value into the other values in the storage slot
|
||||||
m_context << u256(0x100) << eth::Instruction::EXP;
|
m_context << u256(0x100) << Instruction::EXP;
|
||||||
// stack: value storage_ref multiplier
|
// stack: value storage_ref multiplier
|
||||||
// fetch old value
|
// fetch old value
|
||||||
m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD;
|
m_context << Instruction::DUP2 << Instruction::SLOAD;
|
||||||
// stack: value storege_ref multiplier old_full_value
|
// stack: value storege_ref multiplier old_full_value
|
||||||
// clear bytes in old value
|
// clear bytes in old value
|
||||||
m_context
|
m_context
|
||||||
<< eth::Instruction::DUP2 << ((u256(1) << (8 * m_dataType->storageBytes())) - 1)
|
<< Instruction::DUP2 << ((u256(1) << (8 * m_dataType->storageBytes())) - 1)
|
||||||
<< eth::Instruction::MUL;
|
<< Instruction::MUL;
|
||||||
m_context << eth::Instruction::NOT << eth::Instruction::AND;
|
m_context << Instruction::NOT << Instruction::AND;
|
||||||
// stack: value storage_ref multiplier cleared_value
|
// stack: value storage_ref multiplier cleared_value
|
||||||
m_context
|
m_context
|
||||||
<< eth::Instruction::SWAP1 << eth::Instruction::DUP4;
|
<< Instruction::SWAP1 << Instruction::DUP4;
|
||||||
// stack: value storage_ref cleared_value multiplier value
|
// stack: value storage_ref cleared_value multiplier value
|
||||||
if (m_dataType->category() == Type::Category::FixedBytes)
|
if (m_dataType->category() == Type::Category::FixedBytes)
|
||||||
m_context
|
m_context
|
||||||
<< (u256(0x1) << (256 - 8 * dynamic_cast<FixedBytesType const&>(*m_dataType).numBytes()))
|
<< (u256(0x1) << (256 - 8 * dynamic_cast<FixedBytesType const&>(*m_dataType).numBytes()))
|
||||||
<< eth::Instruction::SWAP1 << eth::Instruction::DIV;
|
<< Instruction::SWAP1 << Instruction::DIV;
|
||||||
else if (
|
else if (
|
||||||
m_dataType->category() == Type::Category::Integer &&
|
m_dataType->category() == Type::Category::Integer &&
|
||||||
dynamic_cast<IntegerType const&>(*m_dataType).isSigned()
|
dynamic_cast<IntegerType const&>(*m_dataType).isSigned()
|
||||||
@ -235,15 +235,15 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
|
|||||||
// remove the higher order bits
|
// remove the higher order bits
|
||||||
m_context
|
m_context
|
||||||
<< (u256(1) << (8 * (32 - m_dataType->storageBytes())))
|
<< (u256(1) << (8 * (32 - m_dataType->storageBytes())))
|
||||||
<< eth::Instruction::SWAP1
|
<< Instruction::SWAP1
|
||||||
<< eth::Instruction::DUP2
|
<< Instruction::DUP2
|
||||||
<< eth::Instruction::MUL
|
<< Instruction::MUL
|
||||||
<< eth::Instruction::DIV;
|
<< Instruction::DIV;
|
||||||
m_context << eth::Instruction::MUL << eth::Instruction::OR;
|
m_context << Instruction::MUL << Instruction::OR;
|
||||||
// stack: value storage_ref updated_value
|
// stack: value storage_ref updated_value
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
|
m_context << Instruction::SWAP1 << Instruction::SSTORE;
|
||||||
if (_move)
|
if (_move)
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -253,19 +253,19 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
|
|||||||
"Wrong type conversation for assignment.");
|
"Wrong type conversation for assignment.");
|
||||||
if (m_dataType->category() == Type::Category::Array)
|
if (m_dataType->category() == Type::Category::Array)
|
||||||
{
|
{
|
||||||
m_context << eth::Instruction::POP; // remove byte offset
|
m_context << Instruction::POP; // remove byte offset
|
||||||
ArrayUtils(m_context).copyArrayToStorage(
|
ArrayUtils(m_context).copyArrayToStorage(
|
||||||
dynamic_cast<ArrayType const&>(*m_dataType),
|
dynamic_cast<ArrayType const&>(*m_dataType),
|
||||||
dynamic_cast<ArrayType const&>(_sourceType)
|
dynamic_cast<ArrayType const&>(_sourceType)
|
||||||
);
|
);
|
||||||
if (_move)
|
if (_move)
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
}
|
}
|
||||||
else if (m_dataType->category() == Type::Category::Struct)
|
else if (m_dataType->category() == Type::Category::Struct)
|
||||||
{
|
{
|
||||||
// stack layout: source_ref target_ref target_offset
|
// stack layout: source_ref target_ref target_offset
|
||||||
// note that we have structs, so offset should be zero and are ignored
|
// note that we have structs, so offset should be zero and are ignored
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
auto const& structType = dynamic_cast<StructType const&>(*m_dataType);
|
auto const& structType = dynamic_cast<StructType const&>(*m_dataType);
|
||||||
auto const& sourceType = dynamic_cast<StructType const&>(_sourceType);
|
auto const& sourceType = dynamic_cast<StructType const&>(_sourceType);
|
||||||
solAssert(
|
solAssert(
|
||||||
@ -284,7 +284,7 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
|
|||||||
{
|
{
|
||||||
// stack layout: source_ref target_ref
|
// stack layout: source_ref target_ref
|
||||||
pair<u256, unsigned> const& offsets = sourceType.storageOffsetsOfMember(member.name);
|
pair<u256, unsigned> const& offsets = sourceType.storageOffsetsOfMember(member.name);
|
||||||
m_context << offsets.first << eth::Instruction::DUP3 << eth::Instruction::ADD;
|
m_context << offsets.first << Instruction::DUP3 << Instruction::ADD;
|
||||||
m_context << u256(offsets.second);
|
m_context << u256(offsets.second);
|
||||||
// stack: source_ref target_ref source_member_ref source_member_off
|
// stack: source_ref target_ref source_member_ref source_member_off
|
||||||
StorageItem(m_context, *sourceMemberType).retrieveValue(_location, true);
|
StorageItem(m_context, *sourceMemberType).retrieveValue(_location, true);
|
||||||
@ -296,13 +296,13 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
|
|||||||
// stack layout: source_ref target_ref
|
// stack layout: source_ref target_ref
|
||||||
TypePointer sourceMemberType = sourceType.memberType(member.name);
|
TypePointer sourceMemberType = sourceType.memberType(member.name);
|
||||||
m_context << sourceType.memoryOffsetOfMember(member.name);
|
m_context << sourceType.memoryOffsetOfMember(member.name);
|
||||||
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
|
m_context << Instruction::DUP3 << Instruction::ADD;
|
||||||
MemoryItem(m_context, *sourceMemberType).retrieveValue(_location, true);
|
MemoryItem(m_context, *sourceMemberType).retrieveValue(_location, true);
|
||||||
// stack layout: source_ref target_ref source_value...
|
// stack layout: source_ref target_ref source_value...
|
||||||
}
|
}
|
||||||
unsigned stackSize = sourceMemberType->sizeOnStack();
|
unsigned stackSize = sourceMemberType->sizeOnStack();
|
||||||
pair<u256, unsigned> const& offsets = structType.storageOffsetsOfMember(member.name);
|
pair<u256, unsigned> const& offsets = structType.storageOffsetsOfMember(member.name);
|
||||||
m_context << eth::dupInstruction(1 + stackSize) << offsets.first << eth::Instruction::ADD;
|
m_context << dupInstruction(1 + stackSize) << offsets.first << Instruction::ADD;
|
||||||
m_context << u256(offsets.second);
|
m_context << u256(offsets.second);
|
||||||
// stack: source_ref target_ref target_off source_value... target_member_ref target_member_byte_off
|
// stack: source_ref target_ref target_off source_value... target_member_ref target_member_byte_off
|
||||||
StorageItem(m_context, *memberType).storeValue(*sourceMemberType, _location, true);
|
StorageItem(m_context, *memberType).storeValue(*sourceMemberType, _location, true);
|
||||||
@ -312,7 +312,7 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
|
|||||||
if (_move)
|
if (_move)
|
||||||
utils.popStackSlots(2);
|
utils.popStackSlots(2);
|
||||||
else
|
else
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
|
m_context << Instruction::SWAP1 << Instruction::POP;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
BOOST_THROW_EXCEPTION(
|
BOOST_THROW_EXCEPTION(
|
||||||
@ -344,12 +344,12 @@ void StorageItem::setToZero(SourceLocation const&, bool _removeReference) const
|
|||||||
continue;
|
continue;
|
||||||
pair<u256, unsigned> const& offsets = structType.storageOffsetsOfMember(member.name);
|
pair<u256, unsigned> const& offsets = structType.storageOffsetsOfMember(member.name);
|
||||||
m_context
|
m_context
|
||||||
<< offsets.first << eth::Instruction::DUP3 << eth::Instruction::ADD
|
<< offsets.first << Instruction::DUP3 << Instruction::ADD
|
||||||
<< u256(offsets.second);
|
<< u256(offsets.second);
|
||||||
StorageItem(m_context, *memberType).setToZero();
|
StorageItem(m_context, *memberType).setToZero();
|
||||||
}
|
}
|
||||||
if (_removeReference)
|
if (_removeReference)
|
||||||
m_context << eth::Instruction::POP << eth::Instruction::POP;
|
m_context << Instruction::POP << Instruction::POP;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -360,23 +360,23 @@ void StorageItem::setToZero(SourceLocation const&, bool _removeReference) const
|
|||||||
{
|
{
|
||||||
// offset should be zero
|
// offset should be zero
|
||||||
m_context
|
m_context
|
||||||
<< eth::Instruction::POP << u256(0)
|
<< Instruction::POP << u256(0)
|
||||||
<< eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
|
<< Instruction::SWAP1 << Instruction::SSTORE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_context << u256(0x100) << eth::Instruction::EXP;
|
m_context << u256(0x100) << Instruction::EXP;
|
||||||
// stack: storage_ref multiplier
|
// stack: storage_ref multiplier
|
||||||
// fetch old value
|
// fetch old value
|
||||||
m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD;
|
m_context << Instruction::DUP2 << Instruction::SLOAD;
|
||||||
// stack: storege_ref multiplier old_full_value
|
// stack: storege_ref multiplier old_full_value
|
||||||
// clear bytes in old value
|
// clear bytes in old value
|
||||||
m_context
|
m_context
|
||||||
<< eth::Instruction::SWAP1 << ((u256(1) << (8 * m_dataType->storageBytes())) - 1)
|
<< Instruction::SWAP1 << ((u256(1) << (8 * m_dataType->storageBytes())) - 1)
|
||||||
<< eth::Instruction::MUL;
|
<< Instruction::MUL;
|
||||||
m_context << eth::Instruction::NOT << eth::Instruction::AND;
|
m_context << Instruction::NOT << Instruction::AND;
|
||||||
// stack: storage_ref cleared_value
|
// stack: storage_ref cleared_value
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
|
m_context << Instruction::SWAP1 << Instruction::SSTORE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -393,48 +393,48 @@ void StorageByteArrayElement::retrieveValue(SourceLocation const&, bool _remove)
|
|||||||
{
|
{
|
||||||
// stack: ref byte_number
|
// stack: ref byte_number
|
||||||
if (_remove)
|
if (_remove)
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::SLOAD
|
m_context << Instruction::SWAP1 << Instruction::SLOAD
|
||||||
<< eth::Instruction::SWAP1 << eth::Instruction::BYTE;
|
<< Instruction::SWAP1 << Instruction::BYTE;
|
||||||
else
|
else
|
||||||
m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD
|
m_context << Instruction::DUP2 << Instruction::SLOAD
|
||||||
<< eth::Instruction::DUP2 << eth::Instruction::BYTE;
|
<< Instruction::DUP2 << Instruction::BYTE;
|
||||||
m_context << (u256(1) << (256 - 8)) << eth::Instruction::MUL;
|
m_context << (u256(1) << (256 - 8)) << Instruction::MUL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StorageByteArrayElement::storeValue(Type const&, SourceLocation const&, bool _move) const
|
void StorageByteArrayElement::storeValue(Type const&, SourceLocation const&, bool _move) const
|
||||||
{
|
{
|
||||||
// stack: value ref byte_number
|
// stack: value ref byte_number
|
||||||
m_context << u256(31) << eth::Instruction::SUB << u256(0x100) << eth::Instruction::EXP;
|
m_context << u256(31) << Instruction::SUB << u256(0x100) << Instruction::EXP;
|
||||||
// stack: value ref (1<<(8*(31-byte_number)))
|
// stack: value ref (1<<(8*(31-byte_number)))
|
||||||
m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD;
|
m_context << Instruction::DUP2 << Instruction::SLOAD;
|
||||||
// stack: value ref (1<<(8*(31-byte_number))) old_full_value
|
// stack: value ref (1<<(8*(31-byte_number))) old_full_value
|
||||||
// clear byte in old value
|
// clear byte in old value
|
||||||
m_context << eth::Instruction::DUP2 << u256(0xff) << eth::Instruction::MUL
|
m_context << Instruction::DUP2 << u256(0xff) << Instruction::MUL
|
||||||
<< eth::Instruction::NOT << eth::Instruction::AND;
|
<< Instruction::NOT << Instruction::AND;
|
||||||
// stack: value ref (1<<(32-byte_number)) old_full_value_with_cleared_byte
|
// stack: value ref (1<<(32-byte_number)) old_full_value_with_cleared_byte
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
m_context << (u256(1) << (256 - 8)) << eth::Instruction::DUP5 << eth::Instruction::DIV
|
m_context << (u256(1) << (256 - 8)) << Instruction::DUP5 << Instruction::DIV
|
||||||
<< eth::Instruction::MUL << eth::Instruction::OR;
|
<< Instruction::MUL << Instruction::OR;
|
||||||
// stack: value ref new_full_value
|
// stack: value ref new_full_value
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
|
m_context << Instruction::SWAP1 << Instruction::SSTORE;
|
||||||
if (_move)
|
if (_move)
|
||||||
m_context << eth::Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StorageByteArrayElement::setToZero(SourceLocation const&, bool _removeReference) const
|
void StorageByteArrayElement::setToZero(SourceLocation const&, bool _removeReference) const
|
||||||
{
|
{
|
||||||
// stack: ref byte_number
|
// stack: ref byte_number
|
||||||
if (!_removeReference)
|
if (!_removeReference)
|
||||||
m_context << eth::Instruction::DUP2 << eth::Instruction::DUP2;
|
m_context << Instruction::DUP2 << Instruction::DUP2;
|
||||||
m_context << u256(31) << eth::Instruction::SUB << u256(0x100) << eth::Instruction::EXP;
|
m_context << u256(31) << Instruction::SUB << u256(0x100) << Instruction::EXP;
|
||||||
// stack: ref (1<<(8*(31-byte_number)))
|
// stack: ref (1<<(8*(31-byte_number)))
|
||||||
m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD;
|
m_context << Instruction::DUP2 << Instruction::SLOAD;
|
||||||
// stack: ref (1<<(8*(31-byte_number))) old_full_value
|
// stack: ref (1<<(8*(31-byte_number))) old_full_value
|
||||||
// clear byte in old value
|
// clear byte in old value
|
||||||
m_context << eth::Instruction::SWAP1 << u256(0xff) << eth::Instruction::MUL;
|
m_context << Instruction::SWAP1 << u256(0xff) << Instruction::MUL;
|
||||||
m_context << eth::Instruction::NOT << eth::Instruction::AND;
|
m_context << Instruction::NOT << Instruction::AND;
|
||||||
// stack: ref old_full_value_with_cleared_byte
|
// stack: ref old_full_value_with_cleared_byte
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
|
m_context << Instruction::SWAP1 << Instruction::SSTORE;
|
||||||
}
|
}
|
||||||
|
|
||||||
StorageArrayLength::StorageArrayLength(CompilerContext& _compilerContext, const ArrayType& _arrayType):
|
StorageArrayLength::StorageArrayLength(CompilerContext& _compilerContext, const ArrayType& _arrayType):
|
||||||
@ -448,22 +448,22 @@ void StorageArrayLength::retrieveValue(SourceLocation const&, bool _remove) cons
|
|||||||
{
|
{
|
||||||
ArrayUtils(m_context).retrieveLength(m_arrayType);
|
ArrayUtils(m_context).retrieveLength(m_arrayType);
|
||||||
if (_remove)
|
if (_remove)
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
|
m_context << Instruction::SWAP1 << Instruction::POP;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StorageArrayLength::storeValue(Type const&, SourceLocation const&, bool _move) const
|
void StorageArrayLength::storeValue(Type const&, SourceLocation const&, bool _move) const
|
||||||
{
|
{
|
||||||
if (_move)
|
if (_move)
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
else
|
else
|
||||||
m_context << eth::Instruction::DUP2;
|
m_context << Instruction::DUP2;
|
||||||
ArrayUtils(m_context).resizeDynamicArray(m_arrayType);
|
ArrayUtils(m_context).resizeDynamicArray(m_arrayType);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StorageArrayLength::setToZero(SourceLocation const&, bool _removeReference) const
|
void StorageArrayLength::setToZero(SourceLocation const&, bool _removeReference) const
|
||||||
{
|
{
|
||||||
if (!_removeReference)
|
if (!_removeReference)
|
||||||
m_context << eth::Instruction::DUP1;
|
m_context << Instruction::DUP1;
|
||||||
ArrayUtils(m_context).clearDynamicArray(m_arrayType);
|
ArrayUtils(m_context).clearDynamicArray(m_arrayType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <libevmasm/Assembly.h>
|
#include <libevmasm/Assembly.h>
|
||||||
#include <libevmasm/SourceLocation.h>
|
#include <libevmasm/SourceLocation.h>
|
||||||
|
#include <libevmasm/Instruction.h>
|
||||||
#include <libsolidity/inlineasm/AsmParser.h>
|
#include <libsolidity/inlineasm/AsmParser.h>
|
||||||
#include <libsolidity/inlineasm/AsmData.h>
|
#include <libsolidity/inlineasm/AsmData.h>
|
||||||
|
|
||||||
@ -116,7 +117,7 @@ public:
|
|||||||
m_identifierAccess = [](assembly::Identifier const&, eth::Assembly&, CodeGenerator::IdentifierContext) { return false; };
|
m_identifierAccess = [](assembly::Identifier const&, eth::Assembly&, CodeGenerator::IdentifierContext) { return false; };
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(Instruction const& _instruction)
|
void operator()(dev::solidity::assembly::Instruction const& _instruction)
|
||||||
{
|
{
|
||||||
m_state.assembly.append(_instruction.instruction);
|
m_state.assembly.append(_instruction.instruction);
|
||||||
}
|
}
|
||||||
@ -145,7 +146,7 @@ public:
|
|||||||
"Variable inaccessible, too deep inside stack (" + boost::lexical_cast<string>(heightDiff) + ")"
|
"Variable inaccessible, too deep inside stack (" + boost::lexical_cast<string>(heightDiff) + ")"
|
||||||
);
|
);
|
||||||
else
|
else
|
||||||
m_state.assembly.append(eth::dupInstruction(heightDiff));
|
m_state.assembly.append(solidity::dupInstruction(heightDiff));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (eth::AssemblyItem const* label = m_state.findLabel(_identifier.name))
|
else if (eth::AssemblyItem const* label = m_state.findLabel(_identifier.name))
|
||||||
@ -196,7 +197,7 @@ public:
|
|||||||
//@TODO check height before and after
|
//@TODO check height before and after
|
||||||
while (m_state.variables.size() > numVariables)
|
while (m_state.variables.size() > numVariables)
|
||||||
{
|
{
|
||||||
m_state.assembly.append(eth::Instruction::POP);
|
m_state.assembly.append(solidity::Instruction::POP);
|
||||||
m_state.variables.pop_back();
|
m_state.variables.pop_back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -215,8 +216,8 @@ private:
|
|||||||
);
|
);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_state.assembly.append(eth::swapInstruction(heightDiff));
|
m_state.assembly.append(solidity::swapInstruction(heightDiff));
|
||||||
m_state.assembly.append(eth::Instruction::POP);
|
m_state.assembly.append(solidity::Instruction::POP);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <boost/variant.hpp>
|
#include <boost/variant.hpp>
|
||||||
#include <libevmcore/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
@ -35,7 +35,7 @@ namespace assembly
|
|||||||
/// What follows are the AST nodes for assembly.
|
/// What follows are the AST nodes for assembly.
|
||||||
|
|
||||||
/// Direct EVM instruction (except PUSHi and JUMPDEST)
|
/// Direct EVM instruction (except PUSHi and JUMPDEST)
|
||||||
struct Instruction { eth::Instruction instruction; };
|
struct Instruction { solidity::Instruction instruction; };
|
||||||
/// Literal number or string (up to 32 bytes)
|
/// Literal number or string (up to 32 bytes)
|
||||||
struct Literal { bool isNumber; std::string value; };
|
struct Literal { bool isNumber; std::string value; };
|
||||||
/// External / internal identifier or label reference
|
/// External / internal identifier or label reference
|
||||||
|
@ -23,7 +23,6 @@
|
|||||||
#include <libsolidity/inlineasm/AsmParser.h>
|
#include <libsolidity/inlineasm/AsmParser.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <libevmcore/Instruction.h>
|
|
||||||
#include <libsolidity/parsing/Scanner.h>
|
#include <libsolidity/parsing/Scanner.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -121,17 +120,17 @@ assembly::Statement Parser::parseExpression()
|
|||||||
assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
|
assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
|
||||||
{
|
{
|
||||||
// Allowed instructions, lowercase names.
|
// Allowed instructions, lowercase names.
|
||||||
static map<string, eth::Instruction> s_instructions;
|
static map<string, dev::solidity::Instruction> s_instructions;
|
||||||
if (s_instructions.empty())
|
if (s_instructions.empty())
|
||||||
for (auto const& instruction: eth::c_instructions)
|
for (auto const& instruction: solidity::c_instructions)
|
||||||
{
|
{
|
||||||
if (
|
if (
|
||||||
instruction.second == eth::Instruction::JUMPDEST ||
|
instruction.second == solidity::Instruction::JUMPDEST ||
|
||||||
(eth::Instruction::PUSH1 <= instruction.second && instruction.second <= eth::Instruction::PUSH32)
|
(solidity::Instruction::PUSH1 <= instruction.second && instruction.second <= solidity::Instruction::PUSH32)
|
||||||
)
|
)
|
||||||
continue;
|
continue;
|
||||||
string name = instruction.first;
|
string name = instruction.first;
|
||||||
if (instruction.second == eth::Instruction::SUICIDE)
|
if (instruction.second == solidity::Instruction::SUICIDE)
|
||||||
name = "selfdestruct";
|
name = "selfdestruct";
|
||||||
transform(name.begin(), name.end(), name.begin(), [](unsigned char _c) { return tolower(_c); });
|
transform(name.begin(), name.end(), name.begin(), [](unsigned char _c) { return tolower(_c); });
|
||||||
s_instructions[name] = instruction.second;
|
s_instructions[name] = instruction.second;
|
||||||
@ -152,10 +151,10 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
|
|||||||
// first search the set of instructions.
|
// first search the set of instructions.
|
||||||
if (s_instructions.count(literal))
|
if (s_instructions.count(literal))
|
||||||
{
|
{
|
||||||
eth::Instruction const& instr = s_instructions[literal];
|
dev::solidity::Instruction const& instr = s_instructions[literal];
|
||||||
if (_onlySinglePusher)
|
if (_onlySinglePusher)
|
||||||
{
|
{
|
||||||
eth::InstructionInfo info = eth::instructionInfo(instr);
|
InstructionInfo info = dev::solidity::instructionInfo(instr);
|
||||||
if (info.ret != 1)
|
if (info.ret != 1)
|
||||||
fatalParserError("Instruction " + info.name + " not allowed in this context.");
|
fatalParserError("Instruction " + info.name + " not allowed in this context.");
|
||||||
}
|
}
|
||||||
@ -200,11 +199,11 @@ FunctionalInstruction Parser::parseFunctionalInstruction(assembly::Statement con
|
|||||||
{
|
{
|
||||||
if (_instruction.type() != typeid(Instruction))
|
if (_instruction.type() != typeid(Instruction))
|
||||||
fatalParserError("Assembly instruction required in front of \"(\")");
|
fatalParserError("Assembly instruction required in front of \"(\")");
|
||||||
eth::Instruction instr = boost::get<Instruction>(_instruction).instruction;
|
solidity::Instruction instr = boost::get<solidity::assembly::Instruction>(_instruction).instruction;
|
||||||
eth::InstructionInfo instrInfo = eth::instructionInfo(instr);
|
InstructionInfo instrInfo = instructionInfo(instr);
|
||||||
if (eth::Instruction::DUP1 <= instr && instr <= eth::Instruction::DUP16)
|
if (solidity::Instruction::DUP1 <= instr && instr <= solidity::Instruction::DUP16)
|
||||||
fatalParserError("DUPi instructions not allowed for functional notation");
|
fatalParserError("DUPi instructions not allowed for functional notation");
|
||||||
if (eth::Instruction::SWAP1 <= instr && instr <= eth::Instruction::SWAP16)
|
if (solidity::Instruction::SWAP1 <= instr && instr <= solidity::Instruction::SWAP16)
|
||||||
fatalParserError("SWAPi instructions not allowed for functional notation");
|
fatalParserError("SWAPi instructions not allowed for functional notation");
|
||||||
|
|
||||||
expectToken(Token::LParen);
|
expectToken(Token::LParen);
|
||||||
|
@ -137,8 +137,8 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation(
|
|||||||
using Id = ExpressionClasses::Id;
|
using Id = ExpressionClasses::Id;
|
||||||
using Ids = vector<Id>;
|
using Ids = vector<Id>;
|
||||||
Id hashValue = classes.find(u256(FixedHash<4>::Arith(FixedHash<4>(dev::sha3(_signature)))));
|
Id hashValue = classes.find(u256(FixedHash<4>::Arith(FixedHash<4>(dev::sha3(_signature)))));
|
||||||
Id calldata = classes.find(eth::Instruction::CALLDATALOAD, Ids{classes.find(u256(0))});
|
Id calldata = classes.find(Instruction::CALLDATALOAD, Ids{classes.find(u256(0))});
|
||||||
classes.forceEqual(hashValue, eth::Instruction::DIV, Ids{
|
classes.forceEqual(hashValue, Instruction::DIV, Ids{
|
||||||
calldata,
|
calldata,
|
||||||
classes.find(u256(1) << (8 * 28))
|
classes.find(u256(1) << (8 * 28))
|
||||||
});
|
});
|
||||||
@ -165,7 +165,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation(
|
|||||||
AssemblyItem invalidTag(PushTag, u256(-0x10));
|
AssemblyItem invalidTag(PushTag, u256(-0x10));
|
||||||
state->feedItem(invalidTag, true);
|
state->feedItem(invalidTag, true);
|
||||||
if (parametersSize > 0)
|
if (parametersSize > 0)
|
||||||
state->feedItem(eth::swapInstruction(parametersSize));
|
state->feedItem(swapInstruction(parametersSize));
|
||||||
|
|
||||||
return PathGasMeter(_items).estimateMax(_offset, state);
|
return PathGasMeter(_items).estimateMax(_offset, state);
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ file(GLOB HEADERS "*.h")
|
|||||||
include_directories(BEFORE ..)
|
include_directories(BEFORE ..)
|
||||||
add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
|
add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
|
||||||
|
|
||||||
eth_use(${EXECUTABLE} REQUIRED Solidity::lll Dev::buildinfo)
|
eth_use(${EXECUTABLE} REQUIRED Solidity::lll Dev::buildinfo Solidity::evmasm)
|
||||||
|
|
||||||
install( TARGETS ${EXECUTABLE} DESTINATION bin )
|
install( TARGETS ${EXECUTABLE} DESTINATION bin )
|
||||||
|
|
||||||
|
@ -25,10 +25,11 @@
|
|||||||
#include <liblll/Compiler.h>
|
#include <liblll/Compiler.h>
|
||||||
#include <libdevcore/CommonIO.h>
|
#include <libdevcore/CommonIO.h>
|
||||||
#include <libdevcore/CommonData.h>
|
#include <libdevcore/CommonData.h>
|
||||||
#include <libevmcore/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
#include "ethereum/BuildInfo.h"
|
#include "ethereum/BuildInfo.h"
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace dev;
|
||||||
|
using namespace dev::solidity;
|
||||||
using namespace dev::eth;
|
using namespace dev::eth;
|
||||||
|
|
||||||
void help()
|
void help()
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
#include <libdevcore/Common.h>
|
#include <libdevcore/Common.h>
|
||||||
#include <libdevcore/CommonData.h>
|
#include <libdevcore/CommonData.h>
|
||||||
#include <libdevcore/CommonIO.h>
|
#include <libdevcore/CommonIO.h>
|
||||||
#include <libevmcore/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
#include <libsolidity/interface/Version.h>
|
#include <libsolidity/interface/Version.h>
|
||||||
#include <libsolidity/parsing/Scanner.h>
|
#include <libsolidity/parsing/Scanner.h>
|
||||||
#include <libsolidity/parsing/Parser.h>
|
#include <libsolidity/parsing/Parser.h>
|
||||||
@ -161,11 +161,11 @@ void CommandLineInterface::handleBinary(string const& _contract)
|
|||||||
void CommandLineInterface::handleOpcode(string const& _contract)
|
void CommandLineInterface::handleOpcode(string const& _contract)
|
||||||
{
|
{
|
||||||
if (m_args.count("output-dir"))
|
if (m_args.count("output-dir"))
|
||||||
createFile(_contract + ".opcode", eth::disassemble(m_compiler->object(_contract).bytecode));
|
createFile(_contract + ".opcode", solidity::disassemble(m_compiler->object(_contract).bytecode));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cout << "Opcodes: " << endl;
|
cout << "Opcodes: " << endl;
|
||||||
cout << eth::disassemble(m_compiler->object(_contract).bytecode);
|
cout << solidity::disassemble(m_compiler->object(_contract).bytecode);
|
||||||
cout << endl;
|
cout << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -674,7 +674,7 @@ void CommandLineInterface::handleCombinedJSON()
|
|||||||
if (requests.count("clone-bin"))
|
if (requests.count("clone-bin"))
|
||||||
contractData["clone-bin"] = m_compiler->cloneObject(contractName).toHex();
|
contractData["clone-bin"] = m_compiler->cloneObject(contractName).toHex();
|
||||||
if (requests.count("opcodes"))
|
if (requests.count("opcodes"))
|
||||||
contractData["opcodes"] = eth::disassemble(m_compiler->object(contractName).bytecode);
|
contractData["opcodes"] = solidity::disassemble(m_compiler->object(contractName).bytecode);
|
||||||
if (requests.count("asm"))
|
if (requests.count("asm"))
|
||||||
{
|
{
|
||||||
ostringstream unused;
|
ostringstream unused;
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
#include <libdevcore/Common.h>
|
#include <libdevcore/Common.h>
|
||||||
#include <libdevcore/CommonData.h>
|
#include <libdevcore/CommonData.h>
|
||||||
#include <libdevcore/CommonIO.h>
|
#include <libdevcore/CommonIO.h>
|
||||||
#include <libevmcore/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
#include <libsolidity/parsing/Scanner.h>
|
#include <libsolidity/parsing/Scanner.h>
|
||||||
#include <libsolidity/parsing/Parser.h>
|
#include <libsolidity/parsing/Parser.h>
|
||||||
#include <libsolidity/ast/ASTPrinter.h>
|
#include <libsolidity/ast/ASTPrinter.h>
|
||||||
@ -206,7 +206,7 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback
|
|||||||
contractData["interface"] = compiler.interface(contractName);
|
contractData["interface"] = compiler.interface(contractName);
|
||||||
contractData["bytecode"] = compiler.object(contractName).toHex();
|
contractData["bytecode"] = compiler.object(contractName).toHex();
|
||||||
contractData["runtimeBytecode"] = compiler.runtimeObject(contractName).toHex();
|
contractData["runtimeBytecode"] = compiler.runtimeObject(contractName).toHex();
|
||||||
contractData["opcodes"] = eth::disassemble(compiler.object(contractName).bytecode);
|
contractData["opcodes"] = solidity::disassemble(compiler.object(contractName).bytecode);
|
||||||
contractData["functionHashes"] = functionHashes(compiler.contractDefinition(contractName));
|
contractData["functionHashes"] = functionHashes(compiler.contractDefinition(contractName));
|
||||||
contractData["gasEstimates"] = estimateGas(compiler, contractName);
|
contractData["gasEstimates"] = estimateGas(compiler, contractName);
|
||||||
ostringstream unused;
|
ostringstream unused;
|
||||||
|
@ -174,7 +174,7 @@ BOOST_AUTO_TEST_CASE(literal_true)
|
|||||||
"}\n";
|
"}\n";
|
||||||
bytes code = compileFirstExpression(sourceCode);
|
bytes code = compileFirstExpression(sourceCode);
|
||||||
|
|
||||||
bytes expectation({byte(eth::Instruction::PUSH1), 0x1});
|
bytes expectation({byte(Instruction::PUSH1), 0x1});
|
||||||
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,7 +185,7 @@ BOOST_AUTO_TEST_CASE(literal_false)
|
|||||||
"}\n";
|
"}\n";
|
||||||
bytes code = compileFirstExpression(sourceCode);
|
bytes code = compileFirstExpression(sourceCode);
|
||||||
|
|
||||||
bytes expectation({byte(eth::Instruction::PUSH1), 0x0});
|
bytes expectation({byte(Instruction::PUSH1), 0x0});
|
||||||
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,7 +196,7 @@ BOOST_AUTO_TEST_CASE(int_literal)
|
|||||||
"}\n";
|
"}\n";
|
||||||
bytes code = compileFirstExpression(sourceCode);
|
bytes code = compileFirstExpression(sourceCode);
|
||||||
|
|
||||||
bytes expectation({byte(eth::Instruction::PUSH10), 0x12, 0x34, 0x56, 0x78, 0x90,
|
bytes expectation({byte(Instruction::PUSH10), 0x12, 0x34, 0x56, 0x78, 0x90,
|
||||||
0x12, 0x34, 0x56, 0x78, 0x90});
|
0x12, 0x34, 0x56, 0x78, 0x90});
|
||||||
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
||||||
}
|
}
|
||||||
@ -212,7 +212,7 @@ BOOST_AUTO_TEST_CASE(int_with_wei_ether_subdenomination)
|
|||||||
})";
|
})";
|
||||||
bytes code = compileFirstExpression(sourceCode);
|
bytes code = compileFirstExpression(sourceCode);
|
||||||
|
|
||||||
bytes expectation({byte(eth::Instruction::PUSH1), 0x1});
|
bytes expectation({byte(Instruction::PUSH1), 0x1});
|
||||||
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,7 +227,7 @@ BOOST_AUTO_TEST_CASE(int_with_szabo_ether_subdenomination)
|
|||||||
})";
|
})";
|
||||||
bytes code = compileFirstExpression(sourceCode);
|
bytes code = compileFirstExpression(sourceCode);
|
||||||
|
|
||||||
bytes expectation({byte(eth::Instruction::PUSH5), 0xe8, 0xd4, 0xa5, 0x10, 0x00});
|
bytes expectation({byte(Instruction::PUSH5), 0xe8, 0xd4, 0xa5, 0x10, 0x00});
|
||||||
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,7 +242,7 @@ BOOST_AUTO_TEST_CASE(int_with_finney_ether_subdenomination)
|
|||||||
})";
|
})";
|
||||||
bytes code = compileFirstExpression(sourceCode);
|
bytes code = compileFirstExpression(sourceCode);
|
||||||
|
|
||||||
bytes expectation({byte(eth::Instruction::PUSH7), 0x3, 0x8d, 0x7e, 0xa4, 0xc6, 0x80, 0x00});
|
bytes expectation({byte(Instruction::PUSH7), 0x3, 0x8d, 0x7e, 0xa4, 0xc6, 0x80, 0x00});
|
||||||
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,7 +257,7 @@ BOOST_AUTO_TEST_CASE(int_with_ether_ether_subdenomination)
|
|||||||
})";
|
})";
|
||||||
bytes code = compileFirstExpression(sourceCode);
|
bytes code = compileFirstExpression(sourceCode);
|
||||||
|
|
||||||
bytes expectation({byte(eth::Instruction::PUSH8), 0xd, 0xe0, 0xb6, 0xb3, 0xa7, 0x64, 0x00, 0x00});
|
bytes expectation({byte(Instruction::PUSH8), 0xd, 0xe0, 0xb6, 0xb3, 0xa7, 0x64, 0x00, 0x00});
|
||||||
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,12 +268,12 @@ BOOST_AUTO_TEST_CASE(comparison)
|
|||||||
"}\n";
|
"}\n";
|
||||||
bytes code = compileFirstExpression(sourceCode);
|
bytes code = compileFirstExpression(sourceCode);
|
||||||
|
|
||||||
bytes expectation({byte(eth::Instruction::PUSH1), 0x1,
|
bytes expectation({byte(Instruction::PUSH1), 0x1,
|
||||||
byte(eth::Instruction::PUSH2), 0x11, 0xaa,
|
byte(Instruction::PUSH2), 0x11, 0xaa,
|
||||||
byte(eth::Instruction::PUSH2), 0x10, 0xaa,
|
byte(Instruction::PUSH2), 0x10, 0xaa,
|
||||||
byte(eth::Instruction::LT),
|
byte(Instruction::LT),
|
||||||
byte(eth::Instruction::EQ),
|
byte(Instruction::EQ),
|
||||||
byte(eth::Instruction::ISZERO)});
|
byte(Instruction::ISZERO)});
|
||||||
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,22 +284,22 @@ BOOST_AUTO_TEST_CASE(short_circuiting)
|
|||||||
"}\n";
|
"}\n";
|
||||||
bytes code = compileFirstExpression(sourceCode);
|
bytes code = compileFirstExpression(sourceCode);
|
||||||
|
|
||||||
bytes expectation({byte(eth::Instruction::PUSH1), 0x12, // 8 + 10
|
bytes expectation({byte(Instruction::PUSH1), 0x12, // 8 + 10
|
||||||
byte(eth::Instruction::PUSH1), 0x4,
|
byte(Instruction::PUSH1), 0x4,
|
||||||
byte(eth::Instruction::GT),
|
byte(Instruction::GT),
|
||||||
byte(eth::Instruction::ISZERO), // after this we have 4 <= 8 + 10
|
byte(Instruction::ISZERO), // after this we have 4 <= 8 + 10
|
||||||
byte(eth::Instruction::DUP1),
|
byte(Instruction::DUP1),
|
||||||
byte(eth::Instruction::PUSH1), 0x11,
|
byte(Instruction::PUSH1), 0x11,
|
||||||
byte(eth::Instruction::JUMPI), // short-circuit if it is true
|
byte(Instruction::JUMPI), // short-circuit if it is true
|
||||||
byte(eth::Instruction::POP),
|
byte(Instruction::POP),
|
||||||
byte(eth::Instruction::PUSH1), 0x2,
|
byte(Instruction::PUSH1), 0x2,
|
||||||
byte(eth::Instruction::PUSH1), 0x9,
|
byte(Instruction::PUSH1), 0x9,
|
||||||
byte(eth::Instruction::EQ),
|
byte(Instruction::EQ),
|
||||||
byte(eth::Instruction::ISZERO), // after this we have 9 != 2
|
byte(Instruction::ISZERO), // after this we have 9 != 2
|
||||||
byte(eth::Instruction::JUMPDEST),
|
byte(Instruction::JUMPDEST),
|
||||||
byte(eth::Instruction::PUSH1), 0x1,
|
byte(Instruction::PUSH1), 0x1,
|
||||||
byte(eth::Instruction::EQ),
|
byte(Instruction::EQ),
|
||||||
byte(eth::Instruction::ISZERO)});
|
byte(Instruction::ISZERO)});
|
||||||
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,23 +309,23 @@ BOOST_AUTO_TEST_CASE(arithmetics)
|
|||||||
" function f(uint y) { var x = ((((((((y ^ 8) & 7) | 6) - 5) + 4) % 3) / 2) * 1); }"
|
" function f(uint y) { var x = ((((((((y ^ 8) & 7) | 6) - 5) + 4) % 3) / 2) * 1); }"
|
||||||
"}\n";
|
"}\n";
|
||||||
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}, {"test", "f", "x"}});
|
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}, {"test", "f", "x"}});
|
||||||
bytes expectation({byte(eth::Instruction::PUSH1), 0x1,
|
bytes expectation({byte(Instruction::PUSH1), 0x1,
|
||||||
byte(eth::Instruction::PUSH1), 0x2,
|
byte(Instruction::PUSH1), 0x2,
|
||||||
byte(eth::Instruction::PUSH1), 0x3,
|
byte(Instruction::PUSH1), 0x3,
|
||||||
byte(eth::Instruction::PUSH1), 0x4,
|
byte(Instruction::PUSH1), 0x4,
|
||||||
byte(eth::Instruction::PUSH1), 0x5,
|
byte(Instruction::PUSH1), 0x5,
|
||||||
byte(eth::Instruction::PUSH1), 0x6,
|
byte(Instruction::PUSH1), 0x6,
|
||||||
byte(eth::Instruction::PUSH1), 0x7,
|
byte(Instruction::PUSH1), 0x7,
|
||||||
byte(eth::Instruction::PUSH1), 0x8,
|
byte(Instruction::PUSH1), 0x8,
|
||||||
byte(eth::Instruction::DUP10),
|
byte(Instruction::DUP10),
|
||||||
byte(eth::Instruction::XOR),
|
byte(Instruction::XOR),
|
||||||
byte(eth::Instruction::AND),
|
byte(Instruction::AND),
|
||||||
byte(eth::Instruction::OR),
|
byte(Instruction::OR),
|
||||||
byte(eth::Instruction::SUB),
|
byte(Instruction::SUB),
|
||||||
byte(eth::Instruction::ADD),
|
byte(Instruction::ADD),
|
||||||
byte(eth::Instruction::MOD),
|
byte(Instruction::MOD),
|
||||||
byte(eth::Instruction::DIV),
|
byte(Instruction::DIV),
|
||||||
byte(eth::Instruction::MUL)});
|
byte(Instruction::MUL)});
|
||||||
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,13 +336,13 @@ BOOST_AUTO_TEST_CASE(unary_operators)
|
|||||||
"}\n";
|
"}\n";
|
||||||
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}, {"test", "f", "x"}});
|
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}, {"test", "f", "x"}});
|
||||||
|
|
||||||
bytes expectation({byte(eth::Instruction::PUSH1), 0x2,
|
bytes expectation({byte(Instruction::PUSH1), 0x2,
|
||||||
byte(eth::Instruction::DUP3),
|
byte(Instruction::DUP3),
|
||||||
byte(eth::Instruction::PUSH1), 0x0,
|
byte(Instruction::PUSH1), 0x0,
|
||||||
byte(eth::Instruction::SUB),
|
byte(Instruction::SUB),
|
||||||
byte(eth::Instruction::NOT),
|
byte(Instruction::NOT),
|
||||||
byte(eth::Instruction::EQ),
|
byte(Instruction::EQ),
|
||||||
byte(eth::Instruction::ISZERO)});
|
byte(Instruction::ISZERO)});
|
||||||
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,44 +354,44 @@ BOOST_AUTO_TEST_CASE(unary_inc_dec)
|
|||||||
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "x"}});
|
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "x"}});
|
||||||
|
|
||||||
// Stack: a, x
|
// Stack: a, x
|
||||||
bytes expectation({byte(eth::Instruction::DUP2),
|
bytes expectation({byte(Instruction::DUP2),
|
||||||
byte(eth::Instruction::DUP1),
|
byte(Instruction::DUP1),
|
||||||
byte(eth::Instruction::PUSH1), 0x1,
|
byte(Instruction::PUSH1), 0x1,
|
||||||
byte(eth::Instruction::ADD),
|
byte(Instruction::ADD),
|
||||||
// Stack here: a x a (a+1)
|
// Stack here: a x a (a+1)
|
||||||
byte(eth::Instruction::SWAP3),
|
byte(Instruction::SWAP3),
|
||||||
byte(eth::Instruction::POP), // first ++
|
byte(Instruction::POP), // first ++
|
||||||
// Stack here: (a+1) x a
|
// Stack here: (a+1) x a
|
||||||
byte(eth::Instruction::DUP3),
|
byte(Instruction::DUP3),
|
||||||
byte(eth::Instruction::PUSH1), 0x1,
|
byte(Instruction::PUSH1), 0x1,
|
||||||
byte(eth::Instruction::ADD),
|
byte(Instruction::ADD),
|
||||||
// Stack here: (a+1) x a (a+2)
|
// Stack here: (a+1) x a (a+2)
|
||||||
byte(eth::Instruction::SWAP3),
|
byte(Instruction::SWAP3),
|
||||||
byte(eth::Instruction::POP),
|
byte(Instruction::POP),
|
||||||
// Stack here: (a+2) x a
|
// Stack here: (a+2) x a
|
||||||
byte(eth::Instruction::DUP3), // second ++
|
byte(Instruction::DUP3), // second ++
|
||||||
byte(eth::Instruction::XOR),
|
byte(Instruction::XOR),
|
||||||
// Stack here: (a+2) x a^(a+2)
|
// Stack here: (a+2) x a^(a+2)
|
||||||
byte(eth::Instruction::DUP3),
|
byte(Instruction::DUP3),
|
||||||
byte(eth::Instruction::DUP1),
|
byte(Instruction::DUP1),
|
||||||
byte(eth::Instruction::PUSH1), 0x1,
|
byte(Instruction::PUSH1), 0x1,
|
||||||
byte(eth::Instruction::SWAP1),
|
byte(Instruction::SWAP1),
|
||||||
byte(eth::Instruction::SUB),
|
byte(Instruction::SUB),
|
||||||
// Stack here: (a+2) x a^(a+2) (a+2) (a+1)
|
// Stack here: (a+2) x a^(a+2) (a+2) (a+1)
|
||||||
byte(eth::Instruction::SWAP4),
|
byte(Instruction::SWAP4),
|
||||||
byte(eth::Instruction::POP), // first --
|
byte(Instruction::POP), // first --
|
||||||
byte(eth::Instruction::XOR),
|
byte(Instruction::XOR),
|
||||||
// Stack here: (a+1) x a^(a+2)^(a+2)
|
// Stack here: (a+1) x a^(a+2)^(a+2)
|
||||||
byte(eth::Instruction::DUP3),
|
byte(Instruction::DUP3),
|
||||||
byte(eth::Instruction::PUSH1), 0x1,
|
byte(Instruction::PUSH1), 0x1,
|
||||||
byte(eth::Instruction::SWAP1),
|
byte(Instruction::SWAP1),
|
||||||
byte(eth::Instruction::SUB),
|
byte(Instruction::SUB),
|
||||||
// Stack here: (a+1) x a^(a+2)^(a+2) a
|
// Stack here: (a+1) x a^(a+2)^(a+2) a
|
||||||
byte(eth::Instruction::SWAP3),
|
byte(Instruction::SWAP3),
|
||||||
byte(eth::Instruction::POP), // second ++
|
byte(Instruction::POP), // second ++
|
||||||
// Stack here: a x a^(a+2)^(a+2)
|
// Stack here: a x a^(a+2)^(a+2)
|
||||||
byte(eth::Instruction::DUP3), // will change
|
byte(Instruction::DUP3), // will change
|
||||||
byte(eth::Instruction::XOR)});
|
byte(Instruction::XOR)});
|
||||||
// Stack here: a x a^(a+2)^(a+2)^a
|
// Stack here: a x a^(a+2)^(a+2)^a
|
||||||
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
||||||
}
|
}
|
||||||
@ -404,16 +404,16 @@ BOOST_AUTO_TEST_CASE(assignment)
|
|||||||
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "b"}});
|
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "b"}});
|
||||||
|
|
||||||
// Stack: a, b
|
// Stack: a, b
|
||||||
bytes expectation({byte(eth::Instruction::PUSH1), 0x2,
|
bytes expectation({byte(Instruction::PUSH1), 0x2,
|
||||||
byte(eth::Instruction::DUP2),
|
byte(Instruction::DUP2),
|
||||||
byte(eth::Instruction::DUP4),
|
byte(Instruction::DUP4),
|
||||||
byte(eth::Instruction::ADD),
|
byte(Instruction::ADD),
|
||||||
// Stack here: a b 2 a+b
|
// Stack here: a b 2 a+b
|
||||||
byte(eth::Instruction::SWAP3),
|
byte(Instruction::SWAP3),
|
||||||
byte(eth::Instruction::POP),
|
byte(Instruction::POP),
|
||||||
byte(eth::Instruction::DUP3),
|
byte(Instruction::DUP3),
|
||||||
// Stack here: a+b b 2 a+b
|
// Stack here: a+b b 2 a+b
|
||||||
byte(eth::Instruction::MUL)});
|
byte(Instruction::MUL)});
|
||||||
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -427,26 +427,26 @@ BOOST_AUTO_TEST_CASE(function_call)
|
|||||||
{{"test", "f", "a"}, {"test", "f", "b"}});
|
{{"test", "f", "a"}, {"test", "f", "b"}});
|
||||||
|
|
||||||
// Stack: a, b
|
// Stack: a, b
|
||||||
bytes expectation({byte(eth::Instruction::PUSH1), 0x02,
|
bytes expectation({byte(Instruction::PUSH1), 0x02,
|
||||||
byte(eth::Instruction::PUSH1), 0x0c,
|
byte(Instruction::PUSH1), 0x0c,
|
||||||
byte(eth::Instruction::PUSH1), 0x01,
|
byte(Instruction::PUSH1), 0x01,
|
||||||
byte(eth::Instruction::DUP5),
|
byte(Instruction::DUP5),
|
||||||
byte(eth::Instruction::ADD),
|
byte(Instruction::ADD),
|
||||||
// Stack here: a b 2 <ret label> (a+1)
|
// Stack here: a b 2 <ret label> (a+1)
|
||||||
byte(eth::Instruction::DUP4),
|
byte(Instruction::DUP4),
|
||||||
byte(eth::Instruction::PUSH1), 0x13,
|
byte(Instruction::PUSH1), 0x13,
|
||||||
byte(eth::Instruction::JUMP),
|
byte(Instruction::JUMP),
|
||||||
byte(eth::Instruction::JUMPDEST),
|
byte(Instruction::JUMPDEST),
|
||||||
// Stack here: a b 2 g(a+1, b)
|
// Stack here: a b 2 g(a+1, b)
|
||||||
byte(eth::Instruction::MUL),
|
byte(Instruction::MUL),
|
||||||
// Stack here: a b g(a+1, b)*2
|
// Stack here: a b g(a+1, b)*2
|
||||||
byte(eth::Instruction::DUP3),
|
byte(Instruction::DUP3),
|
||||||
byte(eth::Instruction::ADD),
|
byte(Instruction::ADD),
|
||||||
// Stack here: a b a+g(a+1, b)*2
|
// Stack here: a b a+g(a+1, b)*2
|
||||||
byte(eth::Instruction::SWAP2),
|
byte(Instruction::SWAP2),
|
||||||
byte(eth::Instruction::POP),
|
byte(Instruction::POP),
|
||||||
byte(eth::Instruction::DUP2),
|
byte(Instruction::DUP2),
|
||||||
byte(eth::Instruction::JUMPDEST)});
|
byte(Instruction::JUMPDEST)});
|
||||||
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,7 +457,7 @@ BOOST_AUTO_TEST_CASE(negative_literals_8bits)
|
|||||||
"}\n";
|
"}\n";
|
||||||
bytes code = compileFirstExpression(sourceCode);
|
bytes code = compileFirstExpression(sourceCode);
|
||||||
|
|
||||||
bytes expectation(bytes({byte(eth::Instruction::PUSH32)}) + bytes(31, 0xff) + bytes(1, 0x80));
|
bytes expectation(bytes({byte(Instruction::PUSH32)}) + bytes(31, 0xff) + bytes(1, 0x80));
|
||||||
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -468,7 +468,7 @@ BOOST_AUTO_TEST_CASE(negative_literals_16bits)
|
|||||||
"}\n";
|
"}\n";
|
||||||
bytes code = compileFirstExpression(sourceCode);
|
bytes code = compileFirstExpression(sourceCode);
|
||||||
|
|
||||||
bytes expectation(bytes({byte(eth::Instruction::PUSH32)}) + bytes(30, 0xff) + bytes{0xf5, 0x43});
|
bytes expectation(bytes({byte(Instruction::PUSH32)}) + bytes(30, 0xff) + bytes{0xf5, 0x43});
|
||||||
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -481,7 +481,7 @@ BOOST_AUTO_TEST_CASE(intermediately_overflowing_literals)
|
|||||||
"}\n";
|
"}\n";
|
||||||
bytes code = compileFirstExpression(sourceCode);
|
bytes code = compileFirstExpression(sourceCode);
|
||||||
|
|
||||||
bytes expectation(bytes({byte(eth::Instruction::PUSH1), 0xbf}));
|
bytes expectation(bytes({byte(Instruction::PUSH1), 0xbf}));
|
||||||
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -495,8 +495,8 @@ BOOST_AUTO_TEST_CASE(blockhash)
|
|||||||
bytes code = compileFirstExpression(sourceCode, {}, {},
|
bytes code = compileFirstExpression(sourceCode, {}, {},
|
||||||
{make_shared<MagicVariableDeclaration>("block", make_shared<MagicType>(MagicType::Kind::Block))});
|
{make_shared<MagicVariableDeclaration>("block", make_shared<MagicType>(MagicType::Kind::Block))});
|
||||||
|
|
||||||
bytes expectation({byte(eth::Instruction::PUSH1), 0x03,
|
bytes expectation({byte(Instruction::PUSH1), 0x03,
|
||||||
byte(eth::Instruction::BLOCKHASH)});
|
byte(Instruction::BLOCKHASH)});
|
||||||
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,11 +58,11 @@ public:
|
|||||||
m_optimize = true;
|
m_optimize = true;
|
||||||
bytes optimizedBytecode = compileAndRun(_sourceCode, _value, _contractName);
|
bytes optimizedBytecode = compileAndRun(_sourceCode, _value, _contractName);
|
||||||
size_t nonOptimizedSize = 0;
|
size_t nonOptimizedSize = 0;
|
||||||
eth::eachInstruction(nonOptimizedBytecode, [&](Instruction, u256 const&) {
|
solidity::eachInstruction(nonOptimizedBytecode, [&](Instruction, u256 const&) {
|
||||||
nonOptimizedSize++;
|
nonOptimizedSize++;
|
||||||
});
|
});
|
||||||
size_t optimizedSize = 0;
|
size_t optimizedSize = 0;
|
||||||
eth::eachInstruction(optimizedBytecode, [&](Instruction, u256 const&) {
|
solidity::eachInstruction(optimizedBytecode, [&](Instruction, u256 const&) {
|
||||||
optimizedSize++;
|
optimizedSize++;
|
||||||
});
|
});
|
||||||
BOOST_CHECK_MESSAGE(
|
BOOST_CHECK_MESSAGE(
|
||||||
@ -308,8 +308,8 @@ BOOST_AUTO_TEST_CASE(retain_information_in_branches)
|
|||||||
m_optimize = true;
|
m_optimize = true;
|
||||||
bytes optimizedBytecode = compileAndRun(sourceCode, 0, "c");
|
bytes optimizedBytecode = compileAndRun(sourceCode, 0, "c");
|
||||||
size_t numSHA3s = 0;
|
size_t numSHA3s = 0;
|
||||||
eth::eachInstruction(optimizedBytecode, [&](Instruction _instr, u256 const&) {
|
eachInstruction(optimizedBytecode, [&](Instruction _instr, u256 const&) {
|
||||||
if (_instr == eth::Instruction::SHA3)
|
if (_instr == Instruction::SHA3)
|
||||||
numSHA3s++;
|
numSHA3s++;
|
||||||
});
|
});
|
||||||
BOOST_CHECK_EQUAL(1, numSHA3s);
|
BOOST_CHECK_EQUAL(1, numSHA3s);
|
||||||
@ -351,8 +351,8 @@ BOOST_AUTO_TEST_CASE(store_tags_as_unions)
|
|||||||
m_optimize = true;
|
m_optimize = true;
|
||||||
bytes optimizedBytecode = compileAndRun(sourceCode, 0, "test");
|
bytes optimizedBytecode = compileAndRun(sourceCode, 0, "test");
|
||||||
size_t numSHA3s = 0;
|
size_t numSHA3s = 0;
|
||||||
eth::eachInstruction(optimizedBytecode, [&](Instruction _instr, u256 const&) {
|
eachInstruction(optimizedBytecode, [&](Instruction _instr, u256 const&) {
|
||||||
if (_instr == eth::Instruction::SHA3)
|
if (_instr == Instruction::SHA3)
|
||||||
numSHA3s++;
|
numSHA3s++;
|
||||||
});
|
});
|
||||||
// TEST DISABLED UNTIL 93693404 IS IMPLEMENTED
|
// TEST DISABLED UNTIL 93693404 IS IMPLEMENTED
|
||||||
@ -1071,16 +1071,16 @@ BOOST_AUTO_TEST_CASE(block_deduplicator)
|
|||||||
AssemblyItem(PushTag, 1),
|
AssemblyItem(PushTag, 1),
|
||||||
AssemblyItem(PushTag, 3),
|
AssemblyItem(PushTag, 3),
|
||||||
u256(6),
|
u256(6),
|
||||||
eth::Instruction::SWAP3,
|
Instruction::SWAP3,
|
||||||
eth::Instruction::JUMP,
|
Instruction::JUMP,
|
||||||
AssemblyItem(Tag, 1),
|
AssemblyItem(Tag, 1),
|
||||||
u256(6),
|
u256(6),
|
||||||
eth::Instruction::SWAP3,
|
Instruction::SWAP3,
|
||||||
eth::Instruction::JUMP,
|
Instruction::JUMP,
|
||||||
AssemblyItem(Tag, 2),
|
AssemblyItem(Tag, 2),
|
||||||
u256(6),
|
u256(6),
|
||||||
eth::Instruction::SWAP3,
|
Instruction::SWAP3,
|
||||||
eth::Instruction::JUMP,
|
Instruction::JUMP,
|
||||||
AssemblyItem(Tag, 3)
|
AssemblyItem(Tag, 3)
|
||||||
};
|
};
|
||||||
BlockDeduplicator dedup(input);
|
BlockDeduplicator dedup(input);
|
||||||
@ -1097,23 +1097,23 @@ BOOST_AUTO_TEST_CASE(block_deduplicator_loops)
|
|||||||
{
|
{
|
||||||
AssemblyItems input{
|
AssemblyItems input{
|
||||||
u256(0),
|
u256(0),
|
||||||
eth::Instruction::SLOAD,
|
Instruction::SLOAD,
|
||||||
AssemblyItem(PushTag, 1),
|
AssemblyItem(PushTag, 1),
|
||||||
AssemblyItem(PushTag, 2),
|
AssemblyItem(PushTag, 2),
|
||||||
eth::Instruction::JUMPI,
|
Instruction::JUMPI,
|
||||||
eth::Instruction::JUMP,
|
Instruction::JUMP,
|
||||||
AssemblyItem(Tag, 1),
|
AssemblyItem(Tag, 1),
|
||||||
u256(5),
|
u256(5),
|
||||||
u256(6),
|
u256(6),
|
||||||
eth::Instruction::SSTORE,
|
Instruction::SSTORE,
|
||||||
AssemblyItem(PushTag, 1),
|
AssemblyItem(PushTag, 1),
|
||||||
eth::Instruction::JUMP,
|
Instruction::JUMP,
|
||||||
AssemblyItem(Tag, 2),
|
AssemblyItem(Tag, 2),
|
||||||
u256(5),
|
u256(5),
|
||||||
u256(6),
|
u256(6),
|
||||||
eth::Instruction::SSTORE,
|
Instruction::SSTORE,
|
||||||
AssemblyItem(PushTag, 2),
|
AssemblyItem(PushTag, 2),
|
||||||
eth::Instruction::JUMP,
|
Instruction::JUMP,
|
||||||
};
|
};
|
||||||
BlockDeduplicator dedup(input);
|
BlockDeduplicator dedup(input);
|
||||||
dedup.deduplicate();
|
dedup.deduplicate();
|
||||||
|
Loading…
Reference in New Issue
Block a user