mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Output the storage layout of a contract via storageLayout artifact
This commit is contained in:
@@ -113,6 +113,8 @@ set(sources
|
||||
interface/ReadFile.h
|
||||
interface/StandardCompiler.cpp
|
||||
interface/StandardCompiler.h
|
||||
interface/StorageLayout.cpp
|
||||
interface/StorageLayout.h
|
||||
interface/Version.cpp
|
||||
interface/Version.h
|
||||
parsing/DocStringParser.cpp
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include <libsolidity/interface/ABI.h>
|
||||
#include <libsolidity/interface/Natspec.h>
|
||||
#include <libsolidity/interface/GasEstimator.h>
|
||||
#include <libsolidity/interface/StorageLayout.h>
|
||||
#include <libsolidity/interface/Version.h>
|
||||
#include <libsolidity/parsing/Parser.h>
|
||||
|
||||
@@ -667,6 +668,28 @@ Json::Value const& CompilerStack::contractABI(Contract const& _contract) const
|
||||
return *_contract.abi;
|
||||
}
|
||||
|
||||
Json::Value const& CompilerStack::storageLayout(string const& _contractName) const
|
||||
{
|
||||
if (m_stackState < AnalysisPerformed)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||
|
||||
return storageLayout(contract(_contractName));
|
||||
}
|
||||
|
||||
Json::Value const& CompilerStack::storageLayout(Contract const& _contract) const
|
||||
{
|
||||
if (m_stackState < AnalysisPerformed)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||
|
||||
solAssert(_contract.contract, "");
|
||||
|
||||
// caches the result
|
||||
if (!_contract.storageLayout)
|
||||
_contract.storageLayout.reset(new Json::Value(StorageLayout().generate(*_contract.contract)));
|
||||
|
||||
return *_contract.storageLayout;
|
||||
}
|
||||
|
||||
Json::Value const& CompilerStack::natspecUser(string const& _contractName) const
|
||||
{
|
||||
if (m_stackState < AnalysisPerformed)
|
||||
|
||||
@@ -272,6 +272,10 @@ public:
|
||||
/// Prerequisite: Successful call to parse or compile.
|
||||
Json::Value const& contractABI(std::string const& _contractName) const;
|
||||
|
||||
/// @returns a JSON representing the storage layout of the contract.
|
||||
/// Prerequisite: Successful call to parse or compile.
|
||||
Json::Value const& storageLayout(std::string const& _contractName) const;
|
||||
|
||||
/// @returns a JSON representing the contract's user documentation.
|
||||
/// Prerequisite: Successful call to parse or compile.
|
||||
Json::Value const& natspecUser(std::string const& _contractName) const;
|
||||
@@ -319,6 +323,7 @@ private:
|
||||
eth::LinkerObject eWasmObject; ///< Experimental eWasm code
|
||||
mutable std::unique_ptr<std::string const> metadata; ///< The metadata json that will be hashed into the chain.
|
||||
mutable std::unique_ptr<Json::Value const> abi;
|
||||
mutable std::unique_ptr<Json::Value const> storageLayout;
|
||||
mutable std::unique_ptr<Json::Value const> userDocumentation;
|
||||
mutable std::unique_ptr<Json::Value const> devDocumentation;
|
||||
mutable std::unique_ptr<std::string const> sourceMapping;
|
||||
@@ -382,6 +387,10 @@ private:
|
||||
/// This will generate the JSON object and store it in the Contract object if it is not present yet.
|
||||
Json::Value const& contractABI(Contract const&) const;
|
||||
|
||||
/// @returns the storage layout of the contract as a JSON object.
|
||||
/// This will generate the JSON object and store it in the Contract object if it is not present yet.
|
||||
Json::Value const& storageLayout(Contract const&) const;
|
||||
|
||||
/// @returns the Natspec User documentation as a JSON object.
|
||||
/// This will generate the JSON object and store it in the Contract object if it is not present yet.
|
||||
Json::Value const& natspecUser(Contract const&) const;
|
||||
|
||||
@@ -887,10 +887,12 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
|
||||
string file = contractName.substr(0, colon);
|
||||
string name = contractName.substr(colon + 1);
|
||||
|
||||
// ABI, documentation and metadata
|
||||
// ABI, storage layout, documentation and metadata
|
||||
Json::Value contractData(Json::objectValue);
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "abi", wildcardMatchesExperimental))
|
||||
contractData["abi"] = compilerStack.contractABI(contractName);
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "storageLayout", false))
|
||||
contractData["storageLayout"] = compilerStack.storageLayout(contractName);
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "metadata", wildcardMatchesExperimental))
|
||||
contractData["metadata"] = compilerStack.metadata(contractName);
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "userdoc", wildcardMatchesExperimental))
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#include <libsolidity/interface/StorageLayout.h>
|
||||
|
||||
#include <libsolidity/ast/TypeProvider.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::solidity;
|
||||
|
||||
Json::Value StorageLayout::generate(ContractDefinition const& _contractDef)
|
||||
{
|
||||
solAssert(!m_contract, "");
|
||||
m_contract = &_contractDef;
|
||||
m_types.clear();
|
||||
|
||||
auto typeType = dynamic_cast<TypeType const*>(_contractDef.type());
|
||||
solAssert(typeType, "");
|
||||
auto contractType = dynamic_cast<ContractType const*>(typeType->actualType());
|
||||
solAssert(contractType, "");
|
||||
|
||||
Json::Value variables(Json::arrayValue);
|
||||
for (auto [var, slot, offset]: contractType->stateVariables())
|
||||
variables.append(generate(*var, slot, offset));
|
||||
|
||||
Json::Value layout;
|
||||
layout["storage"] = move(variables);
|
||||
layout["types"] = move(m_types);
|
||||
return layout;
|
||||
}
|
||||
|
||||
Json::Value StorageLayout::generate(VariableDeclaration const& _var, u256 const& _slot, unsigned _offset)
|
||||
{
|
||||
Json::Value varEntry;
|
||||
TypePointer varType = _var.type();
|
||||
|
||||
varEntry["label"] = _var.name();
|
||||
varEntry["astId"] = int(_var.id());
|
||||
varEntry["contract"] = m_contract->fullyQualifiedName();
|
||||
varEntry["slot"] = _slot.str();
|
||||
varEntry["offset"] = _offset;
|
||||
varEntry["type"] = typeKeyName(varType);
|
||||
|
||||
generate(varType);
|
||||
|
||||
return varEntry;
|
||||
}
|
||||
|
||||
void StorageLayout::generate(TypePointer _type)
|
||||
{
|
||||
if (m_types.isMember(typeKeyName(_type)))
|
||||
return;
|
||||
|
||||
// Register it now to cut recursive visits.
|
||||
Json::Value& typeInfo = m_types[typeKeyName(_type)];
|
||||
typeInfo["label"] = _type->toString(true);
|
||||
typeInfo["numberOfBytes"] = u256(_type->storageBytes() * _type->storageSize()).str();
|
||||
|
||||
if (auto structType = dynamic_cast<StructType const*>(_type))
|
||||
{
|
||||
Json::Value members(Json::arrayValue);
|
||||
auto const& structDef = structType->structDefinition();
|
||||
for (auto const& member: structDef.members())
|
||||
{
|
||||
auto const& offsets = structType->storageOffsetsOfMember(member->name());
|
||||
members.append(generate(*member, offsets.first, offsets.second));
|
||||
}
|
||||
typeInfo["members"] = move(members);
|
||||
typeInfo["encoding"] = "inplace";
|
||||
}
|
||||
else if (auto mappingType = dynamic_cast<MappingType const*>(_type))
|
||||
{
|
||||
typeInfo["key"] = typeKeyName(mappingType->keyType());
|
||||
typeInfo["value"] = typeKeyName(mappingType->valueType());
|
||||
generate(mappingType->keyType());
|
||||
generate(mappingType->valueType());
|
||||
typeInfo["encoding"] = "mapping";
|
||||
}
|
||||
else if (auto arrayType = dynamic_cast<ArrayType const*>(_type))
|
||||
{
|
||||
if (arrayType->isByteArray())
|
||||
typeInfo["encoding"] = "bytes";
|
||||
else
|
||||
{
|
||||
typeInfo["base"] = typeKeyName(arrayType->baseType());
|
||||
generate(arrayType->baseType());
|
||||
typeInfo["encoding"] = arrayType->isDynamicallySized() ? "dynamic_array" : "inplace";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
solAssert(_type->isValueType(), "");
|
||||
typeInfo["encoding"] = "inplace";
|
||||
}
|
||||
|
||||
solAssert(typeInfo.isMember("encoding"), "");
|
||||
}
|
||||
|
||||
string StorageLayout::typeKeyName(TypePointer _type)
|
||||
{
|
||||
if (auto refType = dynamic_cast<ReferenceType const*>(_type))
|
||||
return TypeProvider::withLocationIfReference(refType->location(), _type)->richIdentifier();
|
||||
return _type->richIdentifier();
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
/**
|
||||
* Generates the storage layout of a contract.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/ast/Types.h>
|
||||
|
||||
#include <json/json.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
|
||||
class StorageLayout
|
||||
{
|
||||
public:
|
||||
/// Generates the storage layout of the contract
|
||||
/// @param _contractDef The contract definition
|
||||
/// @return A JSON representation of the contract's storage layout.
|
||||
Json::Value generate(ContractDefinition const& _contractDef);
|
||||
|
||||
private:
|
||||
/// Generates the JSON information for a variable and its storage location.
|
||||
Json::Value generate(VariableDeclaration const& _var, u256 const& _slot, unsigned _offset);
|
||||
|
||||
/// Generates the JSON information for @param _type
|
||||
void generate(TypePointer _type);
|
||||
|
||||
/// The key for the JSON object describing a type.
|
||||
std::string typeKeyName(TypePointer _type);
|
||||
|
||||
Json::Value m_types;
|
||||
|
||||
/// Current analyzed contract
|
||||
ContractDefinition const* m_contract = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user