Browse Source

Merge branch 'develop-evmcc' of https://github.com/imapp-pl/cpp-ethereum into develop-evmcc

cl-refactor
Artur Zawłocki 10 years ago
parent
commit
3e5988c7f8
  1. 1
      alethzero/MainWin.cpp
  2. 2
      libethcore/CommonEth.cpp
  3. 1
      libevm/ExtVMFace.h
  4. 2
      libsolidity/AST.h
  5. 4
      libsolidity/CMakeLists.txt
  6. 60
      libsolidity/CompilerStack.cpp
  7. 20
      libsolidity/CompilerStack.h
  8. 1
      libsolidity/Exceptions.h
  9. 278
      libsolidity/InterfaceHandler.cpp
  10. 110
      libsolidity/InterfaceHandler.h
  11. 4
      solc/main.cpp
  12. 43
      test/solidityJSONInterfaceTest.cpp
  13. 400
      test/solidityNatspecJSON.cpp

1
alethzero/MainWin.cpp

@ -1285,6 +1285,7 @@ void Main::on_blocks_currentItemChanged()
s << "<br/>" << sha3(i.data()).abridged();// << ": <b>" << i[1].toHash<h256>() << "</b> [<b>" << i[2].toInt<u256>() << "</b> used]"; s << "<br/>" << sha3(i.data()).abridged();// << ": <b>" << i[1].toHash<h256>() << "</b> [<b>" << i[2].toInt<u256>() << "</b> used]";
s << "<br/>Post: <b>" << info.stateRoot << "</b>"; s << "<br/>Post: <b>" << info.stateRoot << "</b>";
s << "<br/>Dump: <span style=\"font-family: Monospace,Lucida Console,Courier,Courier New,sans-serif; font-size: small\">" << toHex(block[0].data()) << "</span>"; s << "<br/>Dump: <span style=\"font-family: Monospace,Lucida Console,Courier,Courier New,sans-serif; font-size: small\">" << toHex(block[0].data()) << "</span>";
s << "<div>Receipts-Hex: <span style=\"font-family: Monospace,Lucida Console,Courier,Courier New,sans-serif; font-size: small\">" << toHex(ethereum()->blockChain().receipts(h).rlp()) << "</span></div>";
} }
else else
{ {

2
libethcore/CommonEth.cpp

@ -33,7 +33,7 @@ namespace dev
namespace eth namespace eth
{ {
const unsigned c_protocolVersion = 47; const unsigned c_protocolVersion = 48;
const unsigned c_databaseVersion = 5; const unsigned c_databaseVersion = 5;
static const vector<pair<u256, string>> g_units = static const vector<pair<u256, string>> g_units =

1
libevm/ExtVMFace.h

@ -80,6 +80,7 @@ struct SubState
{ {
suicides += _s.suicides; suicides += _s.suicides;
refunds += _s.refunds; refunds += _s.refunds;
logs += _s.logs;
return *this; return *this;
} }
}; };

2
libsolidity/AST.h

@ -204,7 +204,7 @@ public:
Block& getBody() { return *m_body; } Block& getBody() { return *m_body; }
/// @return A shared pointer of an ASTString. /// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation /// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> const& getDocumentation() { return m_documentation; } ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); } void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); }
std::vector<VariableDeclaration const*> const& getLocalVariables() const { return m_localVariables; } std::vector<VariableDeclaration const*> const& getLocalVariables() const { return m_localVariables; }

4
libsolidity/CMakeLists.txt

@ -16,6 +16,10 @@ endif()
include_directories(..) include_directories(..)
target_link_libraries(${EXECUTABLE} evmcore devcore) target_link_libraries(${EXECUTABLE} evmcore devcore)
# TODO: Temporary until PR 532 https://github.com/ethereum/cpp-ethereum/pull/532
# gets accepted. Then we can simply add jsoncpp as a dependency and not the
# whole of JSONRPC as we are doing right here
target_link_libraries(${EXECUTABLE} ${JSONRPC_LS})
install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

60
libsolidity/CompilerStack.cpp

@ -27,6 +27,7 @@
#include <libsolidity/NameAndTypeResolver.h> #include <libsolidity/NameAndTypeResolver.h>
#include <libsolidity/Compiler.h> #include <libsolidity/Compiler.h>
#include <libsolidity/CompilerStack.h> #include <libsolidity/CompilerStack.h>
#include <libsolidity/InterfaceHandler.h>
using namespace std; using namespace std;
@ -35,6 +36,8 @@ namespace dev
namespace solidity namespace solidity
{ {
CompilerStack::CompilerStack(): m_interfaceHandler(make_shared<InterfaceHandler>()) {}
void CompilerStack::setSource(string const& _sourceCode) void CompilerStack::setSource(string const& _sourceCode)
{ {
reset(); reset();
@ -81,48 +84,31 @@ void CompilerStack::streamAssembly(ostream& _outStream)
m_compiler->streamAssembly(_outStream); m_compiler->streamAssembly(_outStream);
} }
string const& CompilerStack::getInterface() std::string const& CompilerStack::getJsonDocumentation(enum DocumentationType _type)
{ {
if (!m_parseSuccessful) if (!m_parseSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
if (m_interface.empty())
auto createDocIfNotThere = [this, _type](std::unique_ptr<string>& _doc)
{ {
stringstream interface; if (!_doc)
interface << '['; _doc = m_interfaceHandler->getDocumentation(m_contractASTNode, _type);
vector<FunctionDefinition const*> exportedFunctions = m_contractASTNode->getInterfaceFunctions(); };
unsigned functionsCount = exportedFunctions.size();
for (FunctionDefinition const* f: exportedFunctions) switch (_type)
{ {
auto streamVariables = [&](vector<ASTPointer<VariableDeclaration>> const& _vars) case NATSPEC_USER:
{ createDocIfNotThere(m_userDocumentation);
unsigned varCount = _vars.size(); return *m_userDocumentation;
for (ASTPointer<VariableDeclaration> const& var: _vars) case NATSPEC_DEV:
{ createDocIfNotThere(m_devDocumentation);
interface << "{" return *m_devDocumentation;
<< "\"name\":" << escaped(var->getName(), false) << "," case ABI_INTERFACE:
<< "\"type\":" << escaped(var->getType()->toString(), false) createDocIfNotThere(m_interface);
<< "}"; return *m_interface;
if (--varCount > 0)
interface << ",";
}
};
interface << '{'
<< "\"name\":" << escaped(f->getName(), false) << ","
<< "\"inputs\":[";
streamVariables(f->getParameters());
interface << "],"
<< "\"outputs\":[";
streamVariables(f->getReturnParameters());
interface << "]"
<< "}";
if (--functionsCount > 0)
interface << ",";
}
interface << ']';
m_interface = interface.str();
} }
return m_interface;
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type."));
} }
bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize) bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize)

20
libsolidity/CompilerStack.h

@ -35,6 +35,14 @@ class Scanner;
class ContractDefinition; class ContractDefinition;
class Compiler; class Compiler;
class GlobalContext; class GlobalContext;
class InterfaceHandler;
enum DocumentationType: unsigned short
{
NATSPEC_USER = 1,
NATSPEC_DEV,
ABI_INTERFACE
};
/** /**
* Easy to use and self-contained Solidity compiler with as few header dependencies as possible. * Easy to use and self-contained Solidity compiler with as few header dependencies as possible.
@ -44,7 +52,7 @@ class GlobalContext;
class CompilerStack class CompilerStack
{ {
public: public:
CompilerStack() {} CompilerStack();
void reset() { *this = CompilerStack(); } void reset() { *this = CompilerStack(); }
void setSource(std::string const& _sourceCode); void setSource(std::string const& _sourceCode);
void parse(); void parse();
@ -62,6 +70,11 @@ public:
/// Returns a string representing the contract interface in JSON. /// Returns a string representing the contract interface in JSON.
/// Prerequisite: Successful call to parse or compile. /// Prerequisite: Successful call to parse or compile.
std::string const& getInterface(); std::string const& getInterface();
/// Returns a string representing the contract's documentation in JSON.
/// Prerequisite: Successful call to parse or compile.
/// @param type The type of the documentation to get.
/// Can be one of 3 types defined at @c documentation_type
std::string const& getJsonDocumentation(enum DocumentationType type);
/// Returns the previously used scanner, useful for counting lines during error reporting. /// Returns the previously used scanner, useful for counting lines during error reporting.
Scanner const& getScanner() const { return *m_scanner; } Scanner const& getScanner() const { return *m_scanner; }
@ -76,8 +89,11 @@ private:
std::shared_ptr<GlobalContext> m_globalContext; std::shared_ptr<GlobalContext> m_globalContext;
std::shared_ptr<ContractDefinition> m_contractASTNode; std::shared_ptr<ContractDefinition> m_contractASTNode;
bool m_parseSuccessful; bool m_parseSuccessful;
std::string m_interface; std::unique_ptr<std::string> m_interface;
std::unique_ptr<std::string> m_userDocumentation;
std::unique_ptr<std::string> m_devDocumentation;
std::shared_ptr<Compiler> m_compiler; std::shared_ptr<Compiler> m_compiler;
std::shared_ptr<InterfaceHandler> m_interfaceHandler;
bytes m_bytecode; bytes m_bytecode;
}; };

1
libsolidity/Exceptions.h

@ -36,6 +36,7 @@ struct TypeError: virtual Exception {};
struct DeclarationError: virtual Exception {}; struct DeclarationError: virtual Exception {};
struct CompilerError: virtual Exception {}; struct CompilerError: virtual Exception {};
struct InternalCompilerError: virtual Exception {}; struct InternalCompilerError: virtual Exception {};
struct DocstringParsingError: virtual Exception {};
typedef boost::error_info<struct tag_sourcePosition, int> errinfo_sourcePosition; typedef boost::error_info<struct tag_sourcePosition, int> errinfo_sourcePosition;
typedef boost::error_info<struct tag_sourceLocation, Location> errinfo_sourceLocation; typedef boost::error_info<struct tag_sourceLocation, Location> errinfo_sourceLocation;

278
libsolidity/InterfaceHandler.cpp

@ -0,0 +1,278 @@
#include <libsolidity/InterfaceHandler.h>
#include <libsolidity/AST.h>
#include <libsolidity/CompilerStack.h>
namespace dev
{
namespace solidity
{
/* -- public -- */
InterfaceHandler::InterfaceHandler()
{
m_lastTag = DOCTAG_NONE;
}
std::unique_ptr<std::string> InterfaceHandler::getDocumentation(std::shared_ptr<ContractDefinition> _contractDef,
enum DocumentationType _type)
{
switch(_type)
{
case NATSPEC_USER:
return getUserDocumentation(_contractDef);
case NATSPEC_DEV:
return getDevDocumentation(_contractDef);
case ABI_INTERFACE:
return getABIInterface(_contractDef);
}
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type"));
return nullptr;
}
std::unique_ptr<std::string> InterfaceHandler::getABIInterface(std::shared_ptr<ContractDefinition> _contractDef)
{
Json::Value methods(Json::arrayValue);
for (FunctionDefinition const* f: _contractDef->getInterfaceFunctions())
{
Json::Value method;
Json::Value inputs(Json::arrayValue);
Json::Value outputs(Json::arrayValue);
auto populateParameters = [](std::vector<ASTPointer<VariableDeclaration>> const& _vars)
{
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();
method["inputs"] = populateParameters(f->getParameters());
method["outputs"] = populateParameters(f->getReturnParameters());
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)
{
resetUser();
parseDocString(*strPtr);
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;
}
}
}
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)
{
// LTODO: Somewhere in this function warnings for mismatch of param names
// should be thrown
Json::Value doc;
Json::Value methods(Json::objectValue);
for (FunctionDefinition const* f: _contractDef->getInterfaceFunctions())
{
Json::Value method;
auto strPtr = f->getDocumentation();
if (strPtr)
{
resetDev();
parseDocString(*strPtr);
if (!m_dev.empty())
method["details"] = Json::Value(m_dev);
Json::Value params(Json::objectValue);
for (auto const& pair: m_params)
params[pair.first] = pair.second;
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;
}
}
doc["methods"] = methods;
return std::unique_ptr<std::string>(new std::string(m_writer.write(doc)));
}
/* -- private -- */
void InterfaceHandler::resetUser()
{
m_notice.clear();
}
void InterfaceHandler::resetDev()
{
m_dev.clear();
m_return.clear();
m_params.clear();
}
static inline std::string::const_iterator skipLineOrEOS(std::string::const_iterator _nlPos,
std::string::const_iterator _end)
{
return (_nlPos == _end) ? _end : ++_nlPos;
}
std::string::const_iterator InterfaceHandler::parseDocTagLine(std::string::const_iterator _pos,
std::string::const_iterator _end,
std::string& _tagString,
enum DocTagType _tagType)
{
auto nlPos = std::find(_pos, _end, '\n');
std::copy(_pos, nlPos, back_inserter(_tagString));
m_lastTag = _tagType;
return skipLineOrEOS(nlPos, _end);
}
std::string::const_iterator InterfaceHandler::parseDocTagParam(std::string::const_iterator _pos,
std::string::const_iterator _end)
{
// find param name
auto currPos = std::find(_pos, _end, ' ');
if (currPos == _end)
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("End of param name not found" + std::string(_pos, _end)));
auto paramName = std::string(_pos, currPos);
currPos += 1;
auto nlPos = std::find(currPos, _end, '\n');
auto paramDesc = std::string(currPos, nlPos);
m_params.push_back(std::make_pair(paramName, paramDesc));
m_lastTag = DOCTAG_PARAM;
return skipLineOrEOS(nlPos, _end);
}
std::string::const_iterator InterfaceHandler::appendDocTagParam(std::string::const_iterator _pos,
std::string::const_iterator _end)
{
// Should never be called with an empty vector
if (asserts(!m_params.empty()))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Tried to append to empty parameter"));
auto pair = m_params.back();
pair.second += " ";
auto nlPos = std::find(_pos, _end, '\n');
std::copy(_pos, nlPos, back_inserter(pair.second));
m_params.at(m_params.size() - 1) = pair;
return skipLineOrEOS(nlPos, _end);
}
std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_iterator _pos,
std::string::const_iterator _end,
std::string const& _tag)
{
// LTODO: need to check for @(start of a tag) between here and the end of line
// for all cases
if (m_lastTag == DOCTAG_NONE || _tag != "")
{
if (_tag == "dev")
return parseDocTagLine(_pos, _end, m_dev, DOCTAG_DEV);
else if (_tag == "notice")
return parseDocTagLine(_pos, _end, m_notice, DOCTAG_NOTICE);
else if (_tag == "return")
return parseDocTagLine(_pos, _end, m_return, DOCTAG_RETURN);
else if (_tag == "param")
return parseDocTagParam(_pos, _end);
else
{
// LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("Unknown tag " + _tag + " encountered"));
}
}
else
return appendDocTag(_pos, _end);
}
std::string::const_iterator InterfaceHandler::appendDocTag(std::string::const_iterator _pos,
std::string::const_iterator _end)
{
switch (m_lastTag)
{
case DOCTAG_DEV:
m_dev += " ";
return parseDocTagLine(_pos, _end, m_dev, DOCTAG_DEV);
case DOCTAG_NOTICE:
m_notice += " ";
return parseDocTagLine(_pos, _end, m_notice, DOCTAG_NOTICE);
case DOCTAG_RETURN:
m_return += " ";
return parseDocTagLine(_pos, _end, m_return, DOCTAG_RETURN);
case DOCTAG_PARAM:
return appendDocTagParam(_pos, _end);
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Illegal documentation tag type"));
break;
}
}
static inline std::string::const_iterator getFirstSpaceOrNl(std::string::const_iterator _pos,
std::string::const_iterator _end)
{
auto spacePos = std::find(_pos, _end, ' ');
auto nlPos = std::find(_pos, _end, '\n');
return (spacePos < nlPos) ? spacePos : nlPos;
}
void InterfaceHandler::parseDocString(std::string const& _string)
{
auto currPos = _string.begin();
auto end = _string.end();
while (currPos != end)
{
auto tagPos = std::find(currPos, end, '@');
auto nlPos = std::find(currPos, end, '\n');
if (tagPos != end && tagPos < nlPos)
{
// we found a tag
auto tagNameEndPos = getFirstSpaceOrNl(tagPos, end);
if (tagNameEndPos == end)
BOOST_THROW_EXCEPTION(DocstringParsingError() <<
errinfo_comment("End of tag " + std::string(tagPos, tagNameEndPos) + "not found"));
currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos + 1, tagNameEndPos));
}
else if (m_lastTag != DOCTAG_NONE) // continuation of the previous tag
currPos = appendDocTag(currPos + 1, end);
else if (currPos != end) // skip the line if a newline was found
currPos = nlPos + 1;
}
}
} //solidity NS
} // dev NS

