diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp
index 4a32f66b2..2a66a6d69 100644
--- a/alethzero/MainWin.cpp
+++ b/alethzero/MainWin.cpp
@@ -1285,6 +1285,7 @@ void Main::on_blocks_currentItemChanged()
s << "
" << sha3(i.data()).abridged();// << ": " << i[1].toHash() << " [" << i[2].toInt() << " used]";
s << "
Post: " << info.stateRoot << "";
s << "
Dump: " << toHex(block[0].data()) << "";
+ s << "
Receipts-Hex: " << toHex(ethereum()->blockChain().receipts(h).rlp()) << "
";
}
else
{
diff --git a/libethcore/CommonEth.cpp b/libethcore/CommonEth.cpp
index e68839ed5..5f9332ad8 100644
--- a/libethcore/CommonEth.cpp
+++ b/libethcore/CommonEth.cpp
@@ -33,7 +33,7 @@ namespace dev
namespace eth
{
-const unsigned c_protocolVersion = 47;
+const unsigned c_protocolVersion = 48;
const unsigned c_databaseVersion = 5;
static const vector> g_units =
diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h
index 9d107a4b6..4a175ff4e 100644
--- a/libevm/ExtVMFace.h
+++ b/libevm/ExtVMFace.h
@@ -80,6 +80,7 @@ struct SubState
{
suicides += _s.suicides;
refunds += _s.refunds;
+ logs += _s.logs;
return *this;
}
};
diff --git a/libsolidity/AST.h b/libsolidity/AST.h
index 87bc3cd40..d29e84a0a 100644
--- a/libsolidity/AST.h
+++ b/libsolidity/AST.h
@@ -204,7 +204,7 @@ public:
Block& getBody() { return *m_body; }
/// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation
- ASTPointer const& getDocumentation() { return m_documentation; }
+ ASTPointer const& getDocumentation() const { return m_documentation; }
void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); }
std::vector const& getLocalVariables() const { return m_localVariables; }
diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt
index ea2ef4b74..b5147ced3 100644
--- a/libsolidity/CMakeLists.txt
+++ b/libsolidity/CMakeLists.txt
@@ -16,6 +16,10 @@ endif()
include_directories(..)
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( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )
diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp
index 6535e00d4..c83257a1d 100644
--- a/libsolidity/CompilerStack.cpp
+++ b/libsolidity/CompilerStack.cpp
@@ -27,6 +27,7 @@
#include
#include
#include
+#include
using namespace std;
@@ -35,6 +36,8 @@ namespace dev
namespace solidity
{
+CompilerStack::CompilerStack(): m_interfaceHandler(make_shared()) {}
+
void CompilerStack::setSource(string const& _sourceCode)
{
reset();
@@ -81,48 +84,31 @@ void CompilerStack::streamAssembly(ostream& _outStream)
m_compiler->streamAssembly(_outStream);
}
-string const& CompilerStack::getInterface()
+std::string const& CompilerStack::getJsonDocumentation(enum DocumentationType _type)
{
if (!m_parseSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
- if (m_interface.empty())
+
+ auto createDocIfNotThere = [this, _type](std::unique_ptr& _doc)
{
- stringstream interface;
- interface << '[';
- vector exportedFunctions = m_contractASTNode->getInterfaceFunctions();
- unsigned functionsCount = exportedFunctions.size();
- for (FunctionDefinition const* f: exportedFunctions)
- {
- auto streamVariables = [&](vector> const& _vars)
- {
- unsigned varCount = _vars.size();
- for (ASTPointer const& var: _vars)
- {
- interface << "{"
- << "\"name\":" << escaped(var->getName(), false) << ","
- << "\"type\":" << escaped(var->getType()->toString(), false)
- << "}";
- 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();
+ if (!_doc)
+ _doc = m_interfaceHandler->getDocumentation(m_contractASTNode, _type);
+ };
+
+ switch (_type)
+ {
+ case NATSPEC_USER:
+ createDocIfNotThere(m_userDocumentation);
+ return *m_userDocumentation;
+ case NATSPEC_DEV:
+ createDocIfNotThere(m_devDocumentation);
+ return *m_devDocumentation;
+ case ABI_INTERFACE:
+ createDocIfNotThere(m_interface);
+ return *m_interface;
}
- return m_interface;
+
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type."));
}
bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize)
diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h
index 6cae8660f..8dc546fbe 100644
--- a/libsolidity/CompilerStack.h
+++ b/libsolidity/CompilerStack.h
@@ -35,6 +35,14 @@ class Scanner;
class ContractDefinition;
class Compiler;
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.
@@ -44,7 +52,7 @@ class GlobalContext;
class CompilerStack
{
public:
- CompilerStack() {}
+ CompilerStack();
void reset() { *this = CompilerStack(); }
void setSource(std::string const& _sourceCode);
void parse();
@@ -62,6 +70,11 @@ public:
/// Returns a string representing the contract interface in JSON.
/// Prerequisite: Successful call to parse or compile.
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.
Scanner const& getScanner() const { return *m_scanner; }
@@ -76,8 +89,11 @@ private:
std::shared_ptr m_globalContext;
std::shared_ptr m_contractASTNode;
bool m_parseSuccessful;
- std::string m_interface;
+ std::unique_ptr m_interface;
+ std::unique_ptr m_userDocumentation;
+ std::unique_ptr m_devDocumentation;
std::shared_ptr m_compiler;
+ std::shared_ptr m_interfaceHandler;
bytes m_bytecode;
};
diff --git a/libsolidity/Exceptions.h b/libsolidity/Exceptions.h
index 1903c1dc2..8298c9810 100644
--- a/libsolidity/Exceptions.h
+++ b/libsolidity/Exceptions.h
@@ -36,6 +36,7 @@ struct TypeError: virtual Exception {};
struct DeclarationError: virtual Exception {};
struct CompilerError: virtual Exception {};
struct InternalCompilerError: virtual Exception {};
+struct DocstringParsingError: virtual Exception {};
typedef boost::error_info errinfo_sourcePosition;
typedef boost::error_info errinfo_sourceLocation;
diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp
new file mode 100644
index 000000000..0115c7f59
--- /dev/null
+++ b/libsolidity/InterfaceHandler.cpp
@@ -0,0 +1,278 @@
+
+#include
+#include
+#include
+
+namespace dev
+{
+namespace solidity
+{
+
+/* -- public -- */
+
+InterfaceHandler::InterfaceHandler()
+{
+ m_lastTag = DOCTAG_NONE;
+}
+
+std::unique_ptr InterfaceHandler::getDocumentation(std::shared_ptr _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 InterfaceHandler::getABIInterface(std::shared_ptr _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> const& _vars)
+ {
+ Json::Value params(Json::arrayValue);
+ for (ASTPointer 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(new std::string(m_writer.write(methods)));
+}
+
+std::unique_ptr InterfaceHandler::getUserDocumentation(std::shared_ptr _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(new std::string(m_writer.write(doc)));
+}
+
+std::unique_ptr InterfaceHandler::getDevDocumentation(std::shared_ptr _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(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
diff --git a/libsolidity/InterfaceHandler.h b/libsolidity/InterfaceHandler.h
new file mode 100644
index 000000000..7a5ee66db
--- /dev/null
+++ b/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 .
+*/
+/**
+ * @author Lefteris
+ * @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
+#include
+#include
+
+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 getDocumentation(std::shared_ptr _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 getABIInterface(std::shared_ptr _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 getUserDocumentation(std::shared_ptr _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 getDevDocumentation(std::shared_ptr _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> m_params;
+};
+
+} //solidity NS
+} // dev NS
diff --git a/solc/main.cpp b/solc/main.cpp
index a7216e594..daeb2707c 100644
--- a/solc/main.cpp
+++ b/solc/main.cpp
@@ -135,7 +135,9 @@ int main(int argc, char** argv)
cout << "Opcodes:" << endl;
cout << eth::disassemble(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;
}
diff --git a/test/solidityJSONInterfaceTest.cpp b/test/solidityJSONInterfaceTest.cpp
index 1a443087f..407f05d03 100644
--- a/test/solidityJSONInterfaceTest.cpp
+++ b/test/solidityJSONInterfaceTest.cpp
@@ -23,6 +23,7 @@
#include
#include
#include
+#include
namespace dev
{
@@ -34,23 +35,37 @@ namespace test
class InterfaceChecker
{
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);
- std::string generatedInterfaceString = m_compilerStack.getInterface();
+ try
+ {
+ m_compilerStack.parse(_code);
+ }
+ catch (const std::exception& e)
+ {
+ std::string const* extra = boost::get_error_info(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;
m_reader.parse(generatedInterfaceString, generatedInterface);
Json::Value expectedInterface;
m_reader.parse(_expectedInterfaceString, expectedInterface);
- return expectedInterface == generatedInterface;
+ BOOST_CHECK_MESSAGE(expectedInterface == generatedInterface,
+ "Expected " << _expectedInterfaceString <<
+ "\n but got:\n" << generatedInterfaceString);
}
-
+
private:
CompilerStack m_compilerStack;
Json::Reader m_reader;
};
-BOOST_FIXTURE_TEST_SUITE(SolidityCompilerJSONInterfaceOutput, InterfaceChecker)
+BOOST_FIXTURE_TEST_SUITE(SolidityABIJSON, InterfaceChecker)
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)
@@ -86,7 +101,7 @@ BOOST_AUTO_TEST_CASE(empty_contract)
char const* interface = "[]";
- BOOST_CHECK(checkInterface(sourceCode, interface));
+ checkInterface(sourceCode, interface);
}
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 g(uint b) returns(uint e) { return b * 8; }\n"
"}\n";
-
+
char const* interface = R"([
{
"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)
@@ -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)
@@ -170,7 +185,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods_order)
" function f(uint a) returns(uint d) { return a * 7; }\n"
" function c(uint b) returns(uint e) { return b * 8; }\n"
"}\n";
-
+
char const* interface = R"([
{
"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()
diff --git a/test/solidityNatspecJSON.cpp b/test/solidityNatspecJSON.cpp
new file mode 100644
index 000000000..2ccedf7a2
--- /dev/null
+++ b/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 .
+ */
+/**
+ * @author Lefteris Karapetsas
+ * @date 2014
+ * Unit tests for the solidity compiler JSON Interface output.
+ */
+
+#include
+#include
+#include
+#include
+
+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(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()
+
+}
+}
+}