Skip to content

Commit

Permalink
Changes to code generation for function call options.
Browse files Browse the repository at this point in the history
  • Loading branch information
chriseth committed Jan 22, 2020
1 parent 4c933e6 commit 9014960
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 20 deletions.
62 changes: 42 additions & 20 deletions libsolidity/codegen/ExpressionCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
case FunctionType::Kind::Creation:
{
_functionCall.expression().accept(*this);
// Stack: [salt], [value]

solAssert(!function.gasSet(), "Gas limit set for contract creation.");
solAssert(function.returnParameterTypes().size() == 1, "");
TypePointers argumentTypes;
Expand All @@ -633,30 +635,32 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
utils().fetchFreeMemoryPointer();
utils().copyContractCodeToMemory(*contract, true);
utils().abiEncode(argumentTypes, function.parameterTypes());
// now on stack: memory_end_ptr
// need: [salt], size, offset, endowment
// now on stack: [salt], [value], memory_end_ptr
// need: [salt], size, offset, value

if (function.saltSet())
{
m_context << dupInstruction(2 + (function.valueSet() ? 1 : 0));
m_context << Instruction::SWAP1;
}

// now: [salt], memory_end_ptr
// now: [salt], [value], [salt], memory_end_ptr
utils().toSizeAfterFreeMemoryPointer();

// now: [salt], size, offset
// now: [salt], [value], [salt], size, offset
if (function.valueSet())
m_context << dupInstruction(3 + (function.saltSet() ? 1 : 0));
else
m_context << u256(0);

// now: [salt], size, offset, endowment
// now: [salt], [value], [salt], size, offset, value
if (function.saltSet())
m_context << Instruction::CREATE2;
else
m_context << Instruction::CREATE;

// now: [salt], [value], address

if (function.valueSet())
m_context << swapInstruction(1) << Instruction::POP;
if (function.saltSet())
Expand Down Expand Up @@ -1213,25 +1217,43 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)

bool ExpressionCompiler::visit(FunctionCallOptions const& _functionCallOptions)
{
_functionCallOptions.expression().accept(*this);

// Desired Stack: [salt], [gas], [value]
enum { Salt, Gas, Value };
int optionIndicies[3] = {-1, -1, -1};

vector<ASTPointer<ASTString>> const& names = _functionCallOptions.names();

for (size_t i = 0; i < names.size(); i++)
if (*names[i] == "salt")
optionIndicies[Salt] = i;
else if (*names[i] == "gas")
optionIndicies[Gas] = i;
else if (*names[i] == "value")
optionIndicies[Value] = i;
enum Option { Salt, Gas, Value };

vector<Option> presentOptions;
FunctionType const& funType = dynamic_cast<FunctionType const&>(
*_functionCallOptions.expression().annotation().type
);
if (funType.saltSet()) presentOptions.emplace_back(Salt);
if (funType.gasSet()) presentOptions.emplace_back(Gas);
if (funType.valueSet()) presentOptions.emplace_back(Value);

for (size_t i = 0; i < _functionCallOptions.options().size(); ++i)
{
string const& name = *_functionCallOptions.names()[i];
Type const* requiredType = TypeProvider::uint256();
Option newOption;
if (name == "salt")
{
newOption = Salt;
requiredType = TypeProvider::fixedBytes(32);
}
else if (name == "gas")
newOption = Gas;
else if (name == "value")
newOption = Value;
else
solAssert(false, "Unexpected option name!");
acceptAndConvert(*_functionCallOptions.options()[i], *requiredType);

for (int index: optionIndicies)
if (index >= 0)
_functionCallOptions.options()[index]->accept(*this);
solAssert(!contains(presentOptions, newOption), "");
size_t insertPos = presentOptions.end() - lower_bound(presentOptions.begin(), presentOptions.end(), newOption);

utils().moveIntoStack(insertPos, 1);
presentOptions.insert(presentOptions.end() - insertPos, newOption);
}

return false;
}
Expand Down
34 changes: 34 additions & 0 deletions test/libsolidity/SolidityEndToEndTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1929,6 +1929,40 @@ BOOST_AUTO_TEST_CASE(gas_and_value_basic)
BOOST_REQUIRE(callContractFunction("checkState()") == encodeArgs(false, 20 - 5));
}

BOOST_AUTO_TEST_CASE(gas_and_value_brace_syntax)
{
char const* sourceCode = R"(
contract helper {
bool flag;
function getBalance() payable public returns (uint256 myBalance) {
return address(this).balance;
}
function setFlag() public { flag = true; }
function getFlag() public returns (bool fl) { return flag; }
}
contract test {
helper h;
constructor() public payable { h = new helper(); }
function sendAmount(uint amount) public payable returns (uint256 bal) {
return h.getBalance{value: amount}();
}
function outOfGas() public returns (bool ret) {
h.setFlag{gas: 2}(); // should fail due to OOG
return true;
}
function checkState() public returns (bool flagAfter, uint myBal) {
flagAfter = h.getFlag();
myBal = address(this).balance;
}
}
)";
compileAndRun(sourceCode, 20);
BOOST_REQUIRE(callContractFunction("sendAmount(uint256)", 5) == encodeArgs(5));
// call to helper should not succeed but amount should be transferred anyway
BOOST_REQUIRE(callContractFunction("outOfGas()") == bytes());
BOOST_REQUIRE(callContractFunction("checkState()") == encodeArgs(false, 20 - 5));
}

BOOST_AUTO_TEST_CASE(gasleft_decrease)
{
char const* sourceCode = R"(
Expand Down

0 comments on commit 9014960

Please sign in to comment.