aboutsummaryrefslogtreecommitdiffstats
path: root/ExpressionCompiler.cpp
diff options
context:
space:
mode:
authorGav Wood <i@gavwood.com>2015-01-16 03:03:35 +0800
committerGav Wood <i@gavwood.com>2015-01-16 03:03:35 +0800
commita9ed77c22a03271c93ab70b8fcd58363c5e78ab0 (patch)
tree4dbe1c48ff4948e8750071a7627506f3076c8b63 /ExpressionCompiler.cpp
parent911916beb7d8fd9c318d43fdf137727d364a95d2 (diff)
parentc6a47a4dc2e6b5a9fb03e8282b6b3ef108fdcbcb (diff)
downloaddexon-solidity-a9ed77c22a03271c93ab70b8fcd58363c5e78ab0.tar.gz
dexon-solidity-a9ed77c22a03271c93ab70b8fcd58363c5e78ab0.tar.zst
dexon-solidity-a9ed77c22a03271c93ab70b8fcd58363c5e78ab0.zip
Merge branch 'develop' of github.com:ethereum/cpp-ethereum into develop
Conflicts: libsolidity/Types.h
Diffstat (limited to 'ExpressionCompiler.cpp')
-rw-r--r--ExpressionCompiler.cpp206
1 files changed, 128 insertions, 78 deletions
diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp
index 29338b69..2b6e53cb 100644
--- a/ExpressionCompiler.cpp
+++ b/ExpressionCompiler.cpp
@@ -232,27 +232,71 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
}
case Location::EXTERNAL:
case Location::BARE:
+ _functionCall.getExpression().accept(*this);
+ appendExternalFunctionCall(function, arguments, function.getLocation() == Location::BARE);
+ break;
+ case Location::CREATION:
{
- FunctionCallOptions options;
- options.bare = function.getLocation() == Location::BARE;
- options.obtainAddress = [&]() { _functionCall.getExpression().accept(*this); };
- appendExternalFunctionCall(function, arguments, options);
+ _functionCall.getExpression().accept(*this);
+ solAssert(!function.gasSet(), "Gas limit set for contract creation.");
+ solAssert(function.getReturnParameterTypes().size() == 1, "");
+ ContractDefinition const& contract = dynamic_cast<ContractType const&>(
+ *function.getReturnParameterTypes().front()).getContractDefinition();
+ // copy the contract's code into memory
+ bytes const& bytecode = m_context.getCompiledContract(contract);
+ m_context << u256(bytecode.size());
+ //@todo could be done by actually appending the Assembly, but then we probably need to compile
+ // multiple times. Will revisit once external fuctions are inlined.
+ m_context.appendData(bytecode);
+ //@todo copy to memory position 0, shift as soon as we use memory
+ m_context << u256(0) << eth::Instruction::CODECOPY;
+
+ unsigned length = bytecode.size();
+ length += appendArgumentCopyToMemory(function.getParameterTypes(), arguments, length);
+ // size, offset, endowment
+ m_context << u256(length) << u256(0);
+ if (function.valueSet())
+ m_context << eth::dupInstruction(3);
+ else
+ m_context << u256(0);
+ m_context << eth::Instruction::CREATE;
+ if (function.valueSet())
+ m_context << eth::swapInstruction(1) << eth::Instruction::POP;
break;
}
- case Location::SEND:
+ case Location::SET_GAS:
{
- FunctionCallOptions options;
- options.bare = true;
- options.obtainAddress = [&]() { _functionCall.getExpression().accept(*this); };
- options.obtainValue = [&]()
- {
- arguments.front()->accept(*this);
- appendTypeConversion(*arguments.front()->getType(),
- *function.getParameterTypes().front(), true);
- };
- appendExternalFunctionCall(FunctionType(TypePointers{}, TypePointers{}, Location::EXTERNAL), {}, options);
+ // stack layout: contract_address function_id [gas] [value]
+ _functionCall.getExpression().accept(*this);
+ arguments.front()->accept(*this);
+ appendTypeConversion(*arguments.front()->getType(), IntegerType(256), true);
+ // Note that function is not the original function, but the ".gas" function.
+ // Its values of gasSet and valueSet is equal to the original function's though.
+ unsigned stackDepth = (function.gasSet() ? 1 : 0) + (function.valueSet() ? 1 : 0);
+ if (stackDepth > 0)
+ m_context << eth::swapInstruction(stackDepth);
+ if (function.gasSet())
+ m_context << eth::Instruction::POP;
break;
}
+ case Location::SET_VALUE:
+ // stack layout: contract_address function_id [gas] [value]
+ _functionCall.getExpression().accept(*this);
+ // Note that function is not the original function, but the ".value" function.
+ // Its values of gasSet and valueSet is equal to the original function's though.
+ if (function.valueSet())
+ m_context << eth::Instruction::POP;
+ arguments.front()->accept(*this);
+ break;
+ case Location::SEND:
+ _functionCall.getExpression().accept(*this);
+ m_context << u256(0); // 0 gas, we do not want to execute code
+ arguments.front()->accept(*this);
+ appendTypeConversion(*arguments.front()->getType(),
+ *function.getParameterTypes().front(), true);
+ appendExternalFunctionCall(FunctionType(TypePointers{}, TypePointers{},
+ Location::EXTERNAL, true, true), {}, true);
+ break;
case Location::SUICIDE:
arguments.front()->accept(*this);
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
@@ -296,11 +340,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
static const map<Location, u256> contractAddresses{{Location::ECRECOVER, 1},
{Location::SHA256, 2},
{Location::RIPEMD160, 3}};
- u256 contractAddress = contractAddresses.find(function.getLocation())->second;
- FunctionCallOptions options;
- options.bare = true;
- options.obtainAddress = [&]() { m_context << contractAddress; };
- appendExternalFunctionCall(function, arguments, options);
+ m_context << contractAddresses.find(function.getLocation())->second;
+ appendExternalFunctionCall(function, arguments, true);
break;
}
default:
@@ -310,40 +351,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
return false;
}
-bool ExpressionCompiler::visit(NewExpression const& _newExpression)
+bool ExpressionCompiler::visit(NewExpression const&)
{
- ContractType const* type = dynamic_cast<ContractType const*>(_newExpression.getType().get());
- solAssert(type, "");
- TypePointers const& types = type->getConstructorType()->getParameterTypes();
- vector<ASTPointer<Expression const>> arguments = _newExpression.getArguments();
- solAssert(arguments.size() == types.size(), "");
-
- // copy the contracts code into memory
- bytes const& bytecode = m_context.getCompiledContract(*_newExpression.getContract());
- m_context << u256(bytecode.size());
- //@todo could be done by actually appending the Assembly, but then we probably need to compile
- // multiple times. Will revisit once external fuctions are inlined.
- m_context.appendData(bytecode);
- //@todo copy to memory position 0, shift as soon as we use memory
- m_context << u256(0) << eth::Instruction::CODECOPY;
-
- unsigned dataOffset = bytecode.size();
- for (unsigned i = 0; i < arguments.size(); ++i)
- {
- arguments[i]->accept(*this);
- appendTypeConversion(*arguments[i]->getType(), *types[i], true);
- unsigned const c_numBytes = types[i]->getCalldataEncodedSize();
- if (c_numBytes > 32)
- BOOST_THROW_EXCEPTION(CompilerError()
- << errinfo_sourceLocation(arguments[i]->getLocation())
- << errinfo_comment("Type " + types[i]->toString() + " not yet supported."));
- bool const c_leftAligned = types[i]->getCategory() == Type::Category::STRING;
- bool const c_padToWords = true;
- dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, c_numBytes,
- c_leftAligned, c_padToWords);
- }
- // size, offset, endowment
- m_context << u256(dataOffset) << u256(0) << u256(0) << eth::Instruction::CREATE;
+ // code is created for the function call (CREATION) only
return false;
}
@@ -377,6 +387,10 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer."));
break;
+ case Type::Category::FUNCTION:
+ solAssert(!!_memberAccess.getExpression().getType()->getMemberType(member),
+ "Invalid member access to function.");
+ break;
case Type::Category::MAGIC:
// we can ignore the kind of magic and only look at the name of the member
if (member == "coinbase")
@@ -653,46 +667,60 @@ void ExpressionCompiler::appendHighBitsCleanup(IntegerType const& _typeOnStack)
void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functionType,
vector<ASTPointer<Expression const>> const& _arguments,
- FunctionCallOptions const& _options)
+ bool bare)
{
solAssert(_arguments.size() == _functionType.getParameterTypes().size(), "");
- _options.obtainAddress();
- if (!_options.bare)
- CompilerUtils(m_context).storeInMemory(0, CompilerUtils::dataStartOffset);
+ // Assumed stack content here:
+ // <stack top>
+ // value [if _functionType.valueSet()]
+ // gas [if _functionType.gasSet()]
+ // function identifier [unless bare]
+ // contract address
- unsigned dataOffset = _options.bare ? 0 : CompilerUtils::dataStartOffset; // reserve 4 bytes for the function's hash identifier
- for (unsigned i = 0; i < _arguments.size(); ++i)
+ unsigned gasValueSize = (_functionType.gasSet() ? 1 : 0) + (_functionType.valueSet() ? 1 : 0);
+
+ unsigned contractStackPos = m_context.currentToBaseStackOffset(1 + gasValueSize + (bare ? 0 : 1));
+ unsigned gasStackPos = m_context.currentToBaseStackOffset(gasValueSize);
+ unsigned valueStackPos = m_context.currentToBaseStackOffset(1);
+
+ if (!bare)
{
- _arguments[i]->accept(*this);
- Type const& type = *_functionType.getParameterTypes()[i];
- appendTypeConversion(*_arguments[i]->getType(), type, true);
- unsigned const c_numBytes = type.getCalldataEncodedSize();
- if (c_numBytes == 0 || c_numBytes > 32)
- BOOST_THROW_EXCEPTION(CompilerError()
- << errinfo_sourceLocation(_arguments[i]->getLocation())
- << errinfo_comment("Type " + type.toString() + " not yet supported."));
- bool const c_leftAligned = type.getCategory() == Type::Category::STRING;
- bool const c_padToWords = true;
- dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, c_numBytes,
- c_leftAligned, c_padToWords);
+ // copy function identifier
+ m_context << eth::dupInstruction(gasValueSize + 1);
+ CompilerUtils(m_context).storeInMemory(0, CompilerUtils::dataStartOffset);
}
+
+ // reserve space for the function identifier
+ unsigned dataOffset = bare ? 0 : CompilerUtils::dataStartOffset;
+ dataOffset += appendArgumentCopyToMemory(_functionType.getParameterTypes(), _arguments, dataOffset);
+
//@todo only return the first return value for now
Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr :
_functionType.getReturnParameterTypes().front().get();
unsigned retSize = firstType ? CompilerUtils::getPaddedSize(firstType->getCalldataEncodedSize()) : 0;
// CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top)
m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0);
- if (_options.obtainValue)
- _options.obtainValue();
+ if (_functionType.valueSet())
+ m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos));
else
m_context << u256(0);
- m_context << eth::dupInstruction(6); //copy contract address
+ m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(contractStackPos));
- m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
- << eth::Instruction::CALL
- << eth::Instruction::POP // @todo do not ignore failure indicator
- << eth::Instruction::POP; // pop contract address
+ if (_functionType.gasSet())
+ m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos));
+ else
+ // send all gas except for the 21 needed to execute "SUB" and "CALL"
+ m_context << u256(21) << eth::Instruction::GAS << eth::Instruction::SUB;
+ m_context << eth::Instruction::CALL
+ << eth::Instruction::POP; // @todo do not ignore failure indicator
+ if (_functionType.valueSet())
+ m_context << eth::Instruction::POP;
+ if (_functionType.gasSet())
+ m_context << eth::Instruction::POP;
+ if (!bare)
+ m_context << eth::Instruction::POP;
+ m_context << eth::Instruction::POP; // pop contract address
if (retSize > 0)
{
@@ -701,6 +729,28 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
}
}
+unsigned ExpressionCompiler::appendArgumentCopyToMemory(TypePointers const& _types,
+ vector<ASTPointer<Expression const>> const& _arguments,
+ unsigned _memoryOffset)
+{
+ unsigned length = 0;
+ for (unsigned i = 0; i < _arguments.size(); ++i)
+ {
+ _arguments[i]->accept(*this);
+ appendTypeConversion(*_arguments[i]->getType(), *_types[i], true);
+ unsigned const c_numBytes = _types[i]->getCalldataEncodedSize();
+ if (c_numBytes == 0 || c_numBytes > 32)
+ BOOST_THROW_EXCEPTION(CompilerError()
+ << errinfo_sourceLocation(_arguments[i]->getLocation())
+ << errinfo_comment("Type " + _types[i]->toString() + " not yet supported."));
+ bool const c_leftAligned = _types[i]->getCategory() == Type::Category::STRING;
+ bool const c_padToWords = true;
+ length += CompilerUtils(m_context).storeInMemory(_memoryOffset + length, c_numBytes,
+ c_leftAligned, c_padToWords);
+ }
+ return length;
+}
+
ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType,
unsigned _baseStackOffset):
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset),