2014-10-30 00:20:32 +00:00
|
|
|
/*
|
|
|
|
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/>.
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* @author Christian <c@ethdev.com>
|
|
|
|
* @date 2014
|
|
|
|
* Utilities for the solidity compiler.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
2014-10-30 17:15:25 +00:00
|
|
|
#include <ostream>
|
2014-11-06 13:50:18 +00:00
|
|
|
#include <libevmcore/Instruction.h>
|
|
|
|
#include <libevmcore/Assembly.h>
|
2014-10-30 00:20:32 +00:00
|
|
|
#include <libsolidity/Types.h>
|
|
|
|
|
|
|
|
namespace dev {
|
|
|
|
namespace solidity {
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Context to be shared by all units that compile the same contract.
|
|
|
|
* It stores the generated bytecode and the position of identifiers in memory and on the stack.
|
|
|
|
*/
|
|
|
|
class CompilerContext
|
|
|
|
{
|
|
|
|
public:
|
2014-11-07 01:06:37 +00:00
|
|
|
CompilerContext(): m_stateVariablesSize(0) {}
|
2014-10-30 00:20:32 +00:00
|
|
|
|
2014-11-07 01:06:37 +00:00
|
|
|
void addStateVariable(VariableDeclaration const& _declaration);
|
2014-10-30 00:20:32 +00:00
|
|
|
void startNewFunction() { m_localVariables.clear(); m_asm.setDeposit(0); }
|
|
|
|
void initializeLocalVariables(unsigned _numVariables);
|
|
|
|
void addVariable(VariableDeclaration const& _declaration) { m_localVariables.push_back(&_declaration); }
|
|
|
|
void addFunction(FunctionDefinition const& _function) { m_functionEntryLabels.insert(std::make_pair(&_function, m_asm.newTag())); }
|
|
|
|
|
|
|
|
void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); }
|
|
|
|
|
2014-11-07 01:06:37 +00:00
|
|
|
bool isFunctionDefinition(Declaration const* _declaration) const { return m_functionEntryLabels.count(_declaration); }
|
|
|
|
bool isLocalVariable(Declaration const* _declaration) const;
|
|
|
|
bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration); }
|
|
|
|
|
|
|
|
eth::AssemblyItem getFunctionEntryLabel(FunctionDefinition const& _function) const;
|
|
|
|
/// Returns the distance of the given local variable from the top of the local variable stack.
|
|
|
|
unsigned getBaseStackOffsetOfVariable(Declaration const& _declaration) const;
|
|
|
|
/// If supplied by a value returned by @ref getBaseStackOffsetOfVariable(variable), returns
|
|
|
|
/// the distance of that variable from the current top of the stack.
|
|
|
|
unsigned baseToCurrentStackOffset(unsigned _baseOffset) const;
|
|
|
|
u256 getStorageLocationOfVariable(Declaration const& _declaration) const;
|
|
|
|
|
2014-10-30 00:20:32 +00:00
|
|
|
/// Appends a JUMPI instruction to a new tag and @returns the tag
|
|
|
|
eth::AssemblyItem appendConditionalJump() { return m_asm.appendJumpI().tag(); }
|
|
|
|
/// Appends a JUMPI instruction to @a _tag
|
|
|
|
CompilerContext& appendConditionalJumpTo(eth::AssemblyItem const& _tag) { m_asm.appendJumpI(_tag); return *this; }
|
|
|
|
/// Appends a JUMP to a new tag and @returns the tag
|
|
|
|
eth::AssemblyItem appendJump() { return m_asm.appendJump().tag(); }
|
|
|
|
/// Appends a JUMP to a specific tag
|
|
|
|
CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm.appendJump(_tag); return *this; }
|
|
|
|
/// Appends pushing of a new tag and @returns the new tag.
|
|
|
|
eth::AssemblyItem pushNewTag() { return m_asm.append(m_asm.newPushTag()).tag(); }
|
|
|
|
/// @returns a new tag without pushing any opcodes or data
|
|
|
|
eth::AssemblyItem newTag() { return m_asm.newTag(); }
|
2014-10-30 21:52:15 +00:00
|
|
|
/// Adds a subroutine to the code (in the data section) and pushes its size (via a tag)
|
|
|
|
/// on the stack. @returns the assembly item corresponding to the pushed subroutine, i.e. its offset.
|
|
|
|
eth::AssemblyItem addSubroutine(eth::Assembly const& _assembly) { return m_asm.appendSubSize(_assembly); }
|
2014-10-30 00:20:32 +00:00
|
|
|
|
|
|
|
/// 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::Instruction _instruction) { m_asm.append(_instruction); return *this; }
|
|
|
|
CompilerContext& operator<<(u256 const& _value) { m_asm.append(_value); return *this; }
|
|
|
|
CompilerContext& operator<<(bytes const& _data) { m_asm.append(_data); return *this; }
|
|
|
|
|
2014-10-30 21:52:15 +00:00
|
|
|
eth::Assembly const& getAssembly() const { return m_asm; }
|
2014-10-30 17:15:25 +00:00
|
|
|
void streamAssembly(std::ostream& _stream) const { _stream << m_asm; }
|
2014-11-06 15:30:50 +00:00
|
|
|
bytes getAssembledBytecode(bool _optimize = false) { return m_asm.optimise(_optimize).assemble(); }
|
2014-10-30 00:20:32 +00:00
|
|
|
private:
|
|
|
|
eth::Assembly m_asm;
|
|
|
|
|
2014-11-07 01:06:37 +00:00
|
|
|
/// Size of the state variables, offset of next variable to be added.
|
|
|
|
u256 m_stateVariablesSize;
|
|
|
|
/// Storage offsets of state variables
|
|
|
|
std::map<Declaration const*, u256> m_stateVariables;
|
2014-10-30 00:20:32 +00:00
|
|
|
/// Offsets of local variables on the stack.
|
|
|
|
std::vector<Declaration const*> m_localVariables;
|
|
|
|
/// Labels pointing to the entry points of funcitons.
|
2014-11-07 01:06:37 +00:00
|
|
|
std::map<Declaration const*, eth::AssemblyItem> m_functionEntryLabels;
|
2014-10-30 00:20:32 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|