Merge remote-tracking branch 'origin/develop' into develop_060

This commit is contained in:
chriseth
2019-11-14 13:42:46 +01:00
85 changed files with 1406 additions and 110 deletions
+2
View File
@@ -112,6 +112,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
+4 -2
View File
@@ -38,10 +38,12 @@ namespace solidity
NameAndTypeResolver::NameAndTypeResolver(
GlobalContext& _globalContext,
langutil::EVMVersion _evmVersion,
map<ASTNode const*, shared_ptr<DeclarationContainer>>& _scopes,
ErrorReporter& _errorReporter
) :
):
m_scopes(_scopes),
m_evmVersion(_evmVersion),
m_errorReporter(_errorReporter),
m_globalContext(_globalContext)
{
@@ -324,7 +326,7 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res
{
if (m_scopes.count(&_node))
setScope(&_node);
return ReferencesResolver(m_errorReporter, *this, _resolveInsideCode).resolve(_node);
return ReferencesResolver(m_errorReporter, *this, m_evmVersion, _resolveInsideCode).resolve(_node);
}
}
@@ -28,6 +28,8 @@
#include <libsolidity/ast/ASTAnnotations.h>
#include <libsolidity/ast/ASTVisitor.h>
#include <liblangutil/EVMVersion.h>
#include <boost/noncopyable.hpp>
#include <list>
@@ -55,6 +57,7 @@ public:
/// are filled during the lifetime of this object.
NameAndTypeResolver(
GlobalContext& _globalContext,
langutil::EVMVersion _evmVersion,
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes,
langutil::ErrorReporter& _errorReporter
);
@@ -130,6 +133,7 @@ private:
/// Aliases (for example `import "x" as y;`) create multiple pointers to the same scope.
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& m_scopes;
langutil::EVMVersion m_evmVersion;
DeclarationContainer* m_currentScope = nullptr;
langutil::ErrorReporter& m_errorReporter;
GlobalContext& m_globalContext;
+1 -1
View File
@@ -350,7 +350,7 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
yul::AsmAnalyzer(
analysisInfo,
errorsIgnored,
yul::EVMDialect::strictAssemblyForEVM(EVMVersion{}),
yul::EVMDialect::strictAssemblyForEVM(m_evmVersion),
resolver
).analyze(_inlineAssembly.operations());
return false;
@@ -24,6 +24,7 @@
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/ast/ASTAnnotations.h>
#include <liblangutil/EVMVersion.h>
#include <boost/noncopyable.hpp>
#include <list>
@@ -52,10 +53,12 @@ public:
ReferencesResolver(
langutil::ErrorReporter& _errorReporter,
NameAndTypeResolver& _resolver,
langutil::EVMVersion _evmVersion,
bool _resolveInsideCode = false
):
m_errorReporter(_errorReporter),
m_resolver(_resolver),
m_evmVersion(_evmVersion),
m_resolveInsideCode(_resolveInsideCode)
{}
@@ -99,6 +102,7 @@ private:
langutil::ErrorReporter& m_errorReporter;
NameAndTypeResolver& m_resolver;
langutil::EVMVersion m_evmVersion;
/// Stack of return parameters.
std::vector<ParameterList const*> m_returnParameters;
bool const m_resolveInsideCode;
+25 -4
View File
@@ -457,6 +457,9 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
TypePointer varType = _variable.annotation().type;
solAssert(!!varType, "Variable type not provided.");
if (auto contractType = dynamic_cast<ContractType const*>(varType))
if (contractType->contractDefinition().isLibrary())
m_errorReporter.typeError(_variable.location(), "The type of a variable cannot be a library.");
if (_variable.value())
expectType(*_variable.value(), *varType);
if (_variable.isConstant())
@@ -2633,12 +2636,30 @@ bool TypeChecker::visit(Identifier const& _identifier)
if (functionType->canTakeArguments(*annotation.arguments))
candidates.push_back(declaration);
}
if (candidates.empty())
m_errorReporter.fatalTypeError(_identifier.location(), "No matching declaration found after argument-dependent lookup.");
else if (candidates.size() == 1)
if (candidates.size() == 1)
annotation.referencedDeclaration = candidates.front();
else
m_errorReporter.fatalTypeError(_identifier.location(), "No unique declaration found after argument-dependent lookup.");
{
SecondarySourceLocation ssl;
for (Declaration const* declaration: annotation.overloadedDeclarations)
if (declaration->location().isEmpty())
{
// Try to re-construct function definition
string description;
for (auto const& param: declaration->functionType(true)->parameterTypes())
description += (description.empty() ? "" : ", ") + param->toString(false);
description = "function " + _identifier.name() + "(" + description + ")";
ssl.append("Candidate: " + description, declaration->location());
}
else
ssl.append("Candidate:", declaration->location());
if (candidates.empty())
m_errorReporter.fatalTypeError(_identifier.location(), ssl, "No matching declaration found after argument-dependent lookup.");
else
m_errorReporter.fatalTypeError(_identifier.location(), ssl, "No unique declaration found after argument-dependent lookup.");
}
}
}
solAssert(
+9
View File
@@ -3411,6 +3411,15 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current
return members;
}
BoolResult TypeType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
if (auto const* address = dynamic_cast<AddressType const*>(&_convertTo))
if (address->stateMutability() == StateMutability::NonPayable)
if (auto const* contractType = dynamic_cast<ContractType const*>(m_actualType))
return contractType->contractDefinition().isLibrary();
return isImplicitlyConvertibleTo(_convertTo);
}
ModifierType::ModifierType(ModifierDefinition const& _modifier)
{
TypePointers params;
+1
View File
@@ -1317,6 +1317,7 @@ public:
std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; }
MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
private:
TypePointer m_actualType;
};
+16 -1
View File
@@ -475,7 +475,22 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
{
solAssert(_functionCall.arguments().size() == 1, "");
solAssert(_functionCall.names().empty(), "");
acceptAndConvert(*_functionCall.arguments().front(), *_functionCall.annotation().type);
auto const& expression = *_functionCall.arguments().front();
auto const& targetType = *_functionCall.annotation().type;
if (auto const* typeType = dynamic_cast<TypeType const*>(expression.annotation().type))
if (auto const* addressType = dynamic_cast<AddressType const*>(&targetType))
{
auto const* contractType = dynamic_cast<ContractType const*>(typeType->actualType());
solAssert(
contractType &&
contractType->contractDefinition().isLibrary() &&
addressType->stateMutability() == StateMutability::NonPayable,
""
);
m_context.appendLibraryAddress(contractType->contractDefinition().fullyQualifiedName());
return false;
}
acceptAndConvert(expression, targetType);
return false;
}
+8 -3
View File
@@ -428,15 +428,20 @@ void BMC::inlineFunctionCall(FunctionCall const& _funCall)
auto const& funType = dynamic_cast<FunctionType const*>(calledExpr->annotation().type);
solAssert(funType, "");
auto const& functionParams = funDef->parameters();
auto const& arguments = _funCall.arguments();
unsigned firstParam = 0;
if (funType->bound())
{
auto const& boundFunction = dynamic_cast<MemberAccess const*>(calledExpr);
solAssert(boundFunction, "");
funArgs.push_back(expr(boundFunction->expression()));
funArgs.push_back(expr(boundFunction->expression(), functionParams.front()->type()));
firstParam = 1;
}
for (auto arg: _funCall.arguments())
funArgs.push_back(expr(*arg));
solAssert((arguments.size() + firstParam) == functionParams.size(), "");
for (unsigned i = 0; i < arguments.size(); ++i)
funArgs.push_back(expr(*arguments.at(i), functionParams.at(i + firstParam)->type()));
initializeFunctionCallParameters(*funDef, funArgs);
// The reason why we need to pushCallStack here instead of visit(FunctionDefinition)
+17 -5
View File
@@ -508,7 +508,13 @@ smt::SortPointer CHC::sort(FunctionDefinition const& _function)
auto boolSort = make_shared<smt::Sort>(smt::Kind::Bool);
vector<smt::SortPointer> varSorts;
for (auto const& var: _function.parameters() + _function.returnParameters())
varSorts.push_back(smt::smtSort(*var->type()));
{
// SMT solvers do not support function types as arguments.
if (var->type()->category() == Type::Category::Function)
varSorts.push_back(make_shared<smt::Sort>(smt::Kind::Int));
else
varSorts.push_back(smt::smtSort(*var->type()));
}
return make_shared<smt::FunctionSort>(
m_stateSorts + varSorts,
boolSort
@@ -526,7 +532,13 @@ smt::SortPointer CHC::sort(ASTNode const* _node)
auto boolSort = make_shared<smt::Sort>(smt::Kind::Bool);
vector<smt::SortPointer> varSorts;
for (auto const& var: m_currentFunction->localVariables())
varSorts.push_back(smt::smtSort(*var->type()));
{
// SMT solvers do not support function types as arguments.
if (var->type()->category() == Type::Category::Function)
varSorts.push_back(make_shared<smt::Sort>(smt::Kind::Int));
else
varSorts.push_back(smt::smtSort(*var->type()));
}
return make_shared<smt::FunctionSort>(
fSort->domain + varSorts,
boolSort
@@ -540,7 +552,7 @@ unique_ptr<smt::SymbolicFunctionVariable> CHC::createSymbolicBlock(smt::SortPoin
_name,
m_context
);
m_interface->registerRelation(block->currentValue());
m_interface->registerRelation(block->currentFunctionValue());
return block;
}
@@ -572,7 +584,7 @@ smt::Expression CHC::error()
smt::Expression CHC::error(unsigned _idx)
{
return m_errorPredicate->valueAtIndex(_idx)({});
return m_errorPredicate->functionValueAtIndex(_idx)({});
}
unique_ptr<smt::SymbolicFunctionVariable> CHC::createBlock(ASTNode const* _node, string const& _prefix)
@@ -589,7 +601,7 @@ void CHC::createErrorBlock()
{
solAssert(m_errorPredicate, "");
m_errorPredicate->increaseIndex();
m_interface->registerRelation(m_errorPredicate->currentValue());
m_interface->registerRelation(m_errorPredicate->currentFunctionValue());
}
void CHC::connectBlocks(smt::Expression const& _from, smt::Expression const& _to, smt::Expression const& _constraints)
+3 -3
View File
@@ -154,15 +154,15 @@ private:
//@{
/// Constructor predicate.
/// Default constructor sets state vars to 0.
std::unique_ptr<smt::SymbolicVariable> m_constructorPredicate;
std::unique_ptr<smt::SymbolicFunctionVariable> m_constructorPredicate;
/// Artificial Interface predicate.
/// Single entry block for all functions.
std::unique_ptr<smt::SymbolicVariable> m_interfacePredicate;
std::unique_ptr<smt::SymbolicFunctionVariable> m_interfacePredicate;
/// Artificial Error predicate.
/// Single error block for all assertions.
std::unique_ptr<smt::SymbolicVariable> m_errorPredicate;
std::unique_ptr<smt::SymbolicFunctionVariable> m_errorPredicate;
//@}
/// Variables.
+11 -7
View File
@@ -136,9 +136,13 @@ void SMTEncoder::visitFunctionOrModifier()
*modifierInvocation->name()->annotation().referencedDeclaration
);
vector<smt::Expression> modifierArgsExpr;
if (modifierInvocation->arguments())
for (auto arg: *modifierInvocation->arguments())
modifierArgsExpr.push_back(expr(*arg));
if (auto const* arguments = modifierInvocation->arguments())
{
auto const& modifierParams = modifierDef.parameters();
solAssert(modifierParams.size() == arguments->size(), "");
for (unsigned i = 0; i < arguments->size(); ++i)
modifierArgsExpr.push_back(expr(*arguments->at(i), modifierParams.at(i)->type()));
}
initializeFunctionCallParameters(modifierDef, modifierArgsExpr);
pushCallStack({&modifierDef, modifierInvocation.get()});
modifierDef.body().accept(*this);
@@ -595,10 +599,10 @@ void SMTEncoder::endVisit(Identifier const& _identifier)
{
// Will be translated as part of the node that requested the lvalue.
}
else if (_identifier.annotation().type->category() == Type::Category::Function)
visitFunctionIdentifier(_identifier);
else if (auto decl = identifierToVariable(_identifier))
defineExpr(_identifier, currentValue(*decl));
else if (_identifier.annotation().type->category() == Type::Category::Function)
visitFunctionIdentifier(_identifier);
else if (_identifier.name() == "now")
defineGlobalVariable(_identifier.name(), _identifier);
else if (_identifier.name() == "this")
@@ -699,7 +703,7 @@ void SMTEncoder::endVisit(Return const& _return)
}
}
else if (returnParams.size() == 1)
m_context.addAssertion(expr(*_return.expression()) == m_context.newValue(*returnParams.front()));
m_context.addAssertion(expr(*_return.expression(), returnParams.front()->type()) == m_context.newValue(*returnParams.front()));
}
}
@@ -1353,7 +1357,7 @@ void SMTEncoder::createExpr(Expression const& _e)
void SMTEncoder::defineExpr(Expression const& _e, smt::Expression _value)
{
createExpr(_e);
solAssert(smt::smtKind(_e.annotation().type->category()) != smt::Kind::Function, "Equality operator applied to type that is not fully supported");
solAssert(_value.sort->kind != smt::Kind::Function, "Equality operator applied to type that is not fully supported");
m_context.addAssertion(expr(_e) == _value);
}
+23 -1
View File
@@ -19,6 +19,7 @@
#include <libsolidity/ast/TypeProvider.h>
#include <libsolidity/ast/Types.h>
#include <libdevcore/CommonData.h>
#include <memory>
using namespace std;
@@ -139,7 +140,28 @@ pair<bool, shared_ptr<SymbolicVariable>> newSymbolicVariable(
else if (isBool(_type.category()))
var = make_shared<SymbolicBoolVariable>(type, _uniqueName, _context);
else if (isFunction(_type.category()))
var = make_shared<SymbolicFunctionVariable>(type, _uniqueName, _context);
{
auto const& fType = dynamic_cast<FunctionType const*>(type);
auto const& paramsIn = fType->parameterTypes();
auto const& paramsOut = fType->returnParameterTypes();
auto findFunctionParam = [&](auto&& params) {
return find_if(
begin(params),
end(params),
[&](TypePointer _paramType) { return _paramType->category() == solidity::Type::Category::Function; }
);
};
if (
findFunctionParam(paramsIn) != end(paramsIn) ||
findFunctionParam(paramsOut) != end(paramsOut)
)
{
abstract = true;
var = make_shared<SymbolicIntVariable>(TypeProvider::uint256(), type, _uniqueName, _context);
}
else
var = make_shared<SymbolicFunctionVariable>(type, _uniqueName, _context);
}
else if (isInteger(_type.category()))
var = make_shared<SymbolicIntVariable>(type, type, _uniqueName, _context);
else if (isFixedBytes(_type.category()))
+30 -4
View File
@@ -19,7 +19,6 @@
#include <libsolidity/formal/SymbolicTypes.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/TypeProvider.h>
using namespace std;
using namespace dev;
@@ -153,16 +152,38 @@ SymbolicFunctionVariable::SymbolicFunctionVariable(
solAssert(m_sort->kind == Kind::Function, "");
}
void SymbolicFunctionVariable::resetDeclaration()
Expression SymbolicFunctionVariable::currentValue(solidity::TypePointer const& _targetType) const
{
m_declaration = m_context.newVariable(currentName(), m_sort);
return m_abstract.currentValue(_targetType);
}
Expression SymbolicFunctionVariable::currentFunctionValue() const
{
return m_declaration;
}
Expression SymbolicFunctionVariable::valueAtIndex(int _index) const
{
return m_abstract.valueAtIndex(_index);
}
Expression SymbolicFunctionVariable::functionValueAtIndex(int _index) const
{
return SymbolicVariable::valueAtIndex(_index);
}
Expression SymbolicFunctionVariable::resetIndex()
{
SymbolicVariable::resetIndex();
return m_abstract.resetIndex();
}
Expression SymbolicFunctionVariable::increaseIndex()
{
++(*m_ssa);
resetDeclaration();
return currentValue();
m_abstract.increaseIndex();
return m_abstract.currentValue();
}
Expression SymbolicFunctionVariable::operator()(vector<Expression> _arguments) const
@@ -170,6 +191,11 @@ Expression SymbolicFunctionVariable::operator()(vector<Expression> _arguments) c
return m_declaration(_arguments);
}
void SymbolicFunctionVariable::resetDeclaration()
{
m_declaration = m_context.newVariable(currentName(), m_sort);
}
SymbolicMappingVariable::SymbolicMappingVariable(
solidity::TypePointer _type,
string _uniqueName,
+29 -3
View File
@@ -20,6 +20,7 @@
#include <libsolidity/formal/SolverInterface.h>
#include <libsolidity/formal/SSAVariable.h>
#include <libsolidity/ast/Types.h>
#include <libsolidity/ast/TypeProvider.h>
#include <memory>
namespace dev
@@ -138,7 +139,12 @@ public:
};
/**
* Specialization of SymbolicVariable for FunctionType
* Specialization of SymbolicVariable for FunctionType.
* Besides containing a symbolic function declaration,
* it also has an integer used as abstraction.
* By default, the abstract representation is used when
* values are requested, and the function declaration is
* used when operator() is applied over arguments.
*/
class SymbolicFunctionVariable: public SymbolicVariable
{
@@ -154,8 +160,20 @@ public:
EncodingContext& _context
);
Expression increaseIndex();
Expression operator()(std::vector<Expression> _arguments) const;
Expression currentValue(solidity::TypePointer const& _targetType = TypePointer{}) const override;
// Explicit request the function declaration.
Expression currentFunctionValue() const;
Expression valueAtIndex(int _index) const override;
// Explicit request the function declaration.
Expression functionValueAtIndex(int _index) const;
Expression resetIndex() override;
Expression increaseIndex() override;
Expression operator()(std::vector<Expression> _arguments) const override;
private:
/// Creates a new function declaration.
@@ -163,6 +181,14 @@ private:
/// Stores the current function declaration.
Expression m_declaration;
/// Abstract representation.
SymbolicIntVariable m_abstract{
TypeProvider::uint256(),
TypeProvider::uint256(),
m_uniqueName + "_abstract",
m_context
};
};
/**
+24 -1
View File
@@ -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>
@@ -286,7 +287,7 @@ bool CompilerStack::analyze()
noErrors = false;
m_globalContext = make_shared<GlobalContext>();
NameAndTypeResolver resolver(*m_globalContext, m_scopes, m_errorReporter);
NameAndTypeResolver resolver(*m_globalContext, m_evmVersion, m_scopes, m_errorReporter);
for (Source const* source: m_sourceOrder)
if (!resolver.registerDeclarations(*source->ast))
return false;
@@ -684,6 +685,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)
+9
View File
@@ -287,6 +287,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;
@@ -334,6 +338,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;
@@ -397,6 +402,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;
+3 -1
View File
@@ -927,10 +927,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))
+119
View File
@@ -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();
}
+58
View File
@@ -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;
};
}
}