-
Notifications
You must be signed in to change notification settings - Fork 5.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add option to output storage layout info for a contract #4017
Changes from all commits
8b6a227
7aa54e9
cf59d21
7dc45d8
10ed302
a9d7ccf
cedd39a
5d51644
df4a794
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
/* | ||
This file is part of solidity. | ||
|
||
solidity is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU General Public License as published by | ||
the Free Software Foundation, either version 3 of the License, or | ||
(at your option) any later version. | ||
|
||
solidity is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
GNU General Public License for more details. | ||
|
||
You should have received a copy of the GNU General Public License | ||
along with solidity. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
/** | ||
* @author Santiago Palladino <[email protected]> | ||
* @date 2018 | ||
* Outputs contract storage layout information | ||
*/ | ||
|
||
#include <libsolidity/interface/StorageInfo.h> | ||
#include <libsolidity/codegen/Compiler.h> | ||
#include <libsolidity/ast/AST.h> | ||
|
||
using namespace std; | ||
using namespace dev; | ||
using namespace dev::solidity; | ||
|
||
Json::Value StorageInfo::generate(Compiler const* _compiler) | ||
{ | ||
Json::Value storage(Json::arrayValue); | ||
|
||
if(_compiler == NULL) | ||
{ | ||
return storage; | ||
} | ||
|
||
for (auto it: _compiler->stateVariables()) | ||
{ | ||
if (auto decl = dynamic_cast<VariableDeclaration const*>(it.first)) | ||
{ | ||
auto location = it.second; | ||
auto member = MemberList::Member(decl->name(), decl->type(), decl); | ||
auto memberData = processMember(member, location); | ||
|
||
// Assume that the parent scope of a state variable is a contract | ||
auto parent = ((Declaration*)decl->scope()); | ||
if (parent != NULL) | ||
{ | ||
memberData["contract"] = parent->name(); | ||
} | ||
|
||
storage.append(memberData); | ||
} | ||
} | ||
|
||
return storage; | ||
} | ||
|
||
|
||
Json::Value StorageInfo::processMember(MemberList::Member const& member, pair<u256, unsigned> const& location) | ||
{ | ||
Json::Value data; | ||
|
||
data["name"] = member.name; | ||
data["slot"] = location.first.str(); | ||
data["offset"] = to_string(location.second); | ||
data["type"] = processType(member.type); | ||
|
||
return data; | ||
} | ||
|
||
|
||
Json::Value StorageInfo::processType(TypePointer const& type) | ||
{ | ||
Json::Value data; | ||
|
||
// Common type info | ||
data["name"] = type->canonicalName(); | ||
data["size"] = type->storageSize().str(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps better to call this |
||
|
||
// Only include storageBytes if storageSize is 1, otherwise it always returns 32 | ||
if (type->storageSize() == 1) | ||
{ | ||
data["bytes"] = to_string(type->storageBytes()); | ||
} | ||
|
||
// Recursively visit complex types (structs, mappings, and arrays) | ||
if (type->category() == Type::Category::Struct) | ||
{ | ||
auto childStruct = static_pointer_cast<const StructType>(type); | ||
if (!childStruct->recursive()) | ||
{ | ||
Json::Value members(Json::arrayValue); | ||
for(auto member: childStruct->members(nullptr)) | ||
{ | ||
auto offsets = childStruct->storageOffsetsOfMember(member.name); | ||
auto memberData = processMember(member, offsets); | ||
members.append(memberData); | ||
} | ||
data["members"] = members; | ||
} | ||
} | ||
else if (type->category() == Type::Category::Mapping) { | ||
auto map = static_pointer_cast<const MappingType>(type); | ||
data["key"] = processType(map->keyType()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. key type should be processed differently |
||
data["value"] = processType(map->valueType()); | ||
} | ||
else if (type->category() == Type::Category::Array) { | ||
auto array = static_pointer_cast<const ArrayType>(type); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Big difference between statically-sized and dynamically-sized arrays |
||
if (!array->isByteArray()) | ||
{ | ||
data["base"] = processType(array->baseType()); | ||
} | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Needs assertion that there is no other category. |
||
return data; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
/* | ||
This file is part of solidity. | ||
|
||
solidity is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU General Public License as published by | ||
the Free Software Foundation, either version 3 of the License, or | ||
(at your option) any later version. | ||
|
||
solidity is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
GNU General Public License for more details. | ||
|
||
You should have received a copy of the GNU General Public License | ||
along with solidity. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
/** | ||
* @author Santiago Palladino <[email protected]> | ||
* @date 2018 | ||
* Outputs contract storage layout information | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <string> | ||
#include <memory> | ||
#include <json/json.h> | ||
#include <libsolidity/codegen/Compiler.h> | ||
#include <libsolidity/ast/Types.h> | ||
|
||
namespace dev | ||
{ | ||
namespace solidity | ||
{ | ||
|
||
// Forward declarations | ||
class Compiler; | ||
|
||
class StorageInfo | ||
{ | ||
public: | ||
/// Get the storage layout of a contract | ||
/// @param _compiler The compiler used for the contract | ||
/// @return A JSON representation of the contract's storage layout | ||
static Json::Value generate(Compiler const* _compiler); | ||
|
||
private: | ||
static Json::Value processType(TypePointer const& type); | ||
static Json::Value processMember(MemberList::Member const& member, std::pair<u256, unsigned> const& location); | ||
}; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -112,6 +112,7 @@ static string const g_strSources = "sources"; | |
static string const g_strSourceList = "sourceList"; | ||
static string const g_strSrcMap = "srcmap"; | ||
static string const g_strSrcMapRuntime = "srcmap-runtime"; | ||
static string const g_strStorageInfo = "storage-layout"; | ||
static string const g_strStandardJSON = "standard-json"; | ||
static string const g_strStrictAssembly = "strict-assembly"; | ||
static string const g_strPrettyJson = "pretty-json"; | ||
|
@@ -172,7 +173,8 @@ static set<string> const g_combinedJsonArgs | |
g_strOpcodes, | ||
g_strSignatureHashes, | ||
g_strSrcMap, | ||
g_strSrcMapRuntime | ||
g_strSrcMapRuntime, | ||
g_strStorageInfo | ||
}; | ||
|
||
/// Possible arguments to for --machine | ||
|
@@ -932,6 +934,8 @@ void CommandLineInterface::handleCombinedJSON() | |
contractData[g_strNatspecDev] = dev::jsonCompactPrint(m_compiler->natspecDev(contractName)); | ||
if (requests.count(g_strNatspecUser)) | ||
contractData[g_strNatspecUser] = dev::jsonCompactPrint(m_compiler->natspecUser(contractName)); | ||
if (requests.count(g_strStorageInfo)) | ||
contractData[g_strStorageInfo] = dev::jsonCompactPrint(m_compiler->storageInfo(contractName)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd hope not to support it on the commandline, rather expect people to use it through standard json IO. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should I remove it from the command line? It only affects the combined-json. |
||
} | ||
|
||
bool needsSourceList = requests.count(g_strAst) || requests.count(g_strSrcMap) || requests.count(g_strSrcMapRuntime); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be better to go through ContractType instead of the compiler context.