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>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
using namespace dev;
|
||||
using namespace dev::solidity;
|
||||
|
||||
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);}})"},
|
||||
@ -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";)"}
|
||||
};
|
||||
|
||||
CompilerStack::CompilerStack(bool _addStandardSources):
|
||||
m_parseSuccessful(false)
|
||||
CompilerStack::CompilerStack(bool _addStandardSources, ReadFileCallback const& _readFile):
|
||||
m_readFile(_readFile), m_parseSuccessful(false)
|
||||
{
|
||||
if (_addStandardSources)
|
||||
addSources(StandardSources, true); // add them as libraries
|
||||
@ -104,16 +101,30 @@ bool CompilerStack::parse()
|
||||
m_errors.clear();
|
||||
m_parseSuccessful = false;
|
||||
|
||||
vector<string> sourcesToParse;
|
||||
for (auto const& s: m_sources)
|
||||
sourcesToParse.push_back(s.first);
|
||||
map<string, SourceUnit const*> sourceUnitsByName;
|
||||
for (auto& sourcePair: m_sources)
|
||||
for (size_t i = 0; i < sourcesToParse.size(); ++i)
|
||||
{
|
||||
sourcePair.second.scanner->reset();
|
||||
sourcePair.second.ast = Parser(m_errors).parse(sourcePair.second.scanner);
|
||||
if (!sourcePair.second.ast)
|
||||
string const& path = sourcesToParse[i];
|
||||
Source& source = m_sources[path];
|
||||
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.");
|
||||
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))
|
||||
// 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);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
// topological sorting (depth first search) of the import graph, cutting potential cycles
|
||||
vector<Source const*> sourceOrder;
|
||||
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))
|
||||
return;
|
||||
@ -381,24 +423,18 @@ void CompilerStack::resolveImports()
|
||||
for (ASTPointer<ASTNode> const& node: _source->ast->nodes())
|
||||
if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get()))
|
||||
{
|
||||
string path = absolutePath(import->path(), _sourceName);
|
||||
import->annotation().absolutePath = path;
|
||||
if (!m_sources.count(path))
|
||||
BOOST_THROW_EXCEPTION(
|
||||
Error(Error::Type::ParserError)
|
||||
<< errinfo_sourceLocation(import->location())
|
||||
<< errinfo_comment("Source not found.")
|
||||
);
|
||||
import->annotation().sourceUnit = m_sources.at(path).ast.get();
|
||||
|
||||
toposort(path, &m_sources[path]);
|
||||
string const& path = import->annotation().absolutePath;
|
||||
solAssert(!path.empty(), "");
|
||||
solAssert(m_sources.count(path), "");
|
||||
import->annotation().sourceUnit = m_sources[path].ast.get();
|
||||
toposort(&m_sources[path]);
|
||||
}
|
||||
sourceOrder.push_back(_source);
|
||||
};
|
||||
|
||||
for (auto const& sourcePair: m_sources)
|
||||
if (!sourcePair.second.isLibrary)
|
||||
toposort(sourcePair.first, &sourcePair.second);
|
||||
toposort(&sourcePair.second);
|
||||
|
||||
swap(m_sourceOrder, sourceOrder);
|
||||
}
|
||||
@ -507,6 +543,3 @@ CompilerStack::Source const& CompilerStack::source(string const& _sourceName) co
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <json/json.h>
|
||||
#include <libdevcore/Common.h>
|
||||
@ -74,8 +75,14 @@ enum class DocumentationType: uint8_t
|
||||
class CompilerStack: boost::noncopyable
|
||||
{
|
||||
public:
|
||||
/// Creates a new compiler stack. Adds standard sources if @a _addStandardSources.
|
||||
explicit CompilerStack(bool _addStandardSources = true);
|
||||
/// File reading callback, should return a pair of content and error message (exactly one nonempty)
|
||||
/// 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.
|
||||
void reset(bool _keepSources = false, bool _addStandardSources = true);
|
||||
@ -198,6 +205,10 @@ private:
|
||||
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();
|
||||
/// Checks whether there are libraries with the same name, reports that as an error and
|
||||
/// @returns false in this case.
|
||||
@ -215,6 +226,7 @@ private:
|
||||
Contract const& contract(std::string const& _contractName = "") const;
|
||||
Source const& source(std::string const& _sourceName = "") const;
|
||||
|
||||
ReadFileCallback m_readFile;
|
||||
bool m_parseSuccessful;
|
||||
std::map<std::string const, Source> m_sources;
|
||||
std::shared_ptr<GlobalContext> m_globalContext;
|
||||
|
@ -497,7 +497,18 @@ bool CommandLineInterface::processInput()
|
||||
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
|
||||
{
|
||||
for (auto const& sourceCode: m_sourceCodes)
|
||||
|
Loading…
Reference in New Issue
Block a user