aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ExpressionCompiler.cpp139
-rw-r--r--ExpressionCompiler.h7
-rw-r--r--Types.cpp33
-rw-r--r--Types.h9
4 files changed, 128 insertions, 60 deletions
diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp
index c3c7116e..352b0e6d 100644
--- a/ExpressionCompiler.cpp
+++ b/ExpressionCompiler.cpp
@@ -185,7 +185,9 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
if (asserts(arguments.size() == function.getParameterTypes().size()))
BOOST_THROW_EXCEPTION(InternalCompilerError());
- if (function.getLocation() == Location::INTERNAL)
+ switch (function.getLocation())
+ {
+ case Location::INTERNAL:
{
// Calling convention: Caller pushes return address and arguments
// Callee removes them and pushes return values
@@ -208,61 +210,90 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
// all others
for (unsigned i = 1; i < function.getReturnParameterTypes().size(); ++i)
m_context << eth::Instruction::POP;
+ break;
}
- else if (function.getLocation() == Location::EXTERNAL)
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("External function calls not implemented yet."));
- else
+ case Location::EXTERNAL:
{
- switch (function.getLocation())
- {
- case Location::SEND:
- m_context << u256(0) << u256(0) << u256(0) << u256(0);
- arguments.front()->accept(*this);
- //@todo might not be necessary
- appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
- _functionCall.getExpression().accept(*this);
- m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
- << eth::Instruction::CALL
- << eth::Instruction::POP;
- break;
- case Location::SUICIDE:
- arguments.front()->accept(*this);
- //@todo might not be necessary
- appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
- m_context << eth::Instruction::SUICIDE;
- break;
- case Location::SHA3:
- arguments.front()->accept(*this);
- appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
- // @todo move this once we actually use memory
- m_context << u256(0) << eth::Instruction::MSTORE << u256(32) << u256(0) << eth::Instruction::SHA3;
- break;
- case Location::ECRECOVER:
- case Location::SHA256:
- case Location::RIPEMD160:
+ unsigned dataOffset = 1; // reserve one byte for the function index
+ for (unsigned i = 0; i < arguments.size(); ++i)
{
- static const map<Location, u256> contractAddresses{{Location::ECRECOVER, 1},
- {Location::SHA256, 2},
- {Location::RIPEMD160, 3}};
- u256 contractAddress = contractAddresses.find(function.getLocation())->second;
- // @todo later, combine this code with external function call
- for (unsigned i = 0; i < arguments.size(); ++i)
- {
- arguments[i]->accept(*this);
- appendTypeConversion(*arguments[i]->getType(), *function.getParameterTypes()[i], true);
- // @todo move this once we actually use memory
- m_context << u256(i * 32) << eth::Instruction::MSTORE;
- }
- m_context << u256(32) << u256(0) << u256(arguments.size() * 32) << u256(0) << u256(0)
- << contractAddress << u256(500) //@todo determine actual gas requirement
- << eth::Instruction::CALL
- << eth::Instruction::POP
- << u256(0) << eth::Instruction::MLOAD;
- break;
+ arguments[i]->accept(*this);
+ Type const& type = *function.getParameterTypes()[i];
+ appendTypeConversion(*arguments[i]->getType(), type);
+ unsigned const numBytes = type.getCalldataEncodedSize();
+ if (numBytes == 0 || numBytes > 32)
+ BOOST_THROW_EXCEPTION(CompilerError()
+ << errinfo_sourceLocation(arguments[i]->getLocation())
+ << errinfo_comment("Type " + type.toString() + " not yet supported."));
+ if (numBytes != 32)
+ m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
+ m_context << u256(dataOffset) << eth::Instruction::MSTORE;
+ dataOffset += numBytes;
}
- default:
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Function not yet implemented."));
+ //@todo only return the first return value for now
+ unsigned retSize = function.getReturnParameterTypes().empty() ? 0
+ : function.getReturnParameterTypes().front()->getCalldataEncodedSize();
+ // CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top)
+ m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0) << u256(0);
+ _functionCall.getExpression().accept(*this); // pushes addr and function index
+ m_context << u256(0) << eth::Instruction::MSTORE8
+ << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
+ << eth::Instruction::CALL
+ << eth::Instruction::POP; // @todo do not ignore failure indicator
+ if (retSize == 32)
+ m_context << u256(0) << eth::Instruction::MLOAD;
+ else if (retSize > 0)
+ m_context << (u256(1) << ((32 - retSize) * 8))
+ << u256(0) << eth::Instruction::MLOAD << eth::Instruction::DIV;
+ break;
+ }
+ case Location::SEND:
+ m_context << u256(0) << u256(0) << u256(0) << u256(0);
+ arguments.front()->accept(*this);
+ //@todo might not be necessary
+ appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
+ _functionCall.getExpression().accept(*this);
+ m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
+ << eth::Instruction::CALL
+ << eth::Instruction::POP;
+ break;
+ case Location::SUICIDE:
+ arguments.front()->accept(*this);
+ //@todo might not be necessary
+ appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
+ m_context << eth::Instruction::SUICIDE;
+ break;
+ case Location::SHA3:
+ arguments.front()->accept(*this);
+ appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
+ // @todo move this once we actually use memory
+ m_context << u256(0) << eth::Instruction::MSTORE << u256(32) << u256(0) << eth::Instruction::SHA3;
+ break;
+ case Location::ECRECOVER:
+ case Location::SHA256:
+ case Location::RIPEMD160:
+ {
+ static const map<Location, u256> contractAddresses{{Location::ECRECOVER, 1},
+ {Location::SHA256, 2},
+ {Location::RIPEMD160, 3}};
+ u256 contractAddress = contractAddresses.find(function.getLocation())->second;
+ // @todo later, combine this code with external function call
+ for (unsigned i = 0; i < arguments.size(); ++i)
+ {
+ arguments[i]->accept(*this);
+ appendTypeConversion(*arguments[i]->getType(), *function.getParameterTypes()[i], true);
+ // @todo move this once we actually use memory
+ m_context << u256(i * 32) << eth::Instruction::MSTORE;
}
+ m_context << u256(32) << u256(0) << u256(arguments.size() * 32) << u256(0) << u256(0)
+ << contractAddress << u256(500) //@todo determine actual gas requirement
+ << eth::Instruction::CALL
+ << eth::Instruction::POP
+ << u256(0) << eth::Instruction::MLOAD;
+ break;
+ }
+ default:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid function type."));
}
}
return false;
@@ -289,9 +320,11 @@ void ExpressionCompiler::endVisit(MemberAccess& _memberAccess)
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer."));
break;
case Type::Category::CONTRACT:
- // call function
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Contract variables not yet implemented."));
+ {
+ ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.getExpression().getType());
+ m_context << type.getFunctionIndex(member);
break;
+ }
case Type::Category::MAGIC:
// we can ignore the kind of magic and only look at the name of the member
if (member == "coinbase")
diff --git a/ExpressionCompiler.h b/ExpressionCompiler.h
index 83d7cdc6..fbecbdc8 100644
--- a/ExpressionCompiler.h
+++ b/ExpressionCompiler.h
@@ -31,9 +31,10 @@ class AssemblyItem; // forward
}
namespace solidity {
-class CompilerContext; // forward
-class Type; // forward
-class IntegerType; // forward
+// forward declarations
+class CompilerContext;
+class Type;
+class IntegerType;
/**
* Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream
diff --git a/Types.cpp b/Types.cpp
index 7e07b116..3829016f 100644
--- a/Types.cpp
+++ b/Types.cpp
@@ -140,7 +140,7 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
- return _convertTo.getCategory() == getCategory();
+ return _convertTo.getCategory() == getCategory() || _convertTo.getCategory() == Category::CONTRACT;
}
bool IntegerType::acceptsBinaryOperator(Token::Value _operator) const
@@ -247,6 +247,31 @@ string ContractType::toString() const
return "contract " + m_contract.getName();
}
+MemberList const& ContractType::getMembers() const
+{
+ // We need to lazy-initialize it because of recursive references.
+ if (!m_members)
+ {
+ map<string, shared_ptr<Type const>> members;
+ for (FunctionDefinition const* function: m_contract.getInterfaceFunctions())
+ members[function->getName()] = make_shared<FunctionType>(*function, false);
+ m_members.reset(new MemberList(members));
+ }
+ return *m_members;
+}
+
+unsigned ContractType::getFunctionIndex(string const& _functionName) const
+{
+ unsigned index = 0;
+ for (FunctionDefinition const* function: m_contract.getInterfaceFunctions())
+ {
+ if (function->getName() == _functionName)
+ return index;
+ ++index;
+ }
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Index of non-existing contract function requested."));
+}
+
bool StructType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
@@ -302,7 +327,7 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage offset of non-existing member requested."));
}
-FunctionType::FunctionType(FunctionDefinition const& _function)
+FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal)
{
TypePointers params;
TypePointers retParams;
@@ -314,7 +339,7 @@ FunctionType::FunctionType(FunctionDefinition const& _function)
retParams.push_back(var->getType());
swap(params, m_parameterTypes);
swap(retParams, m_returnParameterTypes);
- m_location = Location::INTERNAL;
+ m_location = _isInternal ? Location::INTERNAL : Location::EXTERNAL;
}
bool FunctionType::operator==(Type const& _other) const
@@ -323,6 +348,8 @@ bool FunctionType::operator==(Type const& _other) const
return false;
FunctionType const& other = dynamic_cast<FunctionType const&>(_other);
+ if (m_location != other.m_location)
+ return false;
if (m_parameterTypes.size() != other.m_parameterTypes.size() ||
m_returnParameterTypes.size() != other.m_returnParameterTypes.size())
return false;
diff --git a/Types.h b/Types.h
index b655f9e0..8e2f4803 100644
--- a/Types.h
+++ b/Types.h
@@ -214,10 +214,17 @@ public:
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const override;
+ virtual bool isValueType() const override { return true; }
virtual std::string toString() const override;
+ virtual MemberList const& getMembers() const override;
+
+ unsigned getFunctionIndex(std::string const& _functionName) const;
+
private:
ContractDefinition const& m_contract;
+ /// List of member types, will be lazy-initialized because of recursive references.
+ mutable std::unique_ptr<MemberList> m_members;
};
/**
@@ -263,7 +270,7 @@ public:
enum class Location { INTERNAL, EXTERNAL, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160 };
virtual Category getCategory() const override { return Category::FUNCTION; }
- explicit FunctionType(FunctionDefinition const& _function);
+ explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes,
Location _location = Location::INTERNAL):
m_parameterTypes(_parameterTypes), m_returnParameterTypes(_returnParameterTypes),