aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian <c@ethdev.com>2014-10-31 01:15:25 +0800
committerChristian <c@ethdev.com>2014-10-31 04:21:25 +0800
commitcb9cb48dc78240717cd4842e75c9314778ebcb10 (patch)
tree1da710c3f44a0b6d2c7735b23a9da006ad016ec3
parenta5f360273896973a99ad696a427726da875d0473 (diff)
downloaddexon-solidity-cb9cb48dc78240717cd4842e75c9314778ebcb10.tar
dexon-solidity-cb9cb48dc78240717cd4842e75c9314778ebcb10.tar.gz
dexon-solidity-cb9cb48dc78240717cd4842e75c9314778ebcb10.tar.bz2
dexon-solidity-cb9cb48dc78240717cd4842e75c9314778ebcb10.tar.lz
dexon-solidity-cb9cb48dc78240717cd4842e75c9314778ebcb10.tar.xz
dexon-solidity-cb9cb48dc78240717cd4842e75c9314778ebcb10.tar.zst
dexon-solidity-cb9cb48dc78240717cd4842e75c9314778ebcb10.zip
Function selector and variable (un)packing.
-rw-r--r--Compiler.cpp100
-rw-r--r--Compiler.h16
-rw-r--r--CompilerContext.h4
-rw-r--r--Types.h8
4 files changed, 117 insertions, 11 deletions
diff --git a/Compiler.cpp b/Compiler.cpp
index fea88560..7e40db15 100644
--- a/Compiler.cpp
+++ b/Compiler.cpp
@@ -51,16 +51,106 @@ void Compiler::compileContract(ContractDefinition& _contract)
function->accept(*this);
}
-void Compiler::appendFunctionSelector(std::vector<ASTPointer<FunctionDefinition>> const&)
+void Compiler::appendFunctionSelector(vector<ASTPointer<FunctionDefinition>> const& _functions)
{
- // filter public functions, and sort by name. Then select function from first byte,
- // unpack arguments from calldata, push to stack and jump. Pack return values to
- // output and return.
+ // sort all public functions and store them together with a tag for their argument decoding section
+ map<string, pair<FunctionDefinition const*, eth::AssemblyItem>> publicFunctions;
+ for (ASTPointer<FunctionDefinition> const& f: _functions)
+ if (f->isPublic())
+ publicFunctions.insert(make_pair(f->getName(), make_pair(f.get(), m_context.newTag())));
+
+ //@todo remove constructor
+
+ if (publicFunctions.size() > 255)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("More than 255 public functions for contract."));
+
+ //@todo check for calldatasize?
+ // retrieve the first byte of the call data
+ m_context << u256(0) << eth::Instruction::CALLDATALOAD << u256(0) << eth::Instruction::BYTE;
+ // check that it is not too large
+ m_context << eth::Instruction::DUP1 << u256(publicFunctions.size() - 1) << eth::Instruction::LT;
+ eth::AssemblyItem returnTag = m_context.appendConditionalJump();
+
+ // otherwise, jump inside jump table (each entry of the table has size 4)
+ m_context << u256(4) << eth::Instruction::MUL;
+ eth::AssemblyItem jumpTableStart = m_context.pushNewTag();
+ m_context << eth::Instruction::ADD << eth::Instruction::JUMP;
+
+ // jump table @todo it could be that the optimizer destroys this
+ m_context << jumpTableStart;
+ for (pair<string, pair<FunctionDefinition const*, eth::AssemblyItem>> const& f: publicFunctions)
+ m_context.appendJumpTo(f.second.second) << eth::Instruction::JUMPDEST;
+
+ m_context << returnTag << eth::Instruction::RETURN;
+
+ for (pair<string, pair<FunctionDefinition const*, eth::AssemblyItem>> const& f: publicFunctions)
+ {
+ m_context << f.second.second;
+ appendFunctionCallSection(*f.second.first);
+ }
+}
+
+void Compiler::appendFunctionCallSection(FunctionDefinition const& _function)
+{
+ eth::AssemblyItem returnTag = m_context.pushNewTag();
+
+ appendCalldataUnpacker(_function);
+
+ m_context.appendJumpTo(m_context.getFunctionEntryLabel(_function));
+ m_context << returnTag;
+
+ appendReturnValuePacker(_function);
+}
+
+void Compiler::appendCalldataUnpacker(FunctionDefinition const& _function)
+{
+ // We do not check the calldata size, everything is zero-padded.
+ unsigned dataOffset = 1;
+
+ //@todo this can be done more efficiently, saving some CALLDATALOAD calls
+ for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
+ {
+ unsigned const numBytes = var->getType()->getCalldataEncodedSize();
+ if (numBytes == 0)
+ BOOST_THROW_EXCEPTION(CompilerError()
+ << errinfo_sourceLocation(var->getLocation())
+ << errinfo_comment("Type not yet supported."));
+ if (numBytes == 32)
+ m_context << u256(dataOffset) << eth::Instruction::CALLDATALOAD;
+ else
+ m_context << (u256(1) << ((32 - numBytes) * 8)) << u256(dataOffset)
+ << eth::Instruction::CALLDATALOAD << eth::Instruction::DIV;
+ dataOffset += numBytes;
+ }
+}
+
+void Compiler::appendReturnValuePacker(FunctionDefinition const& _function)
+{
+ //@todo this can be also done more efficiently
+ unsigned dataOffset = 0;
+ vector<ASTPointer<VariableDeclaration>> const& parameters = _function.getReturnParameters();
+ for (unsigned i = 0 ; i < parameters.size(); ++i)
+ {
+ unsigned numBytes = parameters[i]->getType()->getCalldataEncodedSize();
+ if (numBytes == 0)
+ BOOST_THROW_EXCEPTION(CompilerError()
+ << errinfo_sourceLocation(parameters[i]->getLocation())
+ << errinfo_comment("Type not yet supported."));
+ m_context << eth::dupInstruction(parameters.size() - i);
+ if (numBytes == 32)
+ m_context << u256(dataOffset) << eth::Instruction::MSTORE;
+ else
+ m_context << u256(dataOffset) << (u256(1) << ((32 - numBytes) * 8))
+ << eth::Instruction::MUL << eth::Instruction::MSTORE;
+ dataOffset += numBytes;
+ }
+ // note that the stack is not cleaned up here
+ m_context << u256(dataOffset) << u256(0) << eth::Instruction::RETURN;
}
bool Compiler::visit(FunctionDefinition& _function)
{
- //@todo to simplify this, the colling convention could by changed such that
+ //@todo to simplify this, the calling convention could by changed such that
// caller puts: [retarg0] ... [retargm] [return address] [arg0] ... [argn]
// although note that this reduces the size of the visible stack
diff --git a/Compiler.h b/Compiler.h
index 5f6d9b4e..ebd78665 100644
--- a/Compiler.h
+++ b/Compiler.h
@@ -20,6 +20,7 @@
* Solidity AST to EVM bytecode compiler.
*/
+#include <ostream>
#include <libsolidity/ASTVisitor.h>
#include <libsolidity/CompilerContext.h>
@@ -29,14 +30,20 @@ namespace solidity {
class Compiler: private ASTVisitor
{
public:
+ Compiler(): m_returnTag(m_context.newTag()) {}
+
+ void compileContract(ContractDefinition& _contract);
+ bytes getAssembledBytecode() { return m_context.getAssembledBytecode(); }
+ void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); }
+
/// Compile the given contract and return the EVM bytecode.
static bytes compile(ContractDefinition& _contract);
private:
- Compiler(): m_returnTag(m_context.newTag()) {}
-
- void compileContract(ContractDefinition& _contract);
- void appendFunctionSelector(const std::vector<ASTPointer<FunctionDefinition> >& _functions);
+ void appendFunctionSelector(std::vector<ASTPointer<FunctionDefinition> > const& _functions);
+ void appendFunctionCallSection(FunctionDefinition const& _function);
+ void appendCalldataUnpacker(FunctionDefinition const& _function);
+ void appendReturnValuePacker(FunctionDefinition const& _function);
virtual bool visit(FunctionDefinition& _function) override;
virtual bool visit(IfStatement& _ifStatement) override;
@@ -47,7 +54,6 @@ private:
virtual bool visit(VariableDefinition& _variableDefinition) override;
virtual bool visit(ExpressionStatement& _expressionStatement) override;
- bytes getAssembledBytecode() { return m_context.getAssembledBytecode(); }
CompilerContext m_context;
std::vector<eth::AssemblyItem> m_breakTags; ///< tag to jump to for a "break" statement
diff --git a/CompilerContext.h b/CompilerContext.h
index 90367903..cce5838e 100644
--- a/CompilerContext.h
+++ b/CompilerContext.h
@@ -22,6 +22,7 @@
#pragma once
+#include <ostream>
#include <libevmface/Instruction.h>
#include <liblll/Assembly.h>
#include <libsolidity/Types.h>
@@ -69,7 +70,8 @@ public:
CompilerContext& operator<<(u256 const& _value) { m_asm.append(_value); return *this; }
CompilerContext& operator<<(bytes const& _data) { m_asm.append(_data); return *this; }
- bytes getAssembledBytecode() { return m_asm.assemble(); }
+ void streamAssembly(std::ostream& _stream) const { _stream << m_asm; }
+ bytes getAssembledBytecode() const { return m_asm.assemble(); }
private:
eth::Assembly m_asm;
diff --git a/Types.h b/Types.h
index 828f4809..d7c3b241 100644
--- a/Types.h
+++ b/Types.h
@@ -68,6 +68,10 @@ public:
virtual bool operator==(Type const& _other) const { return getCategory() == _other.getCategory(); }
virtual bool operator!=(Type const& _other) const { return !this->operator ==(_other); }
+ /// @returns number of bytes used by this type when encoded for CALL, or 0 if the encoding
+ /// is not a simple big-endian encoding or the type cannot be stored on the stack.
+ virtual unsigned getCalldataEncodedSize() const { return 0; }
+
virtual std::string toString() const = 0;
virtual u256 literalValue(Literal const&) const { assert(false); }
};
@@ -93,6 +97,8 @@ public:
virtual bool operator==(Type const& _other) const override;
+ virtual unsigned getCalldataEncodedSize() const { return m_bits / 8; }
+
virtual std::string toString() const override;
virtual u256 literalValue(Literal const& _literal) const override;
@@ -121,6 +127,8 @@ public:
return _operator == Token::NOT || _operator == Token::DELETE;
}
+ virtual unsigned getCalldataEncodedSize() const { return 1; }
+
virtual std::string toString() const override { return "bool"; }
virtual u256 literalValue(Literal const& _literal) const override;
};