mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #2262 from ethereum/interface-cleanup
Cleanup the external interfaces (ABI and Natspec)
This commit is contained in:
commit
56424bb202
116
libsolidity/interface/ABI.cpp
Normal file
116
libsolidity/interface/ABI.cpp
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
/**
|
||||
* Utilities to handle the Contract ABI (https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI)
|
||||
*/
|
||||
|
||||
#include <libsolidity/interface/ABI.h>
|
||||
#include <boost/range/irange.hpp>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::solidity;
|
||||
|
||||
Json::Value ABI::generate(ContractDefinition const& _contractDef)
|
||||
{
|
||||
Json::Value abi(Json::arrayValue);
|
||||
|
||||
for (auto it: _contractDef.interfaceFunctions())
|
||||
{
|
||||
auto externalFunctionType = it.second->interfaceFunctionType();
|
||||
Json::Value method;
|
||||
method["type"] = "function";
|
||||
method["name"] = it.second->declaration().name();
|
||||
method["constant"] = it.second->isConstant();
|
||||
method["payable"] = it.second->isPayable();
|
||||
method["inputs"] = formatTypeList(
|
||||
externalFunctionType->parameterNames(),
|
||||
externalFunctionType->parameterTypes(),
|
||||
_contractDef.isLibrary()
|
||||
);
|
||||
method["outputs"] = formatTypeList(
|
||||
externalFunctionType->returnParameterNames(),
|
||||
externalFunctionType->returnParameterTypes(),
|
||||
_contractDef.isLibrary()
|
||||
);
|
||||
abi.append(method);
|
||||
}
|
||||
if (_contractDef.constructor())
|
||||
{
|
||||
Json::Value method;
|
||||
method["type"] = "constructor";
|
||||
auto externalFunction = FunctionType(*_contractDef.constructor(), false).interfaceFunctionType();
|
||||
solAssert(!!externalFunction, "");
|
||||
method["payable"] = externalFunction->isPayable();
|
||||
method["inputs"] = formatTypeList(
|
||||
externalFunction->parameterNames(),
|
||||
externalFunction->parameterTypes(),
|
||||
_contractDef.isLibrary()
|
||||
);
|
||||
abi.append(method);
|
||||
}
|
||||
if (_contractDef.fallbackFunction())
|
||||
{
|
||||
auto externalFunctionType = FunctionType(*_contractDef.fallbackFunction(), false).interfaceFunctionType();
|
||||
solAssert(!!externalFunctionType, "");
|
||||
Json::Value method;
|
||||
method["type"] = "fallback";
|
||||
method["payable"] = externalFunctionType->isPayable();
|
||||
abi.append(method);
|
||||
}
|
||||
for (auto const& it: _contractDef.interfaceEvents())
|
||||
{
|
||||
Json::Value event;
|
||||
event["type"] = "event";
|
||||
event["name"] = it->name();
|
||||
event["anonymous"] = it->isAnonymous();
|
||||
Json::Value params(Json::arrayValue);
|
||||
for (auto const& p: it->parameters())
|
||||
{
|
||||
solAssert(!!p->annotation().type->interfaceType(false), "");
|
||||
Json::Value input;
|
||||
input["name"] = p->name();
|
||||
input["type"] = p->annotation().type->interfaceType(false)->canonicalName(false);
|
||||
input["indexed"] = p->isIndexed();
|
||||
params.append(input);
|
||||
}
|
||||
event["inputs"] = params;
|
||||
abi.append(event);
|
||||
}
|
||||
|
||||
return abi;
|
||||
}
|
||||
|
||||
Json::Value ABI::formatTypeList(
|
||||
vector<string> const& _names,
|
||||
vector<TypePointer> const& _types,
|
||||
bool _forLibrary
|
||||
)
|
||||
{
|
||||
Json::Value params(Json::arrayValue);
|
||||
solAssert(_names.size() == _types.size(), "Names and types vector size does not match");
|
||||
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);
|
||||
}
|
||||
return params;
|
||||
}
|
56
libsolidity/interface/ABI.h
Normal file
56
libsolidity/interface/ABI.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
/**
|
||||
* Utilities to handle the Contract ABI (https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <json/json.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
|
||||
// Forward declarations
|
||||
class ContractDefinition;
|
||||
class Type;
|
||||
using TypePointer = std::shared_ptr<Type const>;
|
||||
|
||||
class ABI
|
||||
{
|
||||
public:
|
||||
/// Get the ABI Interface of the contract
|
||||
/// @param _contractDef The contract definition
|
||||
/// @return A JSONrepresentation of the contract's ABI Interface
|
||||
static Json::Value generate(ContractDefinition const& _contractDef);
|
||||
private:
|
||||
/// @returns a json value suitable for a list of types in function input or output
|
||||
/// parameters or other places. If @a _forLibrary is true, complex types are referenced
|
||||
/// by name, otherwise they are anonymously expanded.
|
||||
static Json::Value formatTypeList(
|
||||
std::vector<std::string> const& _names,
|
||||
std::vector<TypePointer> const& _types,
|
||||
bool _forLibrary
|
||||
);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -37,7 +37,8 @@
|
||||
#include <libsolidity/analysis/PostTypeChecker.h>
|
||||
#include <libsolidity/analysis/SyntaxChecker.h>
|
||||
#include <libsolidity/codegen/Compiler.h>
|
||||
#include <libsolidity/interface/InterfaceHandler.h>
|
||||
#include <libsolidity/interface/ABI.h>
|
||||
#include <libsolidity/interface/Natspec.h>
|
||||
#include <libsolidity/interface/GasEstimator.h>
|
||||
#include <libsolidity/formal/Why3Translator.h>
|
||||
|
||||
@ -219,8 +220,8 @@ bool CompilerStack::analyze()
|
||||
TypeChecker typeChecker(m_errors);
|
||||
if (typeChecker.checkTypeRequirements(*contract))
|
||||
{
|
||||
contract->setDevDocumentation(InterfaceHandler::devDocumentation(*contract));
|
||||
contract->setUserDocumentation(InterfaceHandler::userDocumentation(*contract));
|
||||
contract->setDevDocumentation(Natspec::devDocumentation(*contract));
|
||||
contract->setUserDocumentation(Natspec::userDocumentation(*contract));
|
||||
}
|
||||
else
|
||||
noErrors = false;
|
||||
@ -444,17 +445,31 @@ map<string, unsigned> CompilerStack::sourceIndices() const
|
||||
return indices;
|
||||
}
|
||||
|
||||
Json::Value const& CompilerStack::interface(string const& _contractName) const
|
||||
Json::Value const& CompilerStack::contractABI(string const& _contractName) const
|
||||
{
|
||||
return metadata(_contractName, DocumentationType::ABIInterface);
|
||||
return contractABI(contract(_contractName));
|
||||
}
|
||||
|
||||
Json::Value const& CompilerStack::metadata(string const& _contractName, DocumentationType _type) const
|
||||
Json::Value const& CompilerStack::contractABI(Contract const& _contract) const
|
||||
{
|
||||
return metadata(contract(_contractName), _type);
|
||||
if (m_stackState < AnalysisSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
|
||||
|
||||
solAssert(_contract.contract, "");
|
||||
|
||||
// caches the result
|
||||
if (!_contract.abi)
|
||||
_contract.abi.reset(new Json::Value(ABI::generate(*_contract.contract)));
|
||||
|
||||
return *_contract.abi;
|
||||
}
|
||||
|
||||
Json::Value const& CompilerStack::metadata(Contract const& _contract, DocumentationType _type) const
|
||||
Json::Value const& CompilerStack::natspec(string const& _contractName, DocumentationType _type) const
|
||||
{
|
||||
return natspec(contract(_contractName), _type);
|
||||
}
|
||||
|
||||
Json::Value const& CompilerStack::natspec(Contract const& _contract, DocumentationType _type) const
|
||||
{
|
||||
if (m_stackState < AnalysisSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
|
||||
@ -471,16 +486,13 @@ Json::Value const& CompilerStack::metadata(Contract const& _contract, Documentat
|
||||
case DocumentationType::NatspecDev:
|
||||
doc = &_contract.devDocumentation;
|
||||
break;
|
||||
case DocumentationType::ABIInterface:
|
||||
doc = &_contract.interface;
|
||||
break;
|
||||
default:
|
||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type."));
|
||||
}
|
||||
|
||||
// caches the result
|
||||
if (!*doc)
|
||||
doc->reset(new Json::Value(InterfaceHandler::documentation(*_contract.contract, _type)));
|
||||
doc->reset(new Json::Value(Natspec::documentation(*_contract.contract, _type)));
|
||||
|
||||
return *(*doc);
|
||||
}
|
||||
@ -830,9 +842,9 @@ string CompilerStack::createOnChainMetadata(Contract const& _contract) const
|
||||
for (auto const& library: m_libraries)
|
||||
meta["settings"]["libraries"][library.first] = "0x" + toHex(library.second.asBytes());
|
||||
|
||||
meta["output"]["abi"] = metadata(_contract, DocumentationType::ABIInterface);
|
||||
meta["output"]["userdoc"] = metadata(_contract, DocumentationType::NatspecUser);
|
||||
meta["output"]["devdoc"] = metadata(_contract, DocumentationType::NatspecDev);
|
||||
meta["output"]["abi"] = contractABI(_contract);
|
||||
meta["output"]["userdoc"] = natspec(_contract, DocumentationType::NatspecUser);
|
||||
meta["output"]["devdoc"] = natspec(_contract, DocumentationType::NatspecDev);
|
||||
|
||||
return jsonCompactPrint(meta);
|
||||
}
|
||||
|
@ -59,15 +59,14 @@ class FunctionDefinition;
|
||||
class SourceUnit;
|
||||
class Compiler;
|
||||
class GlobalContext;
|
||||
class InterfaceHandler;
|
||||
class Natspec;
|
||||
class Error;
|
||||
class DeclarationContainer;
|
||||
|
||||
enum class DocumentationType: uint8_t
|
||||
{
|
||||
NatspecUser = 1,
|
||||
NatspecDev,
|
||||
ABIInterface
|
||||
NatspecDev
|
||||
};
|
||||
|
||||
/**
|
||||
@ -173,14 +172,14 @@ public:
|
||||
/// @returns a mapping assigning each source name its index inside the vector returned
|
||||
/// by sourceNames().
|
||||
std::map<std::string, unsigned> sourceIndices() const;
|
||||
/// @returns a JSON representing the contract interface.
|
||||
/// @returns a JSON representing the contract ABI.
|
||||
/// Prerequisite: Successful call to parse or compile.
|
||||
Json::Value const& interface(std::string const& _contractName = "") const;
|
||||
Json::Value const& contractABI(std::string const& _contractName = "") const;
|
||||
/// @returns a JSON representing the contract's documentation.
|
||||
/// Prerequisite: Successful call to parse or compile.
|
||||
/// @param type The type of the documentation to get.
|
||||
/// Can be one of 4 types defined at @c DocumentationType
|
||||
Json::Value const& metadata(std::string const& _contractName, DocumentationType _type) const;
|
||||
Json::Value const& natspec(std::string const& _contractName, DocumentationType _type) const;
|
||||
std::string const& onChainMetadata(std::string const& _contractName) const;
|
||||
void useMetadataLiteralSources(bool _metadataLiteralSources) { m_metadataLiteralSources = _metadataLiteralSources; }
|
||||
|
||||
@ -230,7 +229,7 @@ private:
|
||||
eth::LinkerObject runtimeObject;
|
||||
eth::LinkerObject cloneObject;
|
||||
std::string onChainMetadata; ///< The metadata json that will be hashed into the chain.
|
||||
mutable std::unique_ptr<Json::Value const> interface;
|
||||
mutable std::unique_ptr<Json::Value const> abi;
|
||||
mutable std::unique_ptr<Json::Value const> userDocumentation;
|
||||
mutable std::unique_ptr<Json::Value const> devDocumentation;
|
||||
mutable std::unique_ptr<std::string const> sourceMapping;
|
||||
@ -267,7 +266,8 @@ private:
|
||||
|
||||
std::string createOnChainMetadata(Contract const& _contract) const;
|
||||
std::string computeSourceMapping(eth::AssemblyItems const& _items) const;
|
||||
Json::Value const& metadata(Contract const&, DocumentationType _type) const;
|
||||
Json::Value const& contractABI(Contract const&) const;
|
||||
Json::Value const& natspec(Contract const&, DocumentationType _type) const;
|
||||
|
||||
struct Remapping
|
||||
{
|
||||
|
@ -1,197 +0,0 @@
|
||||
|
||||
#include <libsolidity/interface/InterfaceHandler.h>
|
||||
#include <boost/range/irange.hpp>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/interface/CompilerStack.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::solidity;
|
||||
|
||||
Json::Value InterfaceHandler::documentation(
|
||||
ContractDefinition const& _contractDef,
|
||||
DocumentationType _type
|
||||
)
|
||||
{
|
||||
switch(_type)
|
||||
{
|
||||
case DocumentationType::NatspecUser:
|
||||
return userDocumentation(_contractDef);
|
||||
case DocumentationType::NatspecDev:
|
||||
return devDocumentation(_contractDef);
|
||||
case DocumentationType::ABIInterface:
|
||||
return abiInterface(_contractDef);
|
||||
}
|
||||
|
||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type"));
|
||||
}
|
||||
|
||||
Json::Value InterfaceHandler::abiInterface(ContractDefinition const& _contractDef)
|
||||
{
|
||||
Json::Value abi(Json::arrayValue);
|
||||
|
||||
for (auto it: _contractDef.interfaceFunctions())
|
||||
{
|
||||
auto externalFunctionType = it.second->interfaceFunctionType();
|
||||
Json::Value method;
|
||||
method["type"] = "function";
|
||||
method["name"] = it.second->declaration().name();
|
||||
method["constant"] = it.second->isConstant();
|
||||
method["payable"] = it.second->isPayable();
|
||||
method["inputs"] = formatTypeList(
|
||||
externalFunctionType->parameterNames(),
|
||||
externalFunctionType->parameterTypes(),
|
||||
_contractDef.isLibrary()
|
||||
);
|
||||
method["outputs"] = formatTypeList(
|
||||
externalFunctionType->returnParameterNames(),
|
||||
externalFunctionType->returnParameterTypes(),
|
||||
_contractDef.isLibrary()
|
||||
);
|
||||
abi.append(method);
|
||||
}
|
||||
if (_contractDef.constructor())
|
||||
{
|
||||
Json::Value method;
|
||||
method["type"] = "constructor";
|
||||
auto externalFunction = FunctionType(*_contractDef.constructor(), false).interfaceFunctionType();
|
||||
solAssert(!!externalFunction, "");
|
||||
method["payable"] = externalFunction->isPayable();
|
||||
method["inputs"] = formatTypeList(
|
||||
externalFunction->parameterNames(),
|
||||
externalFunction->parameterTypes(),
|
||||
_contractDef.isLibrary()
|
||||
);
|
||||
abi.append(method);
|
||||
}
|
||||
if (_contractDef.fallbackFunction())
|
||||
{
|
||||
auto externalFunctionType = FunctionType(*_contractDef.fallbackFunction(), false).interfaceFunctionType();
|
||||
solAssert(!!externalFunctionType, "");
|
||||
Json::Value method;
|
||||
method["type"] = "fallback";
|
||||
method["payable"] = externalFunctionType->isPayable();
|
||||
abi.append(method);
|
||||
}
|
||||
for (auto const& it: _contractDef.interfaceEvents())
|
||||
{
|
||||
Json::Value event;
|
||||
event["type"] = "event";
|
||||
event["name"] = it->name();
|
||||
event["anonymous"] = it->isAnonymous();
|
||||
Json::Value params(Json::arrayValue);
|
||||
for (auto const& p: it->parameters())
|
||||
{
|
||||
solAssert(!!p->annotation().type->interfaceType(false), "");
|
||||
Json::Value input;
|
||||
input["name"] = p->name();
|
||||
input["type"] = p->annotation().type->interfaceType(false)->canonicalName(false);
|
||||
input["indexed"] = p->isIndexed();
|
||||
params.append(input);
|
||||
}
|
||||
event["inputs"] = params;
|
||||
abi.append(event);
|
||||
}
|
||||
|
||||
return abi;
|
||||
}
|
||||
|
||||
Json::Value InterfaceHandler::userDocumentation(ContractDefinition const& _contractDef)
|
||||
{
|
||||
Json::Value doc;
|
||||
Json::Value methods(Json::objectValue);
|
||||
|
||||
for (auto const& it: _contractDef.interfaceFunctions())
|
||||
if (it.second->hasDeclaration())
|
||||
if (auto const* f = dynamic_cast<FunctionDefinition const*>(&it.second->declaration()))
|
||||
{
|
||||
string value = extractDoc(f->annotation().docTags, "notice");
|
||||
if (!value.empty())
|
||||
{
|
||||
Json::Value user;
|
||||
// since @notice is the only user tag if missing function should not appear
|
||||
user["notice"] = Json::Value(value);
|
||||
methods[it.second->externalSignature()] = user;
|
||||
}
|
||||
}
|
||||
doc["methods"] = methods;
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
Json::Value InterfaceHandler::devDocumentation(ContractDefinition const& _contractDef)
|
||||
{
|
||||
Json::Value doc;
|
||||
Json::Value methods(Json::objectValue);
|
||||
|
||||
auto author = extractDoc(_contractDef.annotation().docTags, "author");
|
||||
if (!author.empty())
|
||||
doc["author"] = author;
|
||||
auto title = extractDoc(_contractDef.annotation().docTags, "title");
|
||||
if (!title.empty())
|
||||
doc["title"] = title;
|
||||
|
||||
for (auto const& it: _contractDef.interfaceFunctions())
|
||||
{
|
||||
if (!it.second->hasDeclaration())
|
||||
continue;
|
||||
Json::Value method;
|
||||
if (auto fun = dynamic_cast<FunctionDefinition const*>(&it.second->declaration()))
|
||||
{
|
||||
auto dev = extractDoc(fun->annotation().docTags, "dev");
|
||||
if (!dev.empty())
|
||||
method["details"] = Json::Value(dev);
|
||||
|
||||
auto author = extractDoc(fun->annotation().docTags, "author");
|
||||
if (!author.empty())
|
||||
method["author"] = author;
|
||||
|
||||
auto ret = extractDoc(fun->annotation().docTags, "return");
|
||||
if (!ret.empty())
|
||||
method["return"] = ret;
|
||||
|
||||
Json::Value params(Json::objectValue);
|
||||
auto paramRange = fun->annotation().docTags.equal_range("param");
|
||||
for (auto i = paramRange.first; i != paramRange.second; ++i)
|
||||
params[i->second.paramName] = Json::Value(i->second.content);
|
||||
|
||||
if (!params.empty())
|
||||
method["params"] = params;
|
||||
|
||||
if (!method.empty())
|
||||
// add the function, only if we have any documentation to add
|
||||
methods[it.second->externalSignature()] = method;
|
||||
}
|
||||
}
|
||||
doc["methods"] = methods;
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
Json::Value InterfaceHandler::formatTypeList(
|
||||
vector<string> const& _names,
|
||||
vector<TypePointer> const& _types,
|
||||
bool _forLibrary
|
||||
)
|
||||
{
|
||||
Json::Value params(Json::arrayValue);
|
||||
solAssert(_names.size() == _types.size(), "Names and types vector size does not match");
|
||||
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);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
string InterfaceHandler::extractDoc(multimap<string, DocTag> const& _tags, string const& _name)
|
||||
{
|
||||
string value;
|
||||
auto range = _tags.equal_range(_name);
|
||||
for (auto i = range.first; i != range.second; i++)
|
||||
value += i->second.content;
|
||||
return value;
|
||||
}
|
130
libsolidity/interface/Natspec.cpp
Normal file
130
libsolidity/interface/Natspec.cpp
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
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 Lefteris <lefteris@ethdev.com>
|
||||
* @date 2014
|
||||
* Takes the parsed AST and produces the Natspec documentation:
|
||||
* https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format
|
||||
*
|
||||
* Can generally deal with JSON files
|
||||
*/
|
||||
|
||||
#include <libsolidity/interface/Natspec.h>
|
||||
#include <boost/range/irange.hpp>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/interface/CompilerStack.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::solidity;
|
||||
|
||||
Json::Value Natspec::documentation(
|
||||
ContractDefinition const& _contractDef,
|
||||
DocumentationType _type
|
||||
)
|
||||
{
|
||||
switch(_type)
|
||||
{
|
||||
case DocumentationType::NatspecUser:
|
||||
return userDocumentation(_contractDef);
|
||||
case DocumentationType::NatspecDev:
|
||||
return devDocumentation(_contractDef);
|
||||
}
|
||||
|
||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type"));
|
||||
}
|
||||
|
||||
Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef)
|
||||
{
|
||||
Json::Value doc;
|
||||
Json::Value methods(Json::objectValue);
|
||||
|
||||
for (auto const& it: _contractDef.interfaceFunctions())
|
||||
if (it.second->hasDeclaration())
|
||||
if (auto const* f = dynamic_cast<FunctionDefinition const*>(&it.second->declaration()))
|
||||
{
|
||||
string value = extractDoc(f->annotation().docTags, "notice");
|
||||
if (!value.empty())
|
||||
{
|
||||
Json::Value user;
|
||||
// since @notice is the only user tag if missing function should not appear
|
||||
user["notice"] = Json::Value(value);
|
||||
methods[it.second->externalSignature()] = user;
|
||||
}
|
||||
}
|
||||
doc["methods"] = methods;
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef)
|
||||
{
|
||||
Json::Value doc;
|
||||
Json::Value methods(Json::objectValue);
|
||||
|
||||
auto author = extractDoc(_contractDef.annotation().docTags, "author");
|
||||
if (!author.empty())
|
||||
doc["author"] = author;
|
||||
auto title = extractDoc(_contractDef.annotation().docTags, "title");
|
||||
if (!title.empty())
|
||||
doc["title"] = title;
|
||||
|
||||
for (auto const& it: _contractDef.interfaceFunctions())
|
||||
{
|
||||
if (!it.second->hasDeclaration())
|
||||
continue;
|
||||
Json::Value method;
|
||||
if (auto fun = dynamic_cast<FunctionDefinition const*>(&it.second->declaration()))
|
||||
{
|
||||
auto dev = extractDoc(fun->annotation().docTags, "dev");
|
||||
if (!dev.empty())
|
||||
method["details"] = Json::Value(dev);
|
||||
|
||||
auto author = extractDoc(fun->annotation().docTags, "author");
|
||||
if (!author.empty())
|
||||
method["author"] = author;
|
||||
|
||||
auto ret = extractDoc(fun->annotation().docTags, "return");
|
||||
if (!ret.empty())
|
||||
method["return"] = ret;
|
||||
|
||||
Json::Value params(Json::objectValue);
|
||||
auto paramRange = fun->annotation().docTags.equal_range("param");
|
||||
for (auto i = paramRange.first; i != paramRange.second; ++i)
|
||||
params[i->second.paramName] = Json::Value(i->second.content);
|
||||
|
||||
if (!params.empty())
|
||||
method["params"] = params;
|
||||
|
||||
if (!method.empty())
|
||||
// add the function, only if we have any documentation to add
|
||||
methods[it.second->externalSignature()] = method;
|
||||
}
|
||||
}
|
||||
doc["methods"] = methods;
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
string Natspec::extractDoc(multimap<string, DocTag> const& _tags, string const& _name)
|
||||
{
|
||||
string value;
|
||||
auto range = _tags.equal_range(_name);
|
||||
for (auto i = range.first; i != range.second; i++)
|
||||
value += i->second.content;
|
||||
return value;
|
||||
}
|
@ -17,8 +17,7 @@
|
||||
/**
|
||||
* @author Lefteris <lefteris@ethdev.com>
|
||||
* @date 2014
|
||||
* Takes the parsed AST and produces the Natspec
|
||||
* documentation and the ABI interface
|
||||
* Takes the parsed AST and produces the Natspec documentation:
|
||||
* https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format
|
||||
*
|
||||
* Can generally deal with JSON files
|
||||
@ -59,7 +58,7 @@ enum class CommentOwner
|
||||
Function
|
||||
};
|
||||
|
||||
class InterfaceHandler
|
||||
class Natspec
|
||||
{
|
||||
public:
|
||||
/// Get the given type of documentation
|
||||
@ -71,10 +70,6 @@ public:
|
||||
ContractDefinition const& _contractDef,
|
||||
DocumentationType _type
|
||||
);
|
||||
/// Get the ABI Interface of the contract
|
||||
/// @param _contractDef The contract definition
|
||||
/// @return A JSONrepresentation of the contract's ABI Interface
|
||||
static Json::Value abiInterface(ContractDefinition const& _contractDef);
|
||||
/// Get the User documentation of the contract
|
||||
/// @param _contractDef The contract definition
|
||||
/// @return A JSON representation of the contract's user documentation
|
@ -391,10 +391,10 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
|
||||
|
||||
// ABI, documentation and metadata
|
||||
Json::Value contractData(Json::objectValue);
|
||||
contractData["abi"] = m_compilerStack.metadata(contractName, DocumentationType::ABIInterface);
|
||||
contractData["abi"] = m_compilerStack.contractABI(contractName);
|
||||
contractData["metadata"] = m_compilerStack.onChainMetadata(contractName);
|
||||
contractData["userdoc"] = m_compilerStack.metadata(contractName, DocumentationType::NatspecUser);
|
||||
contractData["devdoc"] = m_compilerStack.metadata(contractName, DocumentationType::NatspecDev);
|
||||
contractData["userdoc"] = m_compilerStack.natspec(contractName, DocumentationType::NatspecUser);
|
||||
contractData["devdoc"] = m_compilerStack.natspec(contractName, DocumentationType::NatspecDev);
|
||||
|
||||
// EVM
|
||||
Json::Value evmData(Json::objectValue);
|
||||
|
@ -27,7 +27,6 @@
|
||||
#include <libsolidity/parsing/Scanner.h>
|
||||
#include <libsolidity/inlineasm/AsmParser.h>
|
||||
#include <libsolidity/interface/Exceptions.h>
|
||||
#include <libsolidity/interface/InterfaceHandler.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -266,24 +266,31 @@ void CommandLineInterface::handleOnChainMetadata(string const& _contract)
|
||||
return;
|
||||
|
||||
string data = m_compiler->onChainMetadata(_contract);
|
||||
if (m_args.count("output-dir"))
|
||||
if (m_args.count(g_argOutputDir))
|
||||
createFile(m_compiler->filesystemFriendlyName(_contract) + "_meta.json", data);
|
||||
else
|
||||
cout << "Metadata: " << endl << data << endl;
|
||||
}
|
||||
|
||||
void CommandLineInterface::handleMeta(DocumentationType _type, string const& _contract)
|
||||
void CommandLineInterface::handleABI(string const& _contract)
|
||||
{
|
||||
if (!m_args.count(g_argAbi))
|
||||
return;
|
||||
|
||||
string data = dev::jsonCompactPrint(m_compiler->contractABI(_contract));
|
||||
if (m_args.count(g_argOutputDir))
|
||||
createFile(m_compiler->filesystemFriendlyName(_contract) + ".abi", data);
|
||||
else
|
||||
cout << "Contract JSON ABI " << endl << data << endl;
|
||||
}
|
||||
|
||||
void CommandLineInterface::handleNatspec(DocumentationType _type, string const& _contract)
|
||||
{
|
||||
std::string argName;
|
||||
std::string suffix;
|
||||
std::string title;
|
||||
switch(_type)
|
||||
{
|
||||
case DocumentationType::ABIInterface:
|
||||
argName = g_argAbi;
|
||||
suffix = ".abi";
|
||||
title = "Contract JSON ABI";
|
||||
break;
|
||||
case DocumentationType::NatspecUser:
|
||||
argName = g_argNatspecUser;
|
||||
suffix = ".docuser";
|
||||
@ -301,11 +308,7 @@ void CommandLineInterface::handleMeta(DocumentationType _type, string const& _co
|
||||
|
||||
if (m_args.count(argName))
|
||||
{
|
||||
std::string output;
|
||||
if (_type == DocumentationType::ABIInterface)
|
||||
output = dev::jsonCompactPrint(m_compiler->metadata(_contract, _type));
|
||||
else
|
||||
output = dev::jsonPrettyPrint(m_compiler->metadata(_contract, _type));
|
||||
std::string output = dev::jsonPrettyPrint(m_compiler->natspec(_contract, _type));
|
||||
|
||||
if (m_args.count(g_argOutputDir))
|
||||
createFile(m_compiler->filesystemFriendlyName(_contract) + suffix, output);
|
||||
@ -787,7 +790,7 @@ void CommandLineInterface::handleCombinedJSON()
|
||||
{
|
||||
Json::Value contractData(Json::objectValue);
|
||||
if (requests.count(g_strAbi))
|
||||
contractData[g_strAbi] = dev::jsonCompactPrint(m_compiler->interface(contractName));
|
||||
contractData[g_strAbi] = dev::jsonCompactPrint(m_compiler->contractABI(contractName));
|
||||
if (requests.count("metadata"))
|
||||
contractData["metadata"] = m_compiler->onChainMetadata(contractName);
|
||||
if (requests.count(g_strBinary))
|
||||
@ -814,9 +817,9 @@ void CommandLineInterface::handleCombinedJSON()
|
||||
contractData[g_strSrcMapRuntime] = map ? *map : "";
|
||||
}
|
||||
if (requests.count(g_strNatspecDev))
|
||||
contractData[g_strNatspecDev] = dev::jsonCompactPrint(m_compiler->metadata(contractName, DocumentationType::NatspecDev));
|
||||
contractData[g_strNatspecDev] = dev::jsonCompactPrint(m_compiler->natspec(contractName, DocumentationType::NatspecDev));
|
||||
if (requests.count(g_strNatspecUser))
|
||||
contractData[g_strNatspecUser] = dev::jsonCompactPrint(m_compiler->metadata(contractName, DocumentationType::NatspecUser));
|
||||
contractData[g_strNatspecUser] = dev::jsonCompactPrint(m_compiler->natspec(contractName, DocumentationType::NatspecUser));
|
||||
output[g_strContracts][contractName] = contractData;
|
||||
}
|
||||
|
||||
@ -1069,9 +1072,9 @@ void CommandLineInterface::outputCompilationResults()
|
||||
handleBytecode(contract);
|
||||
handleSignatureHashes(contract);
|
||||
handleOnChainMetadata(contract);
|
||||
handleMeta(DocumentationType::ABIInterface, contract);
|
||||
handleMeta(DocumentationType::NatspecDev, contract);
|
||||
handleMeta(DocumentationType::NatspecUser, contract);
|
||||
handleABI(contract);
|
||||
handleNatspec(DocumentationType::NatspecDev, contract);
|
||||
handleNatspec(DocumentationType::NatspecUser, contract);
|
||||
} // end of contracts iteration
|
||||
|
||||
handleFormal();
|
||||
|
@ -67,7 +67,8 @@ private:
|
||||
void handleBytecode(std::string const& _contract);
|
||||
void handleSignatureHashes(std::string const& _contract);
|
||||
void handleOnChainMetadata(std::string const& _contract);
|
||||
void handleMeta(DocumentationType _type, std::string const& _contract);
|
||||
void handleABI(std::string const& _contract);
|
||||
void handleNatspec(DocumentationType _type, std::string const& _contract);
|
||||
void handleGasEstimation(std::string const& _contract);
|
||||
void handleFormal();
|
||||
|
||||
|
@ -193,7 +193,7 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback
|
||||
for (string const& contractName: compiler.contractNames())
|
||||
{
|
||||
Json::Value contractData(Json::objectValue);
|
||||
contractData["interface"] = dev::jsonCompactPrint(compiler.interface(contractName));
|
||||
contractData["interface"] = dev::jsonCompactPrint(compiler.contractABI(contractName));
|
||||
contractData["bytecode"] = compiler.object(contractName).toHex();
|
||||
contractData["runtimeBytecode"] = compiler.runtimeObject(contractName).toHex();
|
||||
contractData["opcodes"] = solidity::disassemble(compiler.object(contractName).bytecode);
|
||||
|
@ -44,7 +44,7 @@ public:
|
||||
{
|
||||
ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parseAndAnalyze("pragma solidity >=0.0;\n" + _code), "Parsing contract failed");
|
||||
|
||||
Json::Value generatedInterface = m_compilerStack.metadata("", DocumentationType::ABIInterface);
|
||||
Json::Value generatedInterface = m_compilerStack.contractABI("");
|
||||
Json::Value expectedInterface;
|
||||
m_reader.parse(_expectedInterfaceString, expectedInterface);
|
||||
BOOST_CHECK_MESSAGE(
|
||||
|
@ -49,9 +49,9 @@ public:
|
||||
|
||||
Json::Value generatedDocumentation;
|
||||
if (_userDocumentation)
|
||||
generatedDocumentation = m_compilerStack.metadata("", DocumentationType::NatspecUser);
|
||||
generatedDocumentation = m_compilerStack.natspec("", DocumentationType::NatspecUser);
|
||||
else
|
||||
generatedDocumentation = m_compilerStack.metadata("", DocumentationType::NatspecDev);
|
||||
generatedDocumentation = m_compilerStack.natspec("", DocumentationType::NatspecDev);
|
||||
Json::Value expectedDocumentation;
|
||||
m_reader.parse(_expectedDocumentationString, expectedDocumentation);
|
||||
BOOST_CHECK_MESSAGE(
|
||||
|
Loading…
Reference in New Issue
Block a user