aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/interface
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity/interface')
-rw-r--r--libsolidity/interface/CompilerStack.cpp125
-rw-r--r--libsolidity/interface/CompilerStack.h19
2 files changed, 113 insertions, 31 deletions
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index 8ff63056..83459183 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -39,11 +39,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);}})"},
@@ -59,8 +56,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
@@ -105,16 +102,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
@@ -154,6 +165,9 @@ bool CompilerStack::parse()
m_contracts[contract->name()].contract = contract;
}
+ if (!checkLibraryNameClashes())
+ noErrors = false;
+
for (Source const* source: m_sourceOrder)
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
@@ -370,13 +384,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;
@@ -384,28 +429,53 @@ 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);
}
+bool CompilerStack::checkLibraryNameClashes()
+{
+ bool clashFound = false;
+ map<string, SourceLocation> libraries;
+ for (Source const* source: m_sourceOrder)
+ for (ASTPointer<ASTNode> const& node: source->ast->nodes())
+ if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
+ if (contract->isLibrary())
+ {
+ if (libraries.count(contract->name()))
+ {
+ auto err = make_shared<Error>(Error::Type::DeclarationError);
+ *err <<
+ errinfo_sourceLocation(contract->location()) <<
+ errinfo_comment(
+ "Library \"" + contract->name() + "\" declared twice "
+ "(will create ambiguities during linking)."
+ ) <<
+ errinfo_secondarySourceLocation(SecondarySourceLocation().append(
+ "The other declaration is here:", libraries[contract->name()]
+ ));
+
+ m_errors.push_back(err);
+ clashFound = true;
+ }
+ else
+ libraries[contract->name()] = contract->location();
+ }
+ return !clashFound;
+}
+
string CompilerStack::absolutePath(string const& _path, string const& _reference) const
{
// Anything that does not start with `.` is an absolute path.
@@ -479,6 +549,3 @@ CompilerStack::Source const& CompilerStack::source(string const& _sourceName) co
return it->second;
}
-
-}
-}
diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h
index 3e6dc456..517d0055 100644
--- a/libsolidity/interface/CompilerStack.h
+++ b/libsolidity/interface/CompilerStack.h
@@ -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,7 +205,14 @@ 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.
+ bool checkLibraryNameClashes();
/// @returns the absolute path corresponding to @a _path relative to @a _reference.
std::string absolutePath(std::string const& _path, std::string const& _reference) const;
/// Compile a single contract and put the result in @a _compiledContracts.
@@ -212,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;