diff options
author | chriseth <c@ethdev.com> | 2015-12-01 21:07:33 +0800 |
---|---|---|
committer | chriseth <c@ethdev.com> | 2015-12-01 21:07:33 +0800 |
commit | d909f3a57bdd1ca43a132a80293e280910459f0d (patch) | |
tree | bd87da97c529b0ca7c7103f6ccff9909c348766a | |
parent | e853eb22fa0ec391812144675d4f000638db11b7 (diff) | |
parent | 7f415da886db51a5243749a7968917087fb4ca35 (diff) | |
download | dexon-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.cpp | 4 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 41 | ||||
-rw-r--r-- | test/libsolidity/SolidityEndToEndTest.cpp | 120 |
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() } |