Merge remote-tracking branch 'ethereum/develop' into sol_import

Conflicts:
	libsolidity/CompilerStack.cpp
	libsolidity/CompilerStack.h
	solc/main.cpp
This commit is contained in:
Christian 2014-12-05 15:27:07 +01:00
commit d4a958e1fe
7 changed files with 446 additions and 43 deletions

2
AST.h
View File

@ -238,7 +238,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<ASTString> const& getDocumentation() { return m_documentation; }
ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); }
std::vector<VariableDeclaration const*> const& getLocalVariables() const { return m_localVariables; }

View File

@ -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} )

View File

@ -27,6 +27,7 @@
#include <libsolidity/NameAndTypeResolver.h>
#include <libsolidity/Compiler.h>
#include <libsolidity/CompilerStack.h>
#include <libsolidity/InterfaceHandler.h>
using namespace std;
@ -127,45 +128,34 @@ void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractN
string const& CompilerStack::getInterface(std::string const& _contractName)
{
Contract& contract = getContract(_contractName);
if (contract.interface.empty())
{
stringstream interface;
interface << '[';
vector<FunctionDefinition const*> exportedFunctions = contract.contract->getInterfaceFunctions();
unsigned functionsCount = exportedFunctions.size();
for (FunctionDefinition const* f: exportedFunctions)
{
auto streamVariables = [&](vector<ASTPointer<VariableDeclaration>> const& _vars)
{
unsigned varCount = _vars.size();
for (ASTPointer<VariableDeclaration> const& var: _vars)
{
interface << "{"
<< "\"name\":" << escaped(var->getName(), false) << ","
<< "\"type\":" << escaped(var->getType()->toString(), false)
<< "}";
if (--varCount > 0)
interface << ",";
}
};
return getJsonDocumentation(_contractName, ABI_INTERFACE);
}
interface << '{'
<< "\"name\":" << escaped(f->getName(), false) << ","
<< "\"inputs\":[";
streamVariables(f->getParameters());
interface << "],"
<< "\"outputs\":[";
streamVariables(f->getReturnParameters());
interface << "]"
<< "}";
if (--functionsCount > 0)
interface << ",";
}
interface << ']';
contract.interface = interface.str();
std::string const& CompilerStack::getJsonDocumentation(std::string const& _contractName, enum DocumentationType _type)
{
if (!m_parseSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
Contract& contract = getContract(_contractName);
std::unique_ptr<string>* doc;
switch (_type)
{
case NATSPEC_USER:
doc = &contract.userDocumentation;
break;
case NATSPEC_DEV:
doc = &contract.devDocumentation;
break;
case ABI_INTERFACE:
doc = &contract.interface;
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type."));
}
return contract.interface;
if (!*doc)
*doc = contract.interfaceHandler->getDocumentation(*contract.contract, _type);
return *(*doc);
}
Scanner const& CompilerStack::getScanner(string const& _sourceName)
@ -193,7 +183,6 @@ void CompilerStack::reset(bool _keepSources)
else
m_sources.clear();
m_globalContext.reset();
m_compiler.reset();
m_sourceOrder.clear();
m_contracts.clear();
}
@ -247,5 +236,8 @@ CompilerStack::Source& CompilerStack::getSource(string const& _sourceName)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Given source file not found."));
return it->second;
}
CompilerStack::Contract::Contract(): interfaceHandler(make_shared<InterfaceHandler>()) {}
}
}

View File

@ -33,10 +33,18 @@ namespace solidity {
// forward declarations
class Scanner;
class ContractDefinition;
class SourceUnit;
class Compiler;
class GlobalContext;
class ContractDefinition;
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.
@ -47,6 +55,7 @@ class CompilerStack: boost::noncopyable
{
public:
CompilerStack(): m_parseSuccessful(false) {}
/// Adds a source object (e.g. file) to the parser. After this, parse has to be called again.
void addSource(std::string const& _name, std::string const& _content);
void setSource(std::string const& _sourceCode);
@ -71,6 +80,11 @@ public:
/// Returns a string representing the contract interface in JSON.
/// Prerequisite: Successful call to parse or compile.
std::string const& getInterface(std::string const& _contractName = "");
/// 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 DocumentationType
std::string const& getJsonDocumentation(std::string const& _contractName, enum DocumentationType _type);
/// Returns the previously used scanner, useful for counting lines during error reporting.
Scanner const& getScanner(std::string const& _sourceName = "");
@ -94,10 +108,15 @@ private:
struct Contract
{
ContractDefinition const* contract;
std::string interface;
ContractDefinition* contract;
std::shared_ptr<Compiler> compiler;
bytes bytecode;
std::shared_ptr<InterfaceHandler> interfaceHandler;
std::unique_ptr<std::string> interface;
std::unique_ptr<std::string> userDocumentation;
std::unique_ptr<std::string> devDocumentation;
Contract();
};
void reset(bool _keepSources = false);
@ -109,7 +128,6 @@ private:
bool m_parseSuccessful;
std::map<std::string, Source> m_sources;
std::shared_ptr<GlobalContext> m_globalContext;
std::shared_ptr<Compiler> m_compiler;
std::vector<Source const*> m_sourceOrder;
std::map<std::string, Contract> m_contracts;
};

View File

@ -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<struct tag_sourceLocation, Location> errinfo_sourceLocation;

278
InterfaceHandler.cpp Normal file
View File

@ -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(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(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(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(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
InterfaceHandler.h Normal file
View File

@ -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(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(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(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(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