aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorchriseth <c@ethdev.com>2015-12-01 21:07:33 +0800
committerchriseth <c@ethdev.com>2015-12-01 21:07:33 +0800
commitd909f3a57bdd1ca43a132a80293e280910459f0d (patch)
treebd87da97c529b0ca7c7103f6ccff9909c348766a
parente853eb22fa0ec391812144675d4f000638db11b7 (diff)
parent7f415da886db51a5243749a7968917087fb4ca35 (diff)
downloaddexon-solidity-d909f3a57bdd1ca43a132a80293e280910459f0d.tar.gz
dexon-solidity-d909f3a57bdd1ca43a132a80293e280910459f0d.tar.zst
dexon-solidity-d909f3a57bdd1ca43a132a80293e280910459f0d.zip
Merge pull request #262 from chriseth/bind_codegeneration
Code generation for bound methods
-rw-r--r--libsolidity/ast/Types.cpp4
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp41
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp120
3 files changed, 161 insertions, 4 deletions
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index 0409ac63..2dc7fb84 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -1514,6 +1514,10 @@ bool FunctionType::operator==(Type const& _other) const
//@todo this is ugly, but cannot be prevented right now
if (m_gasSet != other.m_gasSet || m_valueSet != other.m_valueSet)
return false;
+ if (bound() != other.bound())
+ return false;
+ if (bound() && *selfType() != *other.selfType())
+ return false;
return true;
}
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index fa077036..a090a28c 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -422,6 +422,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
else
{
FunctionType const& function = *functionType;
+ if (function.bound())
+ // Only callcode functions can be bound, this might be lifted later.
+ solAssert(function.location() == Location::CallCode, "");
switch (function.location())
{
case Location::Internal:
@@ -766,7 +769,26 @@ bool ExpressionCompiler::visit(NewExpression const&)
void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
{
CompilerContext::LocationSetter locationSetter(m_context, _memberAccess);
+
+ // Check whether the member is a bound function.
ASTString const& member = _memberAccess.memberName();
+ if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type.get()))
+ if (funType->bound())
+ {
+ utils().convertType(
+ *_memberAccess.expression().annotation().type,
+ *funType->selfType(),
+ true
+ );
+ auto contract = dynamic_cast<ContractDefinition const*>(funType->declaration().scope());
+ solAssert(contract && contract->isLibrary(), "");
+ //@TODO library name might not be unique
+ m_context.appendLibraryAddress(contract->name());
+ m_context << funType->externalIdentifier();
+ utils().moveIntoStack(funType->selfType()->sizeOnStack(), 2);
+ return;
+ }
+
switch (_memberAccess.expression().annotation().type->category())
{
case Type::Category::Contract:
@@ -1239,7 +1261,6 @@ void ExpressionCompiler::appendExternalFunctionCall(
vector<ASTPointer<Expression const>> const& _arguments
)
{
- eth::EVMSchedule schedule;// TODO: Make relevant to current suppose context.
solAssert(
_functionType.takesArbitraryParameters() ||
_arguments.size() == _functionType.parameterTypes().size(), ""
@@ -1249,15 +1270,20 @@ void ExpressionCompiler::appendExternalFunctionCall(
// <stack top>
// value [if _functionType.valueSet()]
// gas [if _functionType.gasSet()]
+ // self object [if bound - moved to top right away]
// function identifier [unless bare]
// contract address
+ unsigned selfSize = _functionType.bound() ? _functionType.selfType()->sizeOnStack() : 0;
unsigned gasValueSize = (_functionType.gasSet() ? 1 : 0) + (_functionType.valueSet() ? 1 : 0);
-
- unsigned contractStackPos = m_context.currentToBaseStackOffset(1 + gasValueSize + (_functionType.isBareCall() ? 0 : 1));
+ unsigned contractStackPos = m_context.currentToBaseStackOffset(1 + gasValueSize + selfSize + (_functionType.isBareCall() ? 0 : 1));
unsigned gasStackPos = m_context.currentToBaseStackOffset(gasValueSize);
unsigned valueStackPos = m_context.currentToBaseStackOffset(1);
+ // move self object to top
+ if (_functionType.bound())
+ utils().moveToStackTop(gasValueSize, _functionType.selfType()->sizeOnStack());
+
using FunctionKind = FunctionType::Location;
FunctionKind funKind = _functionType.location();
bool returnSuccessCondition = funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode;
@@ -1275,6 +1301,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
// Evaluate arguments.
TypePointers argumentTypes;
+ TypePointers parameterTypes = _functionType.parameterTypes();
bool manualFunctionId =
(funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode) &&
!_arguments.empty() &&
@@ -1295,6 +1322,11 @@ void ExpressionCompiler::appendExternalFunctionCall(
gasStackPos++;
valueStackPos++;
}
+ if (_functionType.bound())
+ {
+ argumentTypes.push_back(_functionType.selfType());
+ parameterTypes.insert(parameterTypes.begin(), _functionType.selfType());
+ }
for (size_t i = manualFunctionId ? 1 : 0; i < _arguments.size(); ++i)
{
_arguments[i]->accept(*this);
@@ -1313,7 +1345,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
// pointer on the stack).
utils().encodeToMemory(
argumentTypes,
- _functionType.parameterTypes(),
+ parameterTypes,
_functionType.padArguments(),
_functionType.takesArbitraryParameters(),
isCallCode
@@ -1346,6 +1378,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos));
else
{
+ eth::EVMSchedule schedule;// TODO: Make relevant to current suppose context.
// send all gas except the amount needed to execute "SUB" and "CALL"
// @todo this retains too much gas for now, needs to be fine-tuned.
u256 gasNeededByCaller = schedule.callGas + 10;
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index 78ceacfa..b69e253d 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -5957,6 +5957,126 @@ BOOST_AUTO_TEST_CASE(string_allocation_bug)
));
}
+BOOST_AUTO_TEST_CASE(using_for_function_on_int)
+{
+ char const* sourceCode = R"(
+ library D { function double(uint self) returns (uint) { return 2*self; } }
+ contract C {
+ using D for uint;
+ function f(uint a) returns (uint) {
+ return a.double();
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "D");
+ compileAndRun(sourceCode, 0, "C", bytes(), map<string, Address>{{"D", m_contractAddress}});
+ BOOST_CHECK(callContractFunction("f(uint256)", u256(9)) == encodeArgs(u256(2 * 9)));
+}
+
+BOOST_AUTO_TEST_CASE(using_for_function_on_struct)
+{
+ char const* sourceCode = R"(
+ library D { struct s { uint a; } function mul(s storage self, uint x) returns (uint) { return self.a *= x; } }
+ contract C {
+ using D for D.s;
+ D.s public x;
+ function f(uint a) returns (uint) {
+ x.a = 3;
+ return x.mul(a);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "D");
+ compileAndRun(sourceCode, 0, "C", bytes(), map<string, Address>{{"D", m_contractAddress}});
+ BOOST_CHECK(callContractFunction("f(uint256)", u256(7)) == encodeArgs(u256(3 * 7)));
+ BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(3 * 7)));
+}
+
+BOOST_AUTO_TEST_CASE(using_for_overload)
+{
+ char const* sourceCode = R"(
+ library D {
+ struct s { uint a; }
+ function mul(s storage self, uint x) returns (uint) { return self.a *= x; }
+ function mul(s storage self, bytes32 x) returns (bytes32) { }
+ }
+ contract C {
+ using D for D.s;
+ D.s public x;
+ function f(uint a) returns (uint) {
+ x.a = 6;
+ return x.mul(a);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "D");
+ compileAndRun(sourceCode, 0, "C", bytes(), map<string, Address>{{"D", m_contractAddress}});
+ BOOST_CHECK(callContractFunction("f(uint256)", u256(7)) == encodeArgs(u256(6 * 7)));
+ BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(6 * 7)));
+}
+
+BOOST_AUTO_TEST_CASE(using_for_by_name)
+{
+ char const* sourceCode = R"(
+ library D { struct s { uint a; } function mul(s storage self, uint x) returns (uint) { return self.a *= x; } }
+ contract C {
+ using D for D.s;
+ D.s public x;
+ function f(uint a) returns (uint) {
+ x.a = 6;
+ return x.mul({x: a});
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "D");
+ compileAndRun(sourceCode, 0, "C", bytes(), map<string, Address>{{"D", m_contractAddress}});
+ BOOST_CHECK(callContractFunction("f(uint256)", u256(7)) == encodeArgs(u256(6 * 7)));
+ BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(6 * 7)));
+}
+
+BOOST_AUTO_TEST_CASE(bound_function_in_var)
+{
+ char const* sourceCode = R"(
+ library D { struct s { uint a; } function mul(s storage self, uint x) returns (uint) { return self.a *= x; } }
+ contract C {
+ using D for D.s;
+ D.s public x;
+ function f(uint a) returns (uint) {
+ x.a = 6;
+ var g = x.mul;
+ return g({x: a});
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "D");
+ compileAndRun(sourceCode, 0, "C", bytes(), map<string, Address>{{"D", m_contractAddress}});
+ BOOST_CHECK(callContractFunction("f(uint256)", u256(7)) == encodeArgs(u256(6 * 7)));
+ BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(6 * 7)));
+}
+
+BOOST_AUTO_TEST_CASE(bound_function_to_string)
+{
+ char const* sourceCode = R"(
+ library D { function length(string memory self) returns (uint) { return bytes(self).length; } }
+ contract C {
+ using D for string;
+ string x;
+ function f() returns (uint) {
+ x = "abc";
+ return x.length();
+ }
+ function g() returns (uint) {
+ string memory s = "abc";
+ return s.length();
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "D");
+ compileAndRun(sourceCode, 0, "C", bytes(), map<string, Address>{{"D", m_contractAddress}});
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(3)));
+ BOOST_CHECK(callContractFunction("g()") == encodeArgs(u256(3)));
+}
+
BOOST_AUTO_TEST_SUITE_END()
}