aboutsummaryrefslogtreecommitdiffstats
path: root/Compiler.h
blob: 1401e12e3976764346ffcdb3687f174451f9e41b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/*
    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
 * Solidity AST to EVM bytecode compiler.
 */

#include <libevmface/Instruction.h>
#include <libsolidity/ASTVisitor.h>
#include <libsolidity/Types.h>
#include <libsolidity/Token.h>

namespace dev {
namespace solidity {

/**
 * A single item of compiled code that can be assembled to a single byte value in the final
 * bytecode. Its main purpose is to inject jump labels and label references into the opcode stream,
 * which can be resolved in the final step.
 */
class AssemblyItem
{
public:
    enum class Type
    {
        CODE,    //< m_data is opcode, m_label is empty.
        DATA,    //< m_data is actual data, m_label is empty
        LABEL,   //< m_data is JUMPDEST opcode, m_label is id of label
        LABELREF //< m_data is empty, m_label is id of label
    };

    explicit AssemblyItem(eth::Instruction _instruction) : m_type(Type::CODE), m_data(byte(_instruction)) {}
    explicit AssemblyItem(byte _data): m_type(Type::DATA), m_data(_data) {}

    /// Factory functions
    static AssemblyItem labelRef(uint32_t _label) { return AssemblyItem(Type::LABELREF, 0, _label); }
    static AssemblyItem label(uint32_t _label) { return AssemblyItem(Type::LABEL, byte(eth::Instruction::JUMPDEST), _label); }

    Type getType() const { return m_type; }
    byte getData() const { return m_data; }
    uint32_t getLabel() const { return m_label; }

private:
    AssemblyItem(Type _type, byte _data, uint32_t _label): m_type(_type), m_data(_data), m_label(_label) {}

    Type m_type;
    byte m_data; //< data to be written to the bytecode stream (or filled by a label if this is a LABELREF)
    uint32_t m_label; //< the id of a label either referenced or defined by this item
};

using AssemblyItems = std::vector<AssemblyItem>;


/**
 * Context to be shared by all units that compile the same contract. Its current usage only
 * concerns dispensing unique jump label IDs and storing their actual positions in the bytecode
 * stream.
 */
class CompilerContext
{
public:
    CompilerContext(): m_nextLabel(0) {}
    uint32_t dispenseNewLabel() { return m_nextLabel++; }
    void setLabelPosition(uint32_t _label, uint32_t _position);
    uint32_t getLabelPosition(uint32_t _label) const;

private:
    uint32_t m_nextLabel;

    std::map<uint32_t, uint32_t> m_labelPositions;
};

/**
 * Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream
 * of EVM instructions. It needs a compiler context that is the same for the whole compilation
 * unit.
 */
class ExpressionCompiler: public ASTVisitor
{
public:
    ExpressionCompiler(CompilerContext& _compilerContext): m_context(_compilerContext) {}

    /// Compile the given expression and (re-)populate the assembly item list.
    void compile(Expression& _expression);
    AssemblyItems const& getAssemblyItems() const { return m_assemblyItems; }
    bytes getAssembledBytecode() const;

    /// Compile the given expression and return the assembly items right away.
    static AssemblyItems compileExpression(CompilerContext& _context, Expression& _expression);

private:
    virtual void endVisit(Assignment& _assignment) override;
    virtual void endVisit(UnaryOperation& _unaryOperation) override;
    virtual bool visit(BinaryOperation& _binaryOperation) override;
    virtual void endVisit(FunctionCall& _functionCall) override;
    virtual void endVisit(MemberAccess& _memberAccess) override;
    virtual void endVisit(IndexAccess& _indexAccess) override;
    virtual void endVisit(Identifier& _identifier) override;
    virtual void endVisit(Literal& _literal) override;

    /// Appends code to remove dirty higher order bits in case of an implicit promotion to a wider type.
    void cleanHigherOrderBitsIfNeeded(Type const& _typeOnStack, Type const& _targetType);

    ///@{
    ///@name Append code for various operator types
    void appendAndOrOperatorCode(BinaryOperation& _binaryOperation);
    void appendCompareOperatorCode(Token::Value _operator, Type const& _type);
    void appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type);

    void appendArithmeticOperatorCode(Token::Value _operator, Type const& _type);
    void appendBitOperatorCode(Token::Value _operator);
    void appendShiftOperatorCode(Token::Value _operator);
    /// @}

    /// Appends a JUMPI instruction to a new label and returns the label
    uint32_t appendConditionalJump();

    /// Append elements to the current instruction list.
    void append(eth::Instruction const& _instruction) { m_assemblyItems.push_back(AssemblyItem(_instruction)); }
    void append(byte _value) { m_assemblyItems.push_back(AssemblyItem(_value)); }
    void append(bytes const& _data);
    void appendLabelref(byte _label) { m_assemblyItems.push_back(AssemblyItem::labelRef(_label)); }
    void appendLabel(byte _label) { m_assemblyItems.push_back(AssemblyItem::label(_label)); }

    AssemblyItems m_assemblyItems;
    CompilerContext& m_context;
};


}
}