diff options
author | Gav Wood <i@gavwood.com> | 2015-01-16 03:03:35 +0800 |
---|---|---|
committer | Gav Wood <i@gavwood.com> | 2015-01-16 03:03:35 +0800 |
commit | a9ed77c22a03271c93ab70b8fcd58363c5e78ab0 (patch) | |
tree | 4dbe1c48ff4948e8750071a7627506f3076c8b63 /ExpressionCompiler.cpp | |
parent | 911916beb7d8fd9c318d43fdf137727d364a95d2 (diff) | |
parent | c6a47a4dc2e6b5a9fb03e8282b6b3ef108fdcbcb (diff) | |
download | dexon-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.cpp | 206 |
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), |