110
libsolidity/InterfaceHandler.h

@ -0,0 +1,110 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum 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.
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Lefteris <lefteris@ethdev.com>
* @date 2014
* Takes the parsed AST and produces the Natspec
* documentation and the ABI interface
* https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format
*
* Can generally deal with JSON files
*/
#pragma once
#include <string>
#include <memory>
#include <jsonrpc/json/json.h>
namespace dev
{
namespace solidity
{
// Forward declarations
class ContractDefinition;
enum DocumentationType: unsigned short;
enum DocTagType
{
DOCTAG_NONE = 0,
DOCTAG_DEV,
DOCTAG_NOTICE,
DOCTAG_PARAM,
DOCTAG_RETURN
};
class InterfaceHandler
{
public:
InterfaceHandler();
/// Get the given type of documentation
/// @param _contractDef The contract definition
/// @param _type The type of the documentation. Can be one of the
/// types provided by @c DocumentationType
/// @return A unique pointer contained string with the json
/// representation of provided type
std::unique_ptr<std::string> getDocumentation(std::shared_ptr<ContractDefinition> _contractDef,
enum DocumentationType _type);
/// Get the ABI Interface of the contract
/// @param _contractDef The contract definition
/// @return A unique pointer contained string with the json
/// representation of the contract's ABI Interface
std::unique_ptr<std::string> getABIInterface(std::shared_ptr<ContractDefinition> _contractDef);
/// Get the User documentation of the contract
/// @param _contractDef The contract definition
/// @return A unique pointer contained string with the json
/// representation of the contract's user documentation
std::unique_ptr<std::string> getUserDocumentation(std::shared_ptr<ContractDefinition> _contractDef);
/// Get the Developer's documentation of the contract
/// @param _contractDef The contract definition
/// @return A unique pointer contained string with the json
/// representation of the contract's developer documentation
std::unique_ptr<std::string> getDevDocumentation(std::shared_ptr<ContractDefinition> _contractDef);
private:
void resetUser();
void resetDev();
std::string::const_iterator parseDocTagLine(std::string::const_iterator _pos,
std::string::const_iterator _end,
std::string& _tagString,
enum DocTagType _tagType);
std::string::const_iterator parseDocTagParam(std::string::const_iterator _pos,
std::string::const_iterator _end);
std::string::const_iterator appendDocTagParam(std::string::const_iterator _pos,
std::string::const_iterator _end);
void parseDocString(std::string const& _string);
std::string::const_iterator appendDocTag(std::string::const_iterator _pos,
std::string::const_iterator _end);
std::string::const_iterator parseDocTag(std::string::const_iterator _pos,
std::string::const_iterator _end,
std::string const& _tag);
Json::StyledWriter m_writer;
// internal state
enum DocTagType m_lastTag;
std::string m_notice;
std::string m_dev;
std::string m_return;
std::vector<std::pair<std::string, std::string>> m_params;
};
} //solidity NS
} // dev NS

4
solc/main.cpp

@ -135,7 +135,9 @@ int main(int argc, char** argv)
cout << "Opcodes:" << endl; cout << "Opcodes:" << endl;
cout << eth::disassemble(compiler.getBytecode()) << endl; cout << eth::disassemble(compiler.getBytecode()) << endl;
cout << "Binary: " << toHex(compiler.getBytecode()) << endl; cout << "Binary: " << toHex(compiler.getBytecode()) << endl;
cout << "Interface specification: " << compiler.getInterface() << endl; cout << "Interface specification: " << compiler.getJsonDocumentation(ABI_INTERFACE) << endl;
cout << "Natspec user documentation: " << compiler.getJsonDocumentation(NATSPEC_USER) << endl;
cout << "Natspec developer documentation: " << compiler.getJsonDocumentation(NATSPEC_DEV) << endl;
return 0; return 0;
} }

43
test/solidityJSONInterfaceTest.cpp

@ -23,6 +23,7 @@
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <libsolidity/CompilerStack.h> #include <libsolidity/CompilerStack.h>
#include <jsonrpc/json/json.h> #include <jsonrpc/json/json.h>
#include <libdevcore/Exceptions.h>
namespace dev namespace dev
{ {
@ -34,23 +35,37 @@ namespace test
class InterfaceChecker class InterfaceChecker
{ {
public: public:
bool checkInterface(std::string const& _code, std::string const& _expectedInterfaceString) void checkInterface(std::string const& _code, std::string const& _expectedInterfaceString)
{ {
m_compilerStack.parse(_code); try
std::string generatedInterfaceString = m_compilerStack.getInterface(); {
m_compilerStack.parse(_code);
}
catch (const std::exception& e)
{
std::string const* extra = boost::get_error_info<errinfo_comment>(e);
std::string msg = std::string("Parsing contract failed with: ") +
e.what() + std::string("\n");
if (extra)
msg += *extra;
BOOST_FAIL(msg);
}
std::string generatedInterfaceString = m_compilerStack.getJsonDocumentation(ABI_INTERFACE);
Json::Value generatedInterface; Json::Value generatedInterface;
m_reader.parse(generatedInterfaceString, generatedInterface); m_reader.parse(generatedInterfaceString, generatedInterface);
Json::Value expectedInterface; Json::Value expectedInterface;
m_reader.parse(_expectedInterfaceString, expectedInterface); m_reader.parse(_expectedInterfaceString, expectedInterface);
return expectedInterface == generatedInterface; BOOST_CHECK_MESSAGE(expectedInterface == generatedInterface,
"Expected " << _expectedInterfaceString <<
"\n but got:\n" << generatedInterfaceString);
} }
private: private:
CompilerStack m_compilerStack; CompilerStack m_compilerStack;
Json::Reader m_reader; Json::Reader m_reader;
}; };
BOOST_FIXTURE_TEST_SUITE(SolidityCompilerJSONInterfaceOutput, InterfaceChecker) BOOST_FIXTURE_TEST_SUITE(SolidityABIJSON, InterfaceChecker)
BOOST_AUTO_TEST_CASE(basic_test) BOOST_AUTO_TEST_CASE(basic_test)
{ {
@ -76,7 +91,7 @@ BOOST_AUTO_TEST_CASE(basic_test)
} }
])"; ])";
BOOST_CHECK(checkInterface(sourceCode, interface)); checkInterface(sourceCode, interface);
} }
BOOST_AUTO_TEST_CASE(empty_contract) BOOST_AUTO_TEST_CASE(empty_contract)
@ -86,7 +101,7 @@ BOOST_AUTO_TEST_CASE(empty_contract)
char const* interface = "[]"; char const* interface = "[]";
BOOST_CHECK(checkInterface(sourceCode, interface)); checkInterface(sourceCode, interface);
} }
BOOST_AUTO_TEST_CASE(multiple_methods) BOOST_AUTO_TEST_CASE(multiple_methods)
@ -95,7 +110,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods)
" function f(uint a) returns(uint d) { return a * 7; }\n" " function f(uint a) returns(uint d) { return a * 7; }\n"
" function g(uint b) returns(uint e) { return b * 8; }\n" " function g(uint b) returns(uint e) { return b * 8; }\n"
"}\n"; "}\n";
char const* interface = R"([ char const* interface = R"([
{ {
"name": "f", "name": "f",
@ -129,7 +144,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods)
} }
])"; ])";
BOOST_CHECK(checkInterface(sourceCode, interface)); checkInterface(sourceCode, interface);
} }
BOOST_AUTO_TEST_CASE(multiple_params) BOOST_AUTO_TEST_CASE(multiple_params)
@ -160,7 +175,7 @@ BOOST_AUTO_TEST_CASE(multiple_params)
} }
])"; ])";
BOOST_CHECK(checkInterface(sourceCode, interface)); checkInterface(sourceCode, interface);
} }
BOOST_AUTO_TEST_CASE(multiple_methods_order) BOOST_AUTO_TEST_CASE(multiple_methods_order)
@ -170,7 +185,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods_order)
" function f(uint a) returns(uint d) { return a * 7; }\n" " function f(uint a) returns(uint d) { return a * 7; }\n"
" function c(uint b) returns(uint e) { return b * 8; }\n" " function c(uint b) returns(uint e) { return b * 8; }\n"
"}\n"; "}\n";
char const* interface = R"([ char const* interface = R"([
{ {
"name": "c", "name": "c",
@ -203,8 +218,8 @@ BOOST_AUTO_TEST_CASE(multiple_methods_order)
] ]
} }
])"; ])";
BOOST_CHECK(checkInterface(sourceCode, interface)); checkInterface(sourceCode, interface);
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

400
test/solidityNatspecJSON.cpp

@ -0,0 +1,400 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum 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.
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Lefteris Karapetsas <lefteris@ethdev.com>
* @date 2014
* Unit tests for the solidity compiler JSON Interface output.
*/
#include <boost/test/unit_test.hpp>
#include <libsolidity/CompilerStack.h>
#include <jsonrpc/json/json.h>
#include <libdevcore/Exceptions.h>
namespace dev
{
namespace solidity
{
namespace test
{
class DocumentationChecker
{
public:
void checkNatspec(std::string const& _code,
std::string const& _expectedDocumentationString,
bool _userDocumentation)
{
std::string generatedDocumentationString;
try
{
m_compilerStack.parse(_code);
}
catch (const std::exception& e)
{
std::string const* extra = boost::get_error_info<errinfo_comment>(e);
std::string msg = std::string("Parsing contract failed with: ") +
e.what() + std::string("\n");
if (extra)
msg += *extra;
BOOST_FAIL(msg);
}
if (_userDocumentation)
generatedDocumentationString = m_compilerStack.getJsonDocumentation(NATSPEC_USER);
else
generatedDocumentationString = m_compilerStack.getJsonDocumentation(NATSPEC_DEV);
Json::Value generatedDocumentation;
m_reader.parse(generatedDocumentationString, generatedDocumentation);
Json::Value expectedDocumentation;
m_reader.parse(_expectedDocumentationString, expectedDocumentation);
BOOST_CHECK_MESSAGE(expectedDocumentation == generatedDocumentation,
"Expected " << _expectedDocumentationString <<
"\n but got:\n" << generatedDocumentationString);
}
private:
CompilerStack m_compilerStack;
Json::Reader m_reader;
};
BOOST_FIXTURE_TEST_SUITE(SolidityNatspecJSON, DocumentationChecker)
BOOST_AUTO_TEST_CASE(user_basic_test)
{
char const* sourceCode = "contract test {\n"
" /// @notice Multiplies `a` by 7\n"
" function mul(uint a) returns(uint d) { return a * 7; }\n"
"}\n";
char const* natspec = "{"
"\"methods\":{"
" \"mul\":{ \"notice\": \"Multiplies `a` by 7\"}"
"}}";
checkNatspec(sourceCode, natspec, true);
}
BOOST_AUTO_TEST_CASE(dev_and_user_basic_test)
{
char const* sourceCode = "contract test {\n"
" /// @notice Multiplies `a` by 7\n"
" /// @dev Multiplies a number by 7\n"
" function mul(uint a) returns(uint d) { return a * 7; }\n"
"}\n";
char const* devNatspec = "{"
"\"methods\":{"
" \"mul\":{ \n"
" \"details\": \"Multiplies a number by 7\"\n"
" }\n"
" }\n"
"}}";
char const* userNatspec = "{"
"\"methods\":{"
" \"mul\":{ \"notice\": \"Multiplies `a` by 7\"}"
"}}";
checkNatspec(sourceCode, devNatspec, false);
checkNatspec(sourceCode, userNatspec, true);
}
BOOST_AUTO_TEST_CASE(user_multiline_comment)
{
char const* sourceCode = "contract test {\n"
" /// @notice Multiplies `a` by 7\n"
" /// and then adds `b`\n"
" function mul_and_add(uint a, uint256 b) returns(uint256 d)\n"
" {\n"
" return (a * 7) + b;\n"
" }\n"
"}\n";
char const* natspec = "{"
"\"methods\":{"
" \"mul_and_add\":{ \"notice\": \"Multiplies `a` by 7 and then adds `b`\"}"
"}}";
checkNatspec(sourceCode, natspec, true);
}
BOOST_AUTO_TEST_CASE(user_multiple_functions)
{
char const* sourceCode = "contract test {\n"
" /// @notice Multiplies `a` by 7 and then adds `b`\n"
" function mul_and_add(uint a, uint256 b) returns(uint256 d)\n"
" {\n"
" return (a * 7) + b;\n"
" }\n"
"\n"
" /// @notice Divides `input` by `div`\n"
" function divide(uint input, uint div) returns(uint d)\n"
" {\n"
" return input / div;\n"
" }\n"
" /// @notice Subtracts 3 from `input`\n"
" function sub(int input) returns(int d)\n"
" {\n"
" return input - 3;\n"
" }\n"
"}\n";
char const* natspec = "{"
"\"methods\":{"
" \"mul_and_add\":{ \"notice\": \"Multiplies `a` by 7 and then adds `b`\"},"
" \"divide\":{ \"notice\": \"Divides `input` by `div`\"},"
" \"sub\":{ \"notice\": \"Subtracts 3 from `input`\"}"
"}}";
checkNatspec(sourceCode, natspec, true);
}
BOOST_AUTO_TEST_CASE(user_empty_contract)
{
char const* sourceCode = "contract test {\n"
"}\n";
char const* natspec = "{\"methods\":{} }";
checkNatspec(sourceCode, natspec, true);
}
BOOST_AUTO_TEST_CASE(dev_and_user_no_doc)
{
char const* sourceCode = "contract test {\n"
" function mul(uint a) returns(uint d) { return a * 7; }\n"
" function sub(int input) returns(int d)\n"
" {\n"
" return input - 3;\n"
" }\n"
"}\n";
char const* devNatspec = "{\"methods\":{}}";
char const* userNatspec = "{\"methods\":{}}";
checkNatspec(sourceCode, devNatspec, false);
checkNatspec(sourceCode, userNatspec, true);
}
BOOST_AUTO_TEST_CASE(dev_desc_after_nl)
{
char const* sourceCode = "contract test {\n"
" /// @dev\n"
" /// Multiplies a number by 7 and adds second parameter\n"
" /// @param a Documentation for the first parameter\n"
" /// @param second Documentation for the second parameter\n"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n";
char const* natspec = "{"
"\"methods\":{"
" \"mul\":{ \n"
" \"details\": \" Multiplies a number by 7 and adds second parameter\",\n"
" \"params\": {\n"
" \"a\": \"Documentation for the first parameter\",\n"
" \"second\": \"Documentation for the second parameter\"\n"
" }\n"
" }\n"
"}}";
checkNatspec(sourceCode, natspec, false);
}
BOOST_AUTO_TEST_CASE(dev_multiple_params)
{
char const* sourceCode = "contract test {\n"
" /// @dev Multiplies a number by 7 and adds second parameter\n"
" /// @param a Documentation for the first parameter\n"
" /// @param second Documentation for the second parameter\n"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n";
char const* natspec = "{"
"\"methods\":{"
" \"mul\":{ \n"
" \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
" \"params\": {\n"
" \"a\": \"Documentation for the first parameter\",\n"
" \"second\": \"Documentation for the second parameter\"\n"
" }\n"
" }\n"
"}}";
checkNatspec(sourceCode, natspec, false);
}
BOOST_AUTO_TEST_CASE(dev_mutiline_param_description)
{
char const* sourceCode = "contract test {\n"
" /// @dev Multiplies a number by 7 and adds second parameter\n"
" /// @param a Documentation for the first parameter starts here.\n"
" /// Since it's a really complicated parameter we need 2 lines\n"
" /// @param second Documentation for the second parameter\n"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n";
char const* natspec = "{"
"\"methods\":{"
" \"mul\":{ \n"
" \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
" \"params\": {\n"
" \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n"
" \"second\": \"Documentation for the second parameter\"\n"
" }\n"
" }\n"
"}}";
checkNatspec(sourceCode, natspec, false);
}
BOOST_AUTO_TEST_CASE(dev_multiple_functions)
{
char const* sourceCode = "contract test {\n"
" /// @dev Multiplies a number by 7 and adds second parameter\n"
" /// @param a Documentation for the first parameter\n"
" /// @param second Documentation for the second parameter\n"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
" \n"
" /// @dev Divides 2 numbers\n"
" /// @param input Documentation for the input parameter\n"
" /// @param div Documentation for the div parameter\n"
" function divide(uint input, uint div) returns(uint d)\n"
" {\n"
" return input / div;\n"
" }\n"
" /// @dev Subtracts 3 from `input`\n"
" /// @param input Documentation for the input parameter\n"
" function sub(int input) returns(int d)\n"
" {\n"
" return input - 3;\n"
" }\n"
"}\n";
char const* natspec = "{"
"\"methods\":{"
" \"mul\":{ \n"
" \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
" \"params\": {\n"
" \"a\": \"Documentation for the first parameter\",\n"
" \"second\": \"Documentation for the second parameter\"\n"
" }\n"
" },\n"
" \"divide\":{ \n"
" \"details\": \"Divides 2 numbers\",\n"
" \"params\": {\n"
" \"input\": \"Documentation for the input parameter\",\n"
" \"div\": \"Documentation for the div parameter\"\n"
" }\n"
" },\n"
" \"sub\":{ \n"
" \"details\": \"Subtracts 3 from `input`\",\n"
" \"params\": {\n"
" \"input\": \"Documentation for the input parameter\"\n"
" }\n"
" }\n"
"}}";
checkNatspec(sourceCode, natspec, false);
}
BOOST_AUTO_TEST_CASE(dev_return)
{
char const* sourceCode = "contract test {\n"
" /// @dev Multiplies a number by 7 and adds second parameter\n"
" /// @param a Documentation for the first parameter starts here.\n"
" /// Since it's a really complicated parameter we need 2 lines\n"
" /// @param second Documentation for the second parameter\n"
" /// @return The result of the multiplication\n"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n";
char const* natspec = "{"
"\"methods\":{"
" \"mul\":{ \n"
" \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
" \"params\": {\n"
" \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n"
" \"second\": \"Documentation for the second parameter\"\n"
" },\n"
" \"return\": \"The result of the multiplication\"\n"
" }\n"
"}}";
checkNatspec(sourceCode, natspec, false);
}
BOOST_AUTO_TEST_CASE(dev_return_desc_after_nl)
{
char const* sourceCode = "contract test {\n"
" /// @dev Multiplies a number by 7 and adds second parameter\n"
" /// @param a Documentation for the first parameter starts here.\n"
" /// Since it's a really complicated parameter we need 2 lines\n"
" /// @param second Documentation for the second parameter\n"
" /// @return\n"
" /// The result of the multiplication\n"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n";
char const* natspec = "{"
"\"methods\":{"
" \"mul\":{ \n"
" \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
" \"params\": {\n"
" \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n"
" \"second\": \"Documentation for the second parameter\"\n"
" },\n"
" \"return\": \" The result of the multiplication\"\n"
" }\n"
"}}";
checkNatspec(sourceCode, natspec, false);
}
BOOST_AUTO_TEST_CASE(dev_multiline_return)
{
char const* sourceCode = "contract test {\n"
" /// @dev Multiplies a number by 7 and adds second parameter\n"
" /// @param a Documentation for the first parameter starts here.\n"
" /// Since it's a really complicated parameter we need 2 lines\n"
" /// @param second Documentation for the second parameter\n"
" /// @return The result of the multiplication\n"
" /// and cookies with nutella\n"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n";
char const* natspec = "{"
"\"methods\":{"
" \"mul\":{ \n"
" \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
" \"params\": {\n"
" \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n"
" \"second\": \"Documentation for the second parameter\"\n"
" },\n"
" \"return\": \"The result of the multiplication and cookies with nutella\"\n"
" }\n"
"}}";
checkNatspec(sourceCode, natspec, false);
}
BOOST_AUTO_TEST_SUITE_END()
}
}
}
Loading…
Cancel
Save