Skip to content

Commit d909f3a

Browse files
committed
Merge pull request #262 from chriseth/bind_codegeneration
Code generation for bound methods
2 parents e853eb2 + 7f415da commit d909f3a

3 files changed

Lines changed: 161 additions & 4 deletions

File tree

libsolidity/ast/Types.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1514,6 +1514,10 @@ bool FunctionType::operator==(Type const& _other) const
15141514
//@todo this is ugly, but cannot be prevented right now
15151515
if (m_gasSet != other.m_gasSet || m_valueSet != other.m_valueSet)
15161516
return false;
1517+
if (bound() != other.bound())
1518+
return false;
1519+
if (bound() && *selfType() != *other.selfType())
1520+
return false;
15171521
return true;
15181522
}
15191523

libsolidity/codegen/ExpressionCompiler.cpp

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
422422
else
423423
{
424424
FunctionType const& function = *functionType;
425+
if (function.bound())
426+
// Only callcode functions can be bound, this might be lifted later.
427+
solAssert(function.location() == Location::CallCode, "");
425428
switch (function.location())
426429
{
427430
case Location::Internal:
@@ -766,7 +769,26 @@ bool ExpressionCompiler::visit(NewExpression const&)
766769
void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
767770
{
768771
CompilerContext::LocationSetter locationSetter(m_context, _memberAccess);
772+
773+
// Check whether the member is a bound function.
769774
ASTString const& member = _memberAccess.memberName();
775+
if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type.get()))
776+
if (funType->bound())
777+
{
778+
utils().convertType(
779+
*_memberAccess.expression().annotation().type,
780+
*funType->selfType(),
781+
true
782+
);
783+
auto contract = dynamic_cast<ContractDefinition const*>(funType->declaration().scope());
784+
solAssert(contract && contract->isLibrary(), "");
785+
//@TODO library name might not be unique
786+
m_context.appendLibraryAddress(contract->name());
787+
m_context << funType->externalIdentifier();
788+
utils().moveIntoStack(funType->selfType()->sizeOnStack(), 2);
789+
return;
790+
}
791+
770792
switch (_memberAccess.expression().annotation().type->category())
771793
{
772794
case Type::Category::Contract:
@@ -1239,7 +1261,6 @@ void ExpressionCompiler::appendExternalFunctionCall(
12391261
vector<ASTPointer<Expression const>> const& _arguments
12401262
)
12411263
{
1242-
eth::EVMSchedule schedule;// TODO: Make relevant to current suppose context.
12431264
solAssert(
12441265
_functionType.takesArbitraryParameters() ||
12451266
_arguments.size() == _functionType.parameterTypes().size(), ""
@@ -1249,15 +1270,20 @@ void ExpressionCompiler::appendExternalFunctionCall(
12491270
// <stack top>
12501271
// value [if _functionType.valueSet()]
12511272
// gas [if _functionType.gasSet()]
1273+
// self object [if bound - moved to top right away]
12521274
// function identifier [unless bare]
12531275
// contract address
12541276

1277+
unsigned selfSize = _functionType.bound() ? _functionType.selfType()->sizeOnStack() : 0;
12551278
unsigned gasValueSize = (_functionType.gasSet() ? 1 : 0) + (_functionType.valueSet() ? 1 : 0);
1256-
1257-
unsigned contractStackPos = m_context.currentToBaseStackOffset(1 + gasValueSize + (_functionType.isBareCall() ? 0 : 1));
1279+
unsigned contractStackPos = m_context.currentToBaseStackOffset(1 + gasValueSize + selfSize + (_functionType.isBareCall() ? 0 : 1));
12581280
unsigned gasStackPos = m_context.currentToBaseStackOffset(gasValueSize);
12591281
unsigned valueStackPos = m_context.currentToBaseStackOffset(1);
12601282

1283+
// move self object to top
1284+
if (_functionType.bound())
1285+
utils().moveToStackTop(gasValueSize, _functionType.selfType()->sizeOnStack());
1286+
12611287
using FunctionKind = FunctionType::Location;
12621288
FunctionKind funKind = _functionType.location();
12631289
bool returnSuccessCondition = funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode;
@@ -1275,6 +1301,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
12751301

12761302
// Evaluate arguments.
12771303
TypePointers argumentTypes;
1304+
TypePointers parameterTypes = _functionType.parameterTypes();
12781305
bool manualFunctionId =
12791306
(funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode) &&
12801307
!_arguments.empty() &&
@@ -1295,6 +1322,11 @@ void ExpressionCompiler::appendExternalFunctionCall(
12951322
gasStackPos++;
12961323
valueStackPos++;
12971324
}
1325+
if (_functionType.bound())
1326+
{
1327+
argumentTypes.push_back(_functionType.selfType());
1328+
parameterTypes.insert(parameterTypes.begin(), _functionType.selfType());
1329+
}
12981330
for (size_t i = manualFunctionId ? 1 : 0; i < _arguments.size(); ++i)
12991331
{
13001332
_arguments[i]->accept(*this);
@@ -1313,7 +1345,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
13131345
// pointer on the stack).
13141346
utils().encodeToMemory(
13151347
argumentTypes,
1316-
_functionType.parameterTypes(),
1348+
parameterTypes,
13171349
_functionType.padArguments(),
13181350
_functionType.takesArbitraryParameters(),
13191351
isCallCode
@@ -1346,6 +1378,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
13461378
m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos));
13471379
else
13481380
{
1381+
eth::EVMSchedule schedule;// TODO: Make relevant to current suppose context.
13491382
// send all gas except the amount needed to execute "SUB" and "CALL"
13501383
// @todo this retains too much gas for now, needs to be fine-tuned.
13511384
u256 gasNeededByCaller = schedule.callGas + 10;

test/libsolidity/SolidityEndToEndTest.cpp

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5957,6 +5957,126 @@ BOOST_AUTO_TEST_CASE(string_allocation_bug)
59575957
));
59585958
}
59595959

5960+
BOOST_AUTO_TEST_CASE(using_for_function_on_int)
5961+
{
5962+
char const* sourceCode = R"(
5963+
library D { function double(uint self) returns (uint) { return 2*self; } }
5964+
contract C {
5965+
using D for uint;
5966+
function f(uint a) returns (uint) {
5967+
return a.double();
5968+
}
5969+
}
5970+
)";
5971+
compileAndRun(sourceCode, 0, "D");
5972+
compileAndRun(sourceCode, 0, "C", bytes(), map<string, Address>{{"D", m_contractAddress}});
5973+
BOOST_CHECK(callContractFunction("f(uint256)", u256(9)) == encodeArgs(u256(2 * 9)));
5974+
}
5975+
5976+
BOOST_AUTO_TEST_CASE(using_for_function_on_struct)
5977+
{
5978+
char const* sourceCode = R"(
5979+
library D { struct s { uint a; } function mul(s storage self, uint x) returns (uint) { return self.a *= x; } }
5980+
contract C {
5981+
using D for D.s;
5982+
D.s public x;
5983+
function f(uint a) returns (uint) {
5984+
x.a = 3;
5985+
return x.mul(a);
5986+
}
5987+
}
5988+
)";
5989+
compileAndRun(sourceCode, 0, "D");
5990+
compileAndRun(sourceCode, 0, "C", bytes(), map<string, Address>{{"D", m_contractAddress}});
5991+
BOOST_CHECK(callContractFunction("f(uint256)", u256(7)) == encodeArgs(u256(3 * 7)));
5992+
BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(3 * 7)));
5993+
}
5994+
5995+
BOOST_AUTO_TEST_CASE(using_for_overload)
5996+
{
5997+
char const* sourceCode = R"(
5998+
library D {
5999+
struct s { uint a; }
6000+
function mul(s storage self, uint x) returns (uint) { return self.a *= x; }
6001+
function mul(s storage self, bytes32 x) returns (bytes32) { }
6002+
}
6003+
contract C {
6004+
using D for D.s;
6005+
D.s public x;
6006+
function f(uint a) returns (uint) {
6007+
x.a = 6;
6008+
return x.mul(a);
6009+
}
6010+
}
6011+
)";
6012+
compileAndRun(sourceCode, 0, "D");
6013+
compileAndRun(sourceCode, 0, "C", bytes(), map<string, Address>{{"D", m_contractAddress}});
6014+
BOOST_CHECK(callContractFunction("f(uint256)", u256(7)) == encodeArgs(u256(6 * 7)));
6015+
BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(6 * 7)));
6016+
}
6017+
6018+
BOOST_AUTO_TEST_CASE(using_for_by_name)
6019+
{
6020+
char const* sourceCode = R"(
6021+
library D { struct s { uint a; } function mul(s storage self, uint x) returns (uint) { return self.a *= x; } }
6022+
contract C {
6023+
using D for D.s;
6024+
D.s public x;
6025+
function f(uint a) returns (uint) {
6026+
x.a = 6;
6027+
return x.mul({x: a});
6028+
}
6029+
}
6030+
)";
6031+
compileAndRun(sourceCode, 0, "D");
6032+
compileAndRun(sourceCode, 0, "C", bytes(), map<string, Address>{{"D", m_contractAddress}});
6033+
BOOST_CHECK(callContractFunction("f(uint256)", u256(7)) == encodeArgs(u256(6 * 7)));
6034+
BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(6 * 7)));
6035+
}
6036+
6037+
BOOST_AUTO_TEST_CASE(bound_function_in_var)
6038+
{
6039+
char const* sourceCode = R"(
6040+
library D { struct s { uint a; } function mul(s storage self, uint x) returns (uint) { return self.a *= x; } }
6041+
contract C {
6042+
using D for D.s;
6043+
D.s public x;
6044+
function f(uint a) returns (uint) {
6045+
x.a = 6;
6046+
var g = x.mul;
6047+
return g({x: a});
6048+
}
6049+
}
6050+
)";
6051+
compileAndRun(sourceCode, 0, "D");
6052+
compileAndRun(sourceCode, 0, "C", bytes(), map<string, Address>{{"D", m_contractAddress}});
6053+
BOOST_CHECK(callContractFunction("f(uint256)", u256(7)) == encodeArgs(u256(6 * 7)));
6054+
BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(6 * 7)));
6055+
}
6056+
6057+
BOOST_AUTO_TEST_CASE(bound_function_to_string)
6058+
{
6059+
char const* sourceCode = R"(
6060+
library D { function length(string memory self) returns (uint) { return bytes(self).length; } }
6061+
contract C {
6062+
using D for string;
6063+
string x;
6064+
function f() returns (uint) {
6065+
x = "abc";
6066+
return x.length();
6067+
}
6068+
function g() returns (uint) {
6069+
string memory s = "abc";
6070+
return s.length();
6071+
}
6072+
}
6073+
)";
6074+
compileAndRun(sourceCode, 0, "D");
6075+
compileAndRun(sourceCode, 0, "C", bytes(), map<string, Address>{{"D", m_contractAddress}});
6076+
BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(3)));
6077+
BOOST_CHECK(callContractFunction("g()") == encodeArgs(u256(3)));
6078+
}
6079+
59606080
BOOST_AUTO_TEST_SUITE_END()
59616081

59626082
}

0 commit comments

Comments
 (0)