diff options
-rw-r--r-- | ExpressionCompiler.cpp | 139 | ||||
-rw-r--r-- | ExpressionCompiler.h | 7 | ||||
-rw-r--r-- | Types.cpp | 33 | ||||
-rw-r--r-- | Types.h | 9 |
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 @@ -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; @@ -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), |