diff options
author | chriseth <c@ethdev.com> | 2017-01-24 19:44:49 +0800 |
---|---|---|
committer | Alex Beregszaszi <alex@rtfs.hu> | 2017-09-16 19:12:43 +0800 |
commit | 2e72bd163a149183c119ca9664b98b0c5473da41 (patch) | |
tree | c21a611b03f2ba123b9b86604ee31558d9454df7 | |
parent | a0d171722a211720773aae0ded8a80991d44ba9d (diff) | |
download | dexon-solidity-2e72bd163a149183c119ca9664b98b0c5473da41.tar.gz dexon-solidity-2e72bd163a149183c119ca9664b98b0c5473da41.tar.zst dexon-solidity-2e72bd163a149183c119ca9664b98b0c5473da41.zip |
Allow structs as part of function interfaces.
-rw-r--r-- | libsolidity/ast/Types.cpp | 2 | ||||
-rw-r--r-- | libsolidity/interface/ABI.cpp | 58 | ||||
-rw-r--r-- | libsolidity/interface/ABI.h | 4 | ||||
-rw-r--r-- | test/libsolidity/SolidityABIJSON.cpp | 136 | ||||
-rw-r--r-- | test/libsolidity/SolidityEndToEndTest.cpp | 41 | ||||
-rw-r--r-- | test/libsolidity/SolidityNameAndTypeResolution.cpp | 50 |
6 files changed, 280 insertions, 11 deletions
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 705d0f7f..3a93b74e 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1770,7 +1770,7 @@ TypePointer StructType::interfaceType(bool _inLibrary) const if (_inLibrary && location() == DataLocation::Storage) return shared_from_this(); else - return TypePointer(); + return copyForLocation(DataLocation::Memory, true); } TypePointer StructType::copyForLocation(DataLocation _location, bool _isPointer) const diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp index 49df843d..7c7496cd 100644 --- a/libsolidity/interface/ABI.cpp +++ b/libsolidity/interface/ABI.cpp @@ -86,12 +86,12 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef) Json::Value params(Json::arrayValue); for (auto const& p: it->parameters()) { - solAssert(!!p->annotation().type->interfaceType(false), ""); + auto type = p->annotation().type->interfaceType(false); + solAssert(type, ""); Json::Value input; - input["name"] = p->name(); - input["type"] = p->annotation().type->interfaceType(false)->canonicalName(false); - input["indexed"] = p->isIndexed(); - params.append(input); + auto param = formatType(p->name(), *type, false); + param["indexed"] = p->isIndexed(); + params.append(param); } event["inputs"] = params; abi.append(event); @@ -111,10 +111,50 @@ Json::Value ABI::formatTypeList( for (unsigned i = 0; i < _names.size(); ++i) { solAssert(_types[i], ""); - Json::Value param; - param["name"] = _names[i]; - param["type"] = _types[i]->canonicalName(_forLibrary); - params.append(param); + params.append(formatType(_names[i], *_types[i], _forLibrary)); } return params; } + +Json::Value ABI::formatType(string const& _name, Type const& _type, bool _forLibrary) +{ + Json::Value ret; + ret["name"] = _name; + if (_type.isValueType() || (_forLibrary && _type.dataStoredIn(DataLocation::Storage))) + ret["type"] = _type.canonicalName(_forLibrary); + else if (ArrayType const* arrayType = dynamic_cast<ArrayType const*>(&_type)) + { + if (arrayType->isByteArray()) + ret["type"] = _type.canonicalName(_forLibrary); + else + { + string suffix; + if (arrayType->isDynamicallySized()) + suffix = "[]"; + else + suffix = string("[") + arrayType->length().str() + "]"; + solAssert(arrayType->baseType(), ""); + Json::Value subtype = formatType("", *arrayType->baseType(), _forLibrary); + if (subtype["type"].isString() && !subtype.isMember("subtype")) + ret["type"] = subtype["type"].asString() + suffix; + else + { + ret["type"] = suffix; + solAssert(!subtype.isMember("subtype"), ""); + ret["subtype"] = subtype["type"]; + } + } + } + else if (StructType const* structType = dynamic_cast<StructType const*>(&_type)) + { + ret["type"] = Json::arrayValue; + for (auto const& member: structType->members(nullptr)) + { + solAssert(member.type, ""); + ret["type"].append(formatType(member.name, *member.type, _forLibrary)); + } + } + else + solAssert(false, "Invalid type."); + return ret; +} diff --git a/libsolidity/interface/ABI.h b/libsolidity/interface/ABI.h index 95b162a9..7e42909b 100644 --- a/libsolidity/interface/ABI.h +++ b/libsolidity/interface/ABI.h @@ -50,6 +50,10 @@ private: std::vector<TypePointer> const& _types, bool _forLibrary ); + /// @returns a Json object with "name", "type" and potentially "subtype" keys, according + /// to the ABI specification. + /// If it is possible to express the type as a single string, it is allowed to return a single string. + static Json::Value formatType(std::string const& _name, Type const& _type, bool _forLibrary); }; } diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index 4b9223de..7f03285d 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -48,7 +48,7 @@ public: Json::Value generatedInterface = m_compilerStack.contractABI(""); Json::Value expectedInterface; - m_reader.parse(_expectedInterfaceString, expectedInterface); + BOOST_REQUIRE(m_reader.parse(_expectedInterfaceString, expectedInterface)); BOOST_CHECK_MESSAGE( expectedInterface == generatedInterface, "Expected:\n" << expectedInterface.toStyledString() << @@ -939,6 +939,140 @@ BOOST_AUTO_TEST_CASE(function_type) checkInterface(sourceCode, interface); } +BOOST_AUTO_TEST_CASE(return_structs) +{ + char const* text = R"( + contract C { + struct S { uint a; T[] sub; } + struct T { uint[2] x; } + function f() returns (uint x, S s) { + } + } + )"; + char const* interface = R"( + [ + { + "constant" : false, + "payable": false, + "inputs": [], + "name": "f", + "outputs": [{ + "name": "x", + "type": "uint256" + }, { + "name": "s", + "type": [{ + "name": "a", + "type": "uint256" + }, { + "name": "sub", + "subtype": [{ + "name": "x", + "type": "uint256[2]" + }], + "type": "[]" + }] + }], + "type" : "function" + } + ] + )"; + checkInterface(text, interface); +} + +BOOST_AUTO_TEST_CASE(event_structs) +{ + char const* text = R"( + contract C { + struct S { uint a; T[] sub; bytes b; } + struct T { uint[2] x; } + event E(T t, S s); + } + )"; + char const* interface = R"( + [{ + "anonymous" : false, + "inputs" : [{ + "indexed" : false, + "name" : "t", + "type" : [{ + "name" : "x", + "type" : "uint256[2]" + }] + }, { + "indexed" : false, + "name" : "s", + "type" : [{ + "name" : "a", + "type" : "uint256" + }, { + "name" : "sub", + "subtype" : [{ + "name" : "x", + "type" : "uint256[2]" + }], + "type" : "[]" + }, { + "name" : "b", + "type" : "bytes" + }] + }], + "name" : "E", + "type" : "event" + }] + )"; + checkInterface(text, interface); +} + +BOOST_AUTO_TEST_CASE(structs_in_libraries) +{ + char const* text = R"( + library L { + struct S { uint a; T[] sub; bytes b; } + struct T { uint[2] x; } + function f(L.S storage s) {} + function g(L.S s) {} + } + )"; + char const* interface = R"( + [{ + "constant" : false, + "inputs" : [{ + "name" : "s", + "type" : [{ + "name" : "a", + "type" : "uint256" + }, { + "name" : "sub", + "subtype" : [{ + "name" : "x", + "type" : "uint256[2]" + }], + "type" : "[]" + }, { + "name" : "b", + "type" : "bytes" + }] + }], + "name" : "g", + "outputs" : [], + "payable" : false, + "type" : "function" + }, { + "constant" : false, + "inputs" : [{ + "name" : "s", + "type" : "L.S storage" + }], + "name" : "f", + "outputs" : [], + "payable" : false, + "type" : "function" + }] + )"; + checkInterface(text, interface); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index ea924fcb..6ea02673 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -9682,6 +9682,47 @@ BOOST_AUTO_TEST_CASE(contracts_separated_with_comment) compileAndRun(sourceCode, 0, "C2"); } +BOOST_AUTO_TEST_CASE(return_structs) +{ + char const* sourceCode = R"( + contract C { + struct S { uint a; T[] sub; } + struct T { uint[2] x; } + function f() returns (uint x, S s) { + x = 7; + s.a = 8; + s.sub = new S[](3); + s.sub[0][0] = 9; + s.sub[1][0] = 10; + s.sub[2][1] = 11; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + +// Will calculate the exact encoding later. +// BOOST_CHECK(callContractFunction("f()") == encodeArgs( +// u256(7), u256(0x40), +// u256(8), u256(0x40), +// u256(3), +// // s.sub[0] +// u256(9), u256(0xc0), +// // s.sub[1] +// u256(10), u256(0xe0), +// // s.sub[2] +// u256(11), u256(0x100), +// // s.sub[0].sub +// u256(2) +// // s.sub[0].sub[0].a +// u256(8), +// u256() +// // s.sub[1].sub +// u256(0) +// // s.sub[2].sub +// u256(2) +// )); +} + BOOST_AUTO_TEST_CASE(include_creation_bytecode_only_once) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 8c271fe1..037bc9a0 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -5396,6 +5396,56 @@ BOOST_AUTO_TEST_CASE(constructible_internal_constructor) success(text); } +BOOST_AUTO_TEST_CASE(return_structs) +{ + char const* text = R"( + contract C { + struct S { uint a; T[] sub; } + struct T { uint[] x; } + function f() returns (uint x, S s) { + } + } + )"; + success(text); +} + +BOOST_AUTO_TEST_CASE(return_recursive_structs) +{ + char const* text = R"( + contract C { + struct S { uint a; S[] sub; } + function f() returns (uint x, S s) { + } + } + )"; + success(text); +} + +BOOST_AUTO_TEST_CASE(return_recursive_structs2) +{ + char const* text = R"( + contract C { + struct S { uint a; S[2] sub; } + function f() returns (uint x, S s) { + } + } + )"; + CHECK_ERROR(text, TypeError, "recursive data types in external functions."); +} + +BOOST_AUTO_TEST_CASE(return_recursive_structs3) +{ + char const* text = R"( + contract C { + struct S { uint a; S sub; } + struct T { S s; } + function f() returns (uint x, T t) { + } + } + )"; + CHECK_ERROR(text, TypeError, "recursive data types in external functions."); +} + BOOST_AUTO_TEST_CASE(address_checksum_type_deduction) { char const* text = R"( |