Browse Source

Merge pull request #2096 from chriseth/sol_constantFallback

Fallback takes constant amount of gas, and send no gas with send.
cl-refactor
chriseth 10 years ago
parent
commit
887cf3842d
  1. 16
      libsolidity/Compiler.cpp
  2. 39
      libsolidity/ExpressionCompiler.cpp
  3. 6
      libsolidity/Types.cpp
  4. 22
      test/libsolidity/SolidityEndToEndTest.cpp

16
libsolidity/Compiler.cpp

@ -178,6 +178,16 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
map<FixedHash<4>, FunctionTypePointer> interfaceFunctions = _contract.getInterfaceFunctions(); map<FixedHash<4>, FunctionTypePointer> interfaceFunctions = _contract.getInterfaceFunctions();
map<FixedHash<4>, const eth::AssemblyItem> callDataUnpackerEntryPoints; map<FixedHash<4>, const eth::AssemblyItem> callDataUnpackerEntryPoints;
FunctionDefinition const* fallback = _contract.getFallbackFunction();
eth::AssemblyItem notFound = m_context.newTag();
// shortcut messages without data if we have many functions in order to be able to receive
// ether with constant gas
if (interfaceFunctions.size() > 5 || fallback)
{
m_context << eth::Instruction::CALLDATASIZE << eth::Instruction::ISZERO;
m_context.appendConditionalJumpTo(notFound);
}
// retrieve the function signature hash from the calldata // retrieve the function signature hash from the calldata
if (!interfaceFunctions.empty()) if (!interfaceFunctions.empty())
CompilerUtils(m_context).loadFromMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8), true); CompilerUtils(m_context).loadFromMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8), true);
@ -189,7 +199,10 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
m_context << eth::dupInstruction(1) << u256(FixedHash<4>::Arith(it.first)) << eth::Instruction::EQ; m_context << eth::dupInstruction(1) << u256(FixedHash<4>::Arith(it.first)) << eth::Instruction::EQ;
m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first)); m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first));
} }
if (FunctionDefinition const* fallback = _contract.getFallbackFunction()) m_context.appendJumpTo(notFound);
m_context << notFound;
if (fallback)
{ {
eth::AssemblyItem returnTag = m_context.pushNewTag(); eth::AssemblyItem returnTag = m_context.pushNewTag();
fallback->accept(*this); fallback->accept(*this);
@ -198,6 +211,7 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
} }
else else
m_context << eth::Instruction::STOP; // function not found m_context << eth::Instruction::STOP; // function not found
for (auto const& it: interfaceFunctions) for (auto const& it: interfaceFunctions)
{ {
FunctionTypePointer const& functionType = it.second; FunctionTypePointer const& functionType = it.second;

39
libsolidity/ExpressionCompiler.cpp

@ -521,6 +521,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
break; break;
case Location::Send: case Location::Send:
_functionCall.getExpression().accept(*this); _functionCall.getExpression().accept(*this);
m_context << u256(0); // do not send gas (there still is the stipend)
arguments.front()->accept(*this); arguments.front()->accept(*this);
appendTypeConversion(*arguments.front()->getType(), appendTypeConversion(*arguments.front()->getType(),
*function.getParameterTypes().front(), true); *function.getParameterTypes().front(), true);
@ -532,7 +533,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
strings(), strings(),
Location::Bare, Location::Bare,
false, false,
false, true,
true true
), ),
{} {}
@ -1057,10 +1058,15 @@ void ExpressionCompiler::appendExternalFunctionCall(
unsigned gasStackPos = m_context.currentToBaseStackOffset(gasValueSize); unsigned gasStackPos = m_context.currentToBaseStackOffset(gasValueSize);
unsigned valueStackPos = m_context.currentToBaseStackOffset(1); unsigned valueStackPos = m_context.currentToBaseStackOffset(1);
bool returnSuccessCondition =
_functionType.getLocation() == FunctionType::Location::Bare ||
_functionType.getLocation() == FunctionType::Location::BareCallCode;
//@todo only return the first return value for now //@todo only return the first return value for now
Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr : Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr :
_functionType.getReturnParameterTypes().front().get(); _functionType.getReturnParameterTypes().front().get();
unsigned retSize = firstType ? firstType->getCalldataEncodedSize() : 0; unsigned retSize = firstType ? firstType->getCalldataEncodedSize() : 0;
if (returnSuccessCondition)
retSize = 0; // return value actually is success condition
m_context << u256(retSize) << u256(0); m_context << u256(retSize) << u256(0);
if (_functionType.isBareCall()) if (_functionType.isBareCall())
@ -1111,19 +1117,28 @@ void ExpressionCompiler::appendExternalFunctionCall(
else else
m_context << eth::Instruction::CALL; m_context << eth::Instruction::CALL;
//Propagate error condition (if CALL pushes 0 on stack). unsigned remainsSize =
m_context << eth::Instruction::ISZERO; 1 + // contract address
m_context.appendConditionalJumpTo(m_context.errorTag()); _functionType.valueSet() +
_functionType.gasSet() +
!_functionType.isBareCall();
if (_functionType.valueSet()) if (returnSuccessCondition)
m_context << eth::Instruction::POP; m_context << eth::swapInstruction(remainsSize);
if (_functionType.gasSet()) else
m_context << eth::Instruction::POP; {
if (!_functionType.isBareCall()) //Propagate error condition (if CALL pushes 0 on stack).
m_context << eth::Instruction::POP; m_context << eth::Instruction::ISZERO;
m_context << eth::Instruction::POP; // pop contract address m_context.appendConditionalJumpTo(m_context.errorTag());
}
if (_functionType.getLocation() == FunctionType::Location::RIPEMD160) CompilerUtils(m_context).popStackSlots(remainsSize);
if (returnSuccessCondition)
{
// already there
}
else if (_functionType.getLocation() == FunctionType::Location::RIPEMD160)
{ {
// fix: built-in contract returns right-aligned data // fix: built-in contract returns right-aligned data
CompilerUtils(m_context).loadFromMemory(0, IntegerType(160), false, true); CompilerUtils(m_context).loadFromMemory(0, IntegerType(160), false, true);

6
libsolidity/Types.cpp

@ -317,9 +317,9 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe
const MemberList IntegerType::AddressMemberList({ const MemberList IntegerType::AddressMemberList({
{"balance", make_shared<IntegerType >(256)}, {"balance", make_shared<IntegerType >(256)},
{"call", make_shared<FunctionType>(strings(), strings(), FunctionType::Location::Bare, true)}, {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::Bare, true)},
{"callcode", make_shared<FunctionType>(strings(), strings(), FunctionType::Location::BareCallCode, true)}, {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareCallCode, true)},
{"send", make_shared<FunctionType>(strings{"uint"}, strings{}, FunctionType::Location::Send)} {"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Location::Send)}
}); });
IntegerConstantType::IntegerConstantType(Literal const& _literal) IntegerConstantType::IntegerConstantType(Literal const& _literal)

22
test/libsolidity/SolidityEndToEndTest.cpp

@ -4187,6 +4187,28 @@ BOOST_AUTO_TEST_CASE(positive_integers_to_signed)
BOOST_CHECK(callContractFunction("q()") == encodeArgs(250)); BOOST_CHECK(callContractFunction("q()") == encodeArgs(250));
} }
BOOST_AUTO_TEST_CASE(failing_send)
{
char const* sourceCode = R"(
contract Helper {
uint[] data;
function () {
data[9]; // trigger exception
}
}
contract Main {
function callHelper(address _a) returns (bool r, uint bal) {
r = !_a.send(5);
bal = this.balance;
}
}
)";
compileAndRun(sourceCode, 0, "Helper");
u160 const c_helperAddress = m_contractAddress;
compileAndRun(sourceCode, 20, "Main");
BOOST_REQUIRE(callContractFunction("callHelper(address)", c_helperAddress) == encodeArgs(true, 20));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

Loading…
Cancel
Save