mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #351 from chriseth/autoload
Automatically load imported files in solc.
This commit is contained in:
commit
d21c4276b3
@ -38,11 +38,8 @@
|
|||||||
#include <libdevcore/SHA3.h>
|
#include <libdevcore/SHA3.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace dev;
|
||||||
namespace dev
|
using namespace dev::solidity;
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
const map<string, string> StandardSources = map<string, string>{
|
const map<string, string> StandardSources = map<string, string>{
|
||||||
{"coin", R"(import "CoinReg";import "Config";import "configUser";contract coin is configUser{function coin(bytes3 name, uint denom) {CoinReg(Config(configAddr()).lookup(3)).register(name, denom);}})"},
|
{"coin", R"(import "CoinReg";import "Config";import "configUser";contract coin is configUser{function coin(bytes3 name, uint denom) {CoinReg(Config(configAddr()).lookup(3)).register(name, denom);}})"},
|
||||||
@ -58,8 +55,8 @@ const map<string, string> StandardSources = map<string, string>{
|
|||||||
{"std", R"(import "owned";import "mortal";import "Config";import "configUser";import "NameReg";import "named";)"}
|
{"std", R"(import "owned";import "mortal";import "Config";import "configUser";import "NameReg";import "named";)"}
|
||||||
};
|
};
|
||||||
|
|
||||||
CompilerStack::CompilerStack(bool _addStandardSources):
|
CompilerStack::CompilerStack(bool _addStandardSources, ReadFileCallback const& _readFile):
|
||||||
m_parseSuccessful(false)
|
m_readFile(_readFile), m_parseSuccessful(false)
|
||||||
{
|
{
|
||||||
if (_addStandardSources)
|
if (_addStandardSources)
|
||||||
addSources(StandardSources, true); // add them as libraries
|
addSources(StandardSources, true); // add them as libraries
|
||||||
@ -104,16 +101,30 @@ bool CompilerStack::parse()
|
|||||||
m_errors.clear();
|
m_errors.clear();
|
||||||
m_parseSuccessful = false;
|
m_parseSuccessful = false;
|
||||||
|
|
||||||
|
vector<string> sourcesToParse;
|
||||||
|
for (auto const& s: m_sources)
|
||||||
|
sourcesToParse.push_back(s.first);
|
||||||
map<string, SourceUnit const*> sourceUnitsByName;
|
map<string, SourceUnit const*> sourceUnitsByName;
|
||||||
for (auto& sourcePair: m_sources)
|
for (size_t i = 0; i < sourcesToParse.size(); ++i)
|
||||||
{
|
{
|
||||||
sourcePair.second.scanner->reset();
|
string const& path = sourcesToParse[i];
|
||||||
sourcePair.second.ast = Parser(m_errors).parse(sourcePair.second.scanner);
|
Source& source = m_sources[path];
|
||||||
if (!sourcePair.second.ast)
|
source.scanner->reset();
|
||||||
|
source.ast = Parser(m_errors).parse(source.scanner);
|
||||||
|
sourceUnitsByName[path] = source.ast.get();
|
||||||
|
if (!source.ast)
|
||||||
solAssert(!Error::containsOnlyWarnings(m_errors), "Parser returned null but did not report error.");
|
solAssert(!Error::containsOnlyWarnings(m_errors), "Parser returned null but did not report error.");
|
||||||
else
|
else
|
||||||
sourcePair.second.ast->annotation().path = sourcePair.first;
|
{
|
||||||
sourceUnitsByName[sourcePair.first] = sourcePair.second.ast.get();
|
source.ast->annotation().path = path;
|
||||||
|
for (auto const& newSource: loadMissingSources(*source.ast, path))
|
||||||
|
{
|
||||||
|
string const& newPath = newSource.first;
|
||||||
|
string const& newContents = newSource.second;
|
||||||
|
m_sources[newPath].scanner = make_shared<Scanner>(CharStream(newContents), newPath);
|
||||||
|
sourcesToParse.push_back(newPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!Error::containsOnlyWarnings(m_errors))
|
if (!Error::containsOnlyWarnings(m_errors))
|
||||||
// errors while parsing. sould stop before type checking
|
// errors while parsing. sould stop before type checking
|
||||||
@ -367,13 +378,44 @@ tuple<int, int, int, int> CompilerStack::positionFromSourceLocation(SourceLocati
|
|||||||
return make_tuple(++startLine, ++startColumn, ++endLine, ++endColumn);
|
return make_tuple(++startLine, ++startColumn, ++endLine, ++endColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string const& _path)
|
||||||
|
{
|
||||||
|
StringMap newSources;
|
||||||
|
for (auto const& node: _ast.nodes())
|
||||||
|
if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get()))
|
||||||
|
{
|
||||||
|
string path = absolutePath(import->path(), _path);
|
||||||
|
import->annotation().absolutePath = path;
|
||||||
|
if (m_sources.count(path) || newSources.count(path))
|
||||||
|
continue;
|
||||||
|
string contents;
|
||||||
|
string errorMessage;
|
||||||
|
if (!m_readFile)
|
||||||
|
errorMessage = "File not supplied initially.";
|
||||||
|
else
|
||||||
|
tie(contents, errorMessage) = m_readFile(path);
|
||||||
|
if (!errorMessage.empty())
|
||||||
|
{
|
||||||
|
auto err = make_shared<Error>(Error::Type::ParserError);
|
||||||
|
*err <<
|
||||||
|
errinfo_sourceLocation(import->location()) <<
|
||||||
|
errinfo_comment("Source not found: " + errorMessage);
|
||||||
|
m_errors.push_back(std::move(err));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
newSources[path] = contents;
|
||||||
|
}
|
||||||
|
return newSources;
|
||||||
|
}
|
||||||
|
|
||||||
void CompilerStack::resolveImports()
|
void CompilerStack::resolveImports()
|
||||||
{
|
{
|
||||||
// topological sorting (depth first search) of the import graph, cutting potential cycles
|
// topological sorting (depth first search) of the import graph, cutting potential cycles
|
||||||
vector<Source const*> sourceOrder;
|
vector<Source const*> sourceOrder;
|
||||||
set<Source const*> sourcesSeen;
|
set<Source const*> sourcesSeen;
|
||||||
|
|
||||||
function<void(string const&, Source const*)> toposort = [&](string const& _sourceName, Source const* _source)
|
function<void(Source const*)> toposort = [&](Source const* _source)
|
||||||
{
|
{
|
||||||
if (sourcesSeen.count(_source))
|
if (sourcesSeen.count(_source))
|
||||||
return;
|
return;
|
||||||
@ -381,24 +423,18 @@ void CompilerStack::resolveImports()
|
|||||||
for (ASTPointer<ASTNode> const& node: _source->ast->nodes())
|
for (ASTPointer<ASTNode> const& node: _source->ast->nodes())
|
||||||
if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get()))
|
if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get()))
|
||||||
{
|
{
|
||||||
string path = absolutePath(import->path(), _sourceName);
|
string const& path = import->annotation().absolutePath;
|
||||||
import->annotation().absolutePath = path;
|
solAssert(!path.empty(), "");
|
||||||
if (!m_sources.count(path))
|
solAssert(m_sources.count(path), "");
|
||||||
BOOST_THROW_EXCEPTION(
|
import->annotation().sourceUnit = m_sources[path].ast.get();
|
||||||
Error(Error::Type::ParserError)
|
toposort(&m_sources[path]);
|
||||||
<< errinfo_sourceLocation(import->location())
|
|
||||||
<< errinfo_comment("Source not found.")
|
|
||||||
);
|
|
||||||
import->annotation().sourceUnit = m_sources.at(path).ast.get();
|
|
||||||
|
|
||||||
toposort(path, &m_sources[path]);
|
|
||||||
}
|
}
|
||||||
sourceOrder.push_back(_source);
|
sourceOrder.push_back(_source);
|
||||||
};
|
};
|
||||||
|
|
||||||
for (auto const& sourcePair: m_sources)
|
for (auto const& sourcePair: m_sources)
|
||||||
if (!sourcePair.second.isLibrary)
|
if (!sourcePair.second.isLibrary)
|
||||||
toposort(sourcePair.first, &sourcePair.second);
|
toposort(&sourcePair.second);
|
||||||
|
|
||||||
swap(m_sourceOrder, sourceOrder);
|
swap(m_sourceOrder, sourceOrder);
|
||||||
}
|
}
|
||||||
@ -507,6 +543,3 @@ CompilerStack::Source const& CompilerStack::source(string const& _sourceName) co
|
|||||||
|
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <functional>
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
#include <libdevcore/Common.h>
|
#include <libdevcore/Common.h>
|
||||||
@ -74,8 +75,14 @@ enum class DocumentationType: uint8_t
|
|||||||
class CompilerStack: boost::noncopyable
|
class CompilerStack: boost::noncopyable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Creates a new compiler stack. Adds standard sources if @a _addStandardSources.
|
/// File reading callback, should return a pair of content and error message (exactly one nonempty)
|
||||||
explicit CompilerStack(bool _addStandardSources = true);
|
/// for a given path.
|
||||||
|
using ReadFileCallback = std::function<std::pair<std::string, std::string>(std::string const&)>;
|
||||||
|
|
||||||
|
/// Creates a new compiler stack.
|
||||||
|
/// @param _readFile callback to used to read files for import statements. Should return
|
||||||
|
/// @param _addStandardSources Adds standard sources if @a _addStandardSources.
|
||||||
|
explicit CompilerStack(bool _addStandardSources = true, ReadFileCallback const& _readFile = ReadFileCallback());
|
||||||
|
|
||||||
/// Resets the compiler to a state where the sources are not parsed or even removed.
|
/// Resets the compiler to a state where the sources are not parsed or even removed.
|
||||||
void reset(bool _keepSources = false, bool _addStandardSources = true);
|
void reset(bool _keepSources = false, bool _addStandardSources = true);
|
||||||
@ -198,6 +205,10 @@ private:
|
|||||||
mutable std::unique_ptr<std::string const> devDocumentation;
|
mutable std::unique_ptr<std::string const> devDocumentation;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Loads the missing sources from @a _ast (named @a _path) using the callback
|
||||||
|
/// @a m_readFile and stores the absolute paths of all imports in the AST annotations.
|
||||||
|
/// @returns the newly loaded sources.
|
||||||
|
StringMap loadMissingSources(SourceUnit const& _ast, std::string const& _path);
|
||||||
void resolveImports();
|
void resolveImports();
|
||||||
/// Checks whether there are libraries with the same name, reports that as an error and
|
/// Checks whether there are libraries with the same name, reports that as an error and
|
||||||
/// @returns false in this case.
|
/// @returns false in this case.
|
||||||
@ -215,6 +226,7 @@ private:
|
|||||||
Contract const& contract(std::string const& _contractName = "") const;
|
Contract const& contract(std::string const& _contractName = "") const;
|
||||||
Source const& source(std::string const& _sourceName = "") const;
|
Source const& source(std::string const& _sourceName = "") const;
|
||||||
|
|
||||||
|
ReadFileCallback m_readFile;
|
||||||
bool m_parseSuccessful;
|
bool m_parseSuccessful;
|
||||||
std::map<std::string const, Source> m_sources;
|
std::map<std::string const, Source> m_sources;
|
||||||
std::shared_ptr<GlobalContext> m_globalContext;
|
std::shared_ptr<GlobalContext> m_globalContext;
|
||||||
|
@ -497,7 +497,18 @@ bool CommandLineInterface::processInput()
|
|||||||
return link();
|
return link();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_compiler.reset(new CompilerStack(m_args.count(g_argAddStandard) > 0));
|
function<pair<string,string>(string const&)> fileReader = [this](string const& _path)
|
||||||
|
{
|
||||||
|
auto path = boost::filesystem::path(_path);
|
||||||
|
if (!boost::filesystem::exists(path))
|
||||||
|
return make_pair(string(), string("File not found."));
|
||||||
|
else if (!boost::filesystem::is_regular_file(path))
|
||||||
|
return make_pair(string(), string("Not a valid file."));
|
||||||
|
else
|
||||||
|
return make_pair(m_sourceCodes[_path] = dev::contentsString(_path), string());
|
||||||
|
};
|
||||||
|
|
||||||
|
m_compiler.reset(new CompilerStack(m_args.count(g_argAddStandard) > 0, fileReader));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
for (auto const& sourceCode: m_sourceCodes)
|
for (auto const& sourceCode: m_sourceCodes)
|
||||||
|
Loading…
Reference in New Issue
Block a user