2014-12-04 16:19:47 +00:00
|
|
|
|
2014-12-03 15:40:37 +00:00
|
|
|
#include <libsolidity/InterfaceHandler.h>
|
|
|
|
#include <libsolidity/AST.h>
|
|
|
|
#include <libsolidity/CompilerStack.h>
|
|
|
|
|
2014-12-04 22:55:47 +00:00
|
|
|
namespace dev
|
|
|
|
{
|
|
|
|
namespace solidity
|
|
|
|
{
|
2014-12-03 15:40:37 +00:00
|
|
|
|
2014-12-03 16:46:04 +00:00
|
|
|
/* -- public -- */
|
|
|
|
|
2014-12-03 15:40:37 +00:00
|
|
|
InterfaceHandler::InterfaceHandler()
|
|
|
|
{
|
2014-12-04 08:42:38 +00:00
|
|
|
m_lastTag = DOCTAG_NONE;
|
2014-12-03 15:40:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<std::string> InterfaceHandler::getDocumentation(std::shared_ptr<ContractDefinition> _contractDef,
|
2014-12-04 22:55:47 +00:00
|
|
|
enum DocumentationType _type)
|
2014-12-03 15:40:37 +00:00
|
|
|
{
|
|
|
|
switch(_type)
|
|
|
|
{
|
|
|
|
case NATSPEC_USER:
|
|
|
|
return getUserDocumentation(_contractDef);
|
|
|
|
case NATSPEC_DEV:
|
|
|
|
return getDevDocumentation(_contractDef);
|
|
|
|
case ABI_INTERFACE:
|
|
|
|
return getABIInterface(_contractDef);
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Internal error"));
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<std::string> InterfaceHandler::getABIInterface(std::shared_ptr<ContractDefinition> _contractDef)
|
|
|
|
{
|
|
|
|
Json::Value methods(Json::arrayValue);
|
|
|
|
|
2014-12-04 22:55:47 +00:00
|
|
|
for (FunctionDefinition const* f: _contractDef->getInterfaceFunctions())
|
2014-12-03 15:40:37 +00:00
|
|
|
{
|
|
|
|
Json::Value method;
|
|
|
|
Json::Value inputs(Json::arrayValue);
|
|
|
|
Json::Value outputs(Json::arrayValue);
|
|
|
|
|
2014-12-04 00:27:38 +00:00
|
|
|
auto populateParameters = [](std::vector<ASTPointer<VariableDeclaration>> const& _vars)
|
2014-12-03 15:40:37 +00:00
|
|
|
{
|
|
|
|
Json::Value params(Json::arrayValue);
|
|
|
|
for (ASTPointer<VariableDeclaration> const& var: _vars)
|
|
|
|
{
|
|
|
|
Json::Value input;
|
|
|
|
input["name"] = var->getName();
|
|
|
|
input["type"] = var->getType()->toString();
|
|
|
|
params.append(input);
|
|
|
|
}
|
|
|
|
return params;
|
|
|
|
};
|
|
|
|
|
|
|
|
method["name"] = f->getName();
|
2014-12-04 00:27:38 +00:00
|
|
|
method["inputs"] = populateParameters(f->getParameters());
|
|
|
|
method["outputs"] = populateParameters(f->getReturnParameters());
|
2014-12-03 15:40:37 +00:00
|
|
|
methods.append(method);
|
|
|
|
}
|
|
|
|
return std::unique_ptr<std::string>(new std::string(m_writer.write(methods)));
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(std::shared_ptr<ContractDefinition> _contractDef)
|
|
|
|
{
|
|
|
|
Json::Value doc;
|
|
|
|
Json::Value methods(Json::objectValue);
|
|
|
|
|
|
|
|
for (FunctionDefinition const* f: _contractDef->getInterfaceFunctions())
|
|
|
|
{
|
|
|
|
Json::Value user;
|
|
|
|
auto strPtr = f->getDocumentation();
|
|
|
|
if (strPtr)
|
|
|
|
{
|
2014-12-04 16:19:47 +00:00
|
|
|
resetUser();
|
2014-12-04 08:42:38 +00:00
|
|
|
parseDocString(*strPtr);
|
2014-12-04 17:12:52 +00:00
|
|
|
if (!m_notice.empty())
|
|
|
|
{// since @notice is the only user tag if missing function should not appear
|
|
|
|
user["notice"] = Json::Value(m_notice);
|
|
|
|
methods[f->getName()] = user;
|
|
|
|
}
|
2014-12-03 15:40:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
doc["methods"] = methods;
|
|
|
|
|
|
|
|
return std::unique_ptr<std::string>(new std::string(m_writer.write(doc)));
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(std::shared_ptr<ContractDefinition> _contractDef)
|
|
|
|
{
|
2014-12-04 17:12:52 +00:00
|
|
|
// LTODO: Somewhere in this function warnings for mismatch of param names
|
|
|
|
// should be thrown
|
2014-12-03 16:46:04 +00:00
|
|
|
Json::Value doc;
|
|
|
|
Json::Value methods(Json::objectValue);
|
|
|
|
|
|
|
|
for (FunctionDefinition const* f: _contractDef->getInterfaceFunctions())
|
|
|
|
{
|
|
|
|
Json::Value method;
|
|
|
|
auto strPtr = f->getDocumentation();
|
|
|
|
if (strPtr)
|
|
|
|
{
|
2014-12-04 16:19:47 +00:00
|
|
|
resetDev();
|
2014-12-03 16:46:04 +00:00
|
|
|
parseDocString(*strPtr);
|
|
|
|
|
2014-12-04 17:12:52 +00:00
|
|
|
if (!m_dev.empty())
|
|
|
|
method["details"] = Json::Value(m_dev);
|
2014-12-04 16:19:47 +00:00
|
|
|
Json::Value params(Json::objectValue);
|
|
|
|
for (auto const& pair: m_params)
|
|
|
|
params[pair.first] = pair.second;
|
2014-12-04 22:55:47 +00:00
|
|
|
|
2014-12-04 17:12:52 +00:00
|
|
|
if (!m_params.empty())
|
|
|
|
method["params"] = params;
|
|
|
|
if (!m_return.empty())
|
|
|
|
method["return"] = m_return;
|
|
|
|
|
|
|
|
if (!method.empty()) // add the function, only if we have any documentation to add
|
|
|
|
methods[f->getName()] = method;
|
2014-12-03 16:46:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
doc["methods"] = methods;
|
|
|
|
|
|
|
|
return std::unique_ptr<std::string>(new std::string(m_writer.write(doc)));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -- private -- */
|
2014-12-04 16:19:47 +00:00
|
|
|
void InterfaceHandler::resetUser()
|
|
|
|
{
|
|
|
|
m_notice.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void InterfaceHandler::resetDev()
|
|
|
|
{
|
|
|
|
m_dev.clear();
|
2014-12-04 17:12:52 +00:00
|
|
|
m_return.clear();
|
2014-12-04 16:19:47 +00:00
|
|
|
m_params.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t skipLineOrEOS(std::string const& _string, size_t _nlPos)
|
|
|
|
{
|
|
|
|
return (_nlPos == std::string::npos) ? _string.length() : _nlPos + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t InterfaceHandler::parseDocTagLine(std::string const& _string,
|
|
|
|
std::string& _tagString,
|
|
|
|
size_t _pos,
|
2014-12-04 22:55:47 +00:00
|
|
|
enum DocTagType _tagType)
|
2014-12-04 16:19:47 +00:00
|
|
|
{
|
2014-12-04 22:55:47 +00:00
|
|
|
size_t nlPos = _string.find('\n', _pos);
|
2014-12-04 16:19:47 +00:00
|
|
|
_tagString += _string.substr(_pos,
|
|
|
|
nlPos == std::string::npos ?
|
2014-12-04 22:55:47 +00:00
|
|
|
_string.length() - _pos:
|
2014-12-04 16:19:47 +00:00
|
|
|
nlPos - _pos);
|
|
|
|
m_lastTag = _tagType;
|
|
|
|
return skipLineOrEOS(_string, nlPos);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t InterfaceHandler::parseDocTagParam(std::string const& _string, size_t _startPos)
|
|
|
|
{
|
|
|
|
// find param name
|
2014-12-04 22:55:47 +00:00
|
|
|
size_t currPos = _string.find(' ', _startPos);
|
2014-12-04 16:19:47 +00:00
|
|
|
if (currPos == std::string::npos)
|
|
|
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("End of param name not found"));
|
2014-12-04 22:55:47 +00:00
|
|
|
|
2014-12-04 16:19:47 +00:00
|
|
|
|
|
|
|
auto paramName = _string.substr(_startPos, currPos - _startPos);
|
|
|
|
|
|
|
|
currPos += 1;
|
2014-12-04 22:55:47 +00:00
|
|
|
size_t nlPos = _string.find('\n', currPos);
|
2014-12-04 16:19:47 +00:00
|
|
|
auto paramDesc = _string.substr(currPos,
|
|
|
|
nlPos == std::string::npos ?
|
2014-12-04 22:55:47 +00:00
|
|
|
_string.length() - currPos :
|
2014-12-04 16:19:47 +00:00
|
|
|
nlPos - currPos);
|
|
|
|
|
|
|
|
m_params.push_back(std::make_pair(paramName, paramDesc));
|
|
|
|
|
|
|
|
m_lastTag = DOCTAG_PARAM;
|
|
|
|
return skipLineOrEOS(_string, nlPos);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t InterfaceHandler::appendDocTagParam(std::string const& _string, size_t _startPos)
|
|
|
|
{
|
|
|
|
// Should never be called with an empty vector
|
2014-12-04 22:55:47 +00:00
|
|
|
if (asserts(!m_params.empty()))
|
|
|
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Internal: Tried to append to empty parameter"));
|
2014-12-04 16:19:47 +00:00
|
|
|
auto pair = m_params.back();
|
2014-12-04 22:55:47 +00:00
|
|
|
size_t nlPos = _string.find('\n', _startPos);
|
2014-12-04 16:19:47 +00:00
|
|
|
pair.second += _string.substr(_startPos,
|
|
|
|
nlPos == std::string::npos ?
|
2014-12-04 22:55:47 +00:00
|
|
|
_string.length() - _startPos :
|
2014-12-04 16:19:47 +00:00
|
|
|
nlPos - _startPos);
|
|
|
|
|
|
|
|
m_params.at(m_params.size() - 1) = pair;
|
|
|
|
|
|
|
|
return skipLineOrEOS(_string, nlPos);
|
|
|
|
}
|
|
|
|
|
2014-12-03 16:46:04 +00:00
|
|
|
size_t InterfaceHandler::parseDocTag(std::string const& _string, std::string const& _tag, size_t _pos)
|
|
|
|
{
|
2014-12-04 17:12:52 +00:00
|
|
|
// LTODO: need to check for @(start of a tag) between here and the end of line
|
2014-12-04 16:19:47 +00:00
|
|
|
// for all cases
|
2014-12-03 16:46:04 +00:00
|
|
|
size_t nlPos = _pos;
|
2014-12-04 08:42:38 +00:00
|
|
|
if (m_lastTag == DOCTAG_NONE || _tag != "")
|
2014-12-03 16:46:04 +00:00
|
|
|
{
|
2014-12-04 08:42:38 +00:00
|
|
|
if (_tag == "dev")
|
2014-12-04 16:19:47 +00:00
|
|
|
nlPos = parseDocTagLine(_string, m_dev, _pos, DOCTAG_DEV);
|
2014-12-04 08:42:38 +00:00
|
|
|
else if (_tag == "notice")
|
2014-12-04 16:19:47 +00:00
|
|
|
nlPos = parseDocTagLine(_string, m_notice, _pos, DOCTAG_NOTICE);
|
2014-12-04 17:12:52 +00:00
|
|
|
else if (_tag == "return")
|
|
|
|
nlPos = parseDocTagLine(_string, m_return, _pos, DOCTAG_RETURN);
|
2014-12-04 16:19:47 +00:00
|
|
|
else if (_tag == "param")
|
|
|
|
nlPos = parseDocTagParam(_string, _pos);
|
2014-12-04 08:42:38 +00:00
|
|
|
else
|
|
|
|
{
|
2014-12-04 17:12:52 +00:00
|
|
|
// LTODO: Unknown tas, throw some form of warning
|
2014-12-04 08:42:38 +00:00
|
|
|
}
|
2014-12-03 16:46:04 +00:00
|
|
|
}
|
|
|
|
else
|
2014-12-04 16:19:47 +00:00
|
|
|
appendDocTag(_string, _pos);
|
|
|
|
|
|
|
|
return nlPos;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t InterfaceHandler::appendDocTag(std::string const& _string, size_t _startPos)
|
|
|
|
{
|
|
|
|
size_t newPos = _startPos;
|
2014-12-04 22:55:47 +00:00
|
|
|
switch (m_lastTag)
|
2014-12-03 16:46:04 +00:00
|
|
|
{
|
2014-12-04 08:42:38 +00:00
|
|
|
case DOCTAG_DEV:
|
2014-12-04 16:19:47 +00:00
|
|
|
m_dev += " ";
|
|
|
|
newPos = parseDocTagLine(_string, m_dev, _startPos, DOCTAG_DEV);
|
2014-12-04 08:42:38 +00:00
|
|
|
break;
|
|
|
|
case DOCTAG_NOTICE:
|
2014-12-04 16:19:47 +00:00
|
|
|
m_notice += " ";
|
|
|
|
newPos = parseDocTagLine(_string, m_notice, _startPos, DOCTAG_NOTICE);
|
|
|
|
break;
|
2014-12-04 17:12:52 +00:00
|
|
|
case DOCTAG_RETURN:
|
|
|
|
m_return += " ";
|
|
|
|
newPos = parseDocTagLine(_string, m_return, _startPos, DOCTAG_RETURN);
|
|
|
|
break;
|
2014-12-04 16:19:47 +00:00
|
|
|
case DOCTAG_PARAM:
|
|
|
|
newPos = appendDocTagParam(_string, _startPos);
|
2014-12-04 08:42:38 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2014-12-04 16:19:47 +00:00
|
|
|
return newPos;
|
2014-12-03 16:46:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void InterfaceHandler::parseDocString(std::string const& _string, size_t _startPos)
|
|
|
|
{
|
|
|
|
size_t pos2;
|
2014-12-04 08:42:38 +00:00
|
|
|
size_t newPos = _startPos;
|
2014-12-04 22:55:47 +00:00
|
|
|
size_t tagPos = _string.find('@', _startPos);
|
|
|
|
size_t nlPos = _string.find('\n', _startPos);
|
2014-12-03 16:46:04 +00:00
|
|
|
|
2014-12-04 16:19:47 +00:00
|
|
|
if (tagPos != std::string::npos && tagPos < nlPos)
|
2014-12-04 08:42:38 +00:00
|
|
|
{
|
|
|
|
// we found a tag
|
2014-12-04 22:55:47 +00:00
|
|
|
pos2 = _string.find(' ', tagPos);
|
2014-12-04 08:42:38 +00:00
|
|
|
if (pos2 == std::string::npos)
|
|
|
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("End of tag not found"));
|
2014-12-03 16:46:04 +00:00
|
|
|
|
2014-12-04 16:19:47 +00:00
|
|
|
newPos = parseDocTag(_string, _string.substr(tagPos + 1, pos2 - tagPos - 1), pos2 + 1);
|
2014-12-04 08:42:38 +00:00
|
|
|
}
|
2014-12-04 16:19:47 +00:00
|
|
|
else if (m_lastTag != DOCTAG_NONE) // continuation of the previous tag
|
|
|
|
newPos = appendDocTag(_string, _startPos + 1);
|
|
|
|
else // skip the line if a newline was found
|
2014-12-04 08:42:38 +00:00
|
|
|
{
|
2014-12-04 16:19:47 +00:00
|
|
|
if (newPos != std::string::npos)
|
|
|
|
newPos = nlPos + 1;
|
2014-12-04 08:42:38 +00:00
|
|
|
}
|
2014-12-04 16:19:47 +00:00
|
|
|
if (newPos == std::string::npos || newPos == _string.length())
|
2014-12-04 08:42:38 +00:00
|
|
|
return; // EOS
|
2014-12-03 16:46:04 +00:00
|
|
|
parseDocString(_string, newPos);
|
2014-12-03 15:40:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} //solidity NS
|
|
|
|
} // dev NS
|