Skip to content

Commit

Permalink
Stop after parsing.
Browse files Browse the repository at this point in the history
  • Loading branch information
chriseth authored and Marenz committed Jul 23, 2020
1 parent ba4e05c commit 0a24a29
Show file tree
Hide file tree
Showing 13 changed files with 220 additions and 43 deletions.
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Language Features:

Compiler Features:
* Variable declarations using the ``var`` keyword are not recognized anymore.
* New feature to stop compilation after parsing stage.


Bugfixes:
Expand Down
2 changes: 2 additions & 0 deletions docs/using-the-compiler.rst
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ Input Description
// Optional
"settings":
{
// Optional: Stop compilation after this stage. Currently only "parsing" is valid here
"stopAfter": "parsing",
// Optional: Sorted list of remappings
"remappings": [ ":g=/dir" ],
// Optional: Optimizer settings
Expand Down
9 changes: 7 additions & 2 deletions libsolidity/ast/ASTJsonConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,9 +250,14 @@ bool ASTJsonConverter::visit(ImportDirective const& _node)
std::vector<pair<string, Json::Value>> attributes = {
make_pair("file", _node.path()),
make_pair("absolutePath", _node.annotation().absolutePath),
make_pair(m_legacy ? "SourceUnit" : "sourceUnit", nodeId(*_node.annotation().sourceUnit)),
make_pair("scope", idOrNull(_node.scope()))
};

if (_node.annotation().sourceUnit)
attributes.emplace_back(
make_pair(m_legacy ? "SourceUnit" : "sourceUnit", nodeId(*_node.annotation().sourceUnit))
);

attributes.emplace_back("unitAlias", _node.name());
Json::Value symbolAliases(Json::arrayValue);
for (auto const& symbolAlias: _node.symbolAliases())
Expand Down Expand Up @@ -731,7 +736,7 @@ bool ASTJsonConverter::visit(FunctionCall const& _node)
attributes.emplace_back("isStructConstructorCall", _node.annotation().kind == FunctionCallKind::StructConstructorCall);
attributes.emplace_back("type_conversion", _node.annotation().kind == FunctionCallKind::TypeConversion);
}
else
else if (_node.annotation().kind != FunctionCallKind::Unset)
attributes.emplace_back("kind", functionCallKind(_node.annotation().kind));
appendExpressionAttributes(attributes, _node.annotation());
setJsonNode(_node, "FunctionCall", std::move(attributes));
Expand Down
5 changes: 4 additions & 1 deletion libsolidity/ast/Types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,9 @@ util::Result<TypePointers> transformParametersToExternal(TypePointers const& _pa

for (auto const& type: _parameters)
{
if (!type)
continue;

if (TypePointer ext = type->interfaceType(_inLibrary).get())
transformed.push_back(ext);
else
Expand Down Expand Up @@ -3552,7 +3555,7 @@ string FunctionType::externalSignature() const
}

// "inLibrary" is only relevant if this is not an event.
bool const inLibrary = kind() != Kind::Event && dynamic_cast<ContractDefinition const&>(*m_declaration->scope()).isLibrary();
bool const inLibrary = kind() != Kind::Event && m_declaration->scope() && dynamic_cast<ContractDefinition const&>(*m_declaration->scope()).isLibrary();

auto extParams = transformParametersToExternal(m_parameterTypes, inLibrary);

Expand Down
75 changes: 46 additions & 29 deletions libsolidity/interface/CompilerStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ std::optional<CompilerStack::Remapping> CompilerStack::parseRemapping(string con

void CompilerStack::setRemappings(vector<Remapping> const& _remappings)
{
if (m_stackState >= ParsingPerformed)
if (m_stackState >= ParsedAndImported)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set remappings before parsing."));
for (auto const& remapping: _remappings)
solAssert(!remapping.prefix.empty(), "");
Expand All @@ -133,21 +133,21 @@ void CompilerStack::setRemappings(vector<Remapping> const& _remappings)

void CompilerStack::setEVMVersion(langutil::EVMVersion _version)
{
if (m_stackState >= ParsingPerformed)
if (m_stackState >= ParsedAndImported)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set EVM version before parsing."));
m_evmVersion = _version;
}

void CompilerStack::setSMTSolverChoice(smtutil::SMTSolverChoice _enabledSMTSolvers)
{
if (m_stackState >= ParsingPerformed)
if (m_stackState >= ParsedAndImported)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set enabled SMT solvers before parsing."));
m_enabledSMTSolvers = _enabledSMTSolvers;
}

void CompilerStack::setLibraries(std::map<std::string, util::h160> const& _libraries)
{
if (m_stackState >= ParsingPerformed)
if (m_stackState >= ParsedAndImported)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set libraries before parsing."));
m_libraries = _libraries;
}
Expand All @@ -161,36 +161,36 @@ void CompilerStack::setOptimiserSettings(bool _optimize, unsigned _runs)

void CompilerStack::setOptimiserSettings(OptimiserSettings _settings)
{
if (m_stackState >= ParsingPerformed)
if (m_stackState >= ParsedAndImported)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set optimiser settings before parsing."));
m_optimiserSettings = std::move(_settings);
}

void CompilerStack::setRevertStringBehaviour(RevertStrings _revertStrings)
{
if (m_stackState >= ParsingPerformed)
if (m_stackState >= ParsedAndImported)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set revert string settings before parsing."));
solUnimplementedAssert(_revertStrings != RevertStrings::VerboseDebug, "");
m_revertStrings = _revertStrings;
}

void CompilerStack::useMetadataLiteralSources(bool _metadataLiteralSources)
{
if (m_stackState >= ParsingPerformed)
if (m_stackState >= ParsedAndImported)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set use literal sources before parsing."));
m_metadataLiteralSources = _metadataLiteralSources;
}

void CompilerStack::setMetadataHash(MetadataHash _metadataHash)
{
if (m_stackState >= ParsingPerformed)
if (m_stackState >= ParsedAndImported)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set metadata hash before parsing."));
m_metadataHash = _metadataHash;
}

void CompilerStack::addSMTLib2Response(h256 const& _hash, string const& _response)
{
if (m_stackState >= ParsingPerformed)
if (m_stackState >= ParsedAndImported)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must add SMTLib2 responses before parsing."));
m_smtlib2Responses[_hash] = _response;
}
Expand Down Expand Up @@ -247,6 +247,7 @@ bool CompilerStack::parse()
vector<string> sourcesToParse;
for (auto const& s: m_sources)
sourcesToParse.push_back(s.first);

for (size_t i = 0; i < sourcesToParse.size(); ++i)
{
string const& path = sourcesToParse[i];
Expand All @@ -258,17 +259,21 @@ bool CompilerStack::parse()
else
{
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 (m_stopAfter >= ParsedAndImported)
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);
}
}
}

m_stackState = ParsingPerformed;
if (m_stopAfter <= Parsed)
m_stackState = Parsed;
else
m_stackState = ParsedAndImported;
if (!Error::containsOnlyWarnings(m_errorReporter.errors()))
m_hasError = true;
return !m_hasError;
Expand All @@ -290,13 +295,13 @@ void CompilerStack::importASTs(map<string, Json::Value> const& _sources)
source.scanner = scanner;
m_sources[path] = source;
}
m_stackState = ParsingPerformed;
m_stackState = ParsedAndImported;
m_importedSources = true;
}

bool CompilerStack::analyze()
{
if (m_stackState != ParsingPerformed || m_stackState >= AnalysisPerformed)
if (m_stackState != ParsedAndImported || m_stackState >= AnalysisPerformed)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must call analyze only after parsing was performed."));
resolveImports();

Expand Down Expand Up @@ -467,9 +472,13 @@ bool CompilerStack::analyze()
return !m_hasError;
}

bool CompilerStack::parseAndAnalyze()
bool CompilerStack::parseAndAnalyze(State _stopAfter)
{
m_stopAfter = _stopAfter;

bool success = parse();
if (m_stackState >= m_stopAfter)
return success;
if (success || m_parserErrorRecovery)
success = analyze();
return success;
Expand Down Expand Up @@ -500,12 +509,16 @@ bool CompilerStack::isRequestedContract(ContractDefinition const& _contract) con
return false;
}

bool CompilerStack::compile()
bool CompilerStack::compile(State _stopAfter)
{
m_stopAfter = _stopAfter;
if (m_stackState < AnalysisPerformed)
if (!parseAndAnalyze())
return false;

if (m_stackState >= m_stopAfter)
return true;

if (m_hasError)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called compile with errors."));

Expand Down Expand Up @@ -539,11 +552,15 @@ void CompilerStack::link()

vector<string> CompilerStack::contractNames() const
{
if (m_stackState < AnalysisPerformed)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
vector<string> contractNames;
for (auto const& contract: m_contracts)
contractNames.push_back(contract.first);

for (Source const* source: m_sourceOrder)
if (source->ast)
for (
ContractDefinition const* contract:
ASTNode::filteredNodes<ContractDefinition>(source->ast->nodes())
)
contractNames.emplace_back(contract->fullyQualifiedName());
return contractNames;
}

Expand Down Expand Up @@ -834,7 +851,7 @@ Scanner const& CompilerStack::scanner(string const& _sourceName) const

SourceUnit const& CompilerStack::ast(string const& _sourceName) const
{
if (m_stackState < ParsingPerformed)
if (m_stackState < Parsed)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing not yet performed."));
if (!source(_sourceName).ast && !m_parserErrorRecovery)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
Expand Down Expand Up @@ -907,7 +924,7 @@ string const& CompilerStack::Source::ipfsUrl() const

StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string const& _sourcePath)
{
solAssert(m_stackState < ParsingPerformed, "");
solAssert(m_stackState < ParsedAndImported, "");
StringMap newSources;
try
{
Expand Down Expand Up @@ -951,7 +968,7 @@ StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string

string CompilerStack::applyRemapping(string const& _path, string const& _context)
{
solAssert(m_stackState < ParsingPerformed, "");
solAssert(m_stackState < ParsedAndImported, "");
// Try to find the longest prefix match in all remappings that are active in the current context.
auto isPrefixOf = [](string const& _a, string const& _b)
{
Expand Down Expand Up @@ -993,7 +1010,7 @@ string CompilerStack::applyRemapping(string const& _path, string const& _context

void CompilerStack::resolveImports()
{
solAssert(m_stackState == ParsingPerformed, "");
solAssert(m_stackState == ParsedAndImported, "");

// topological sorting (depth first search) of the import graph, cutting potential cycles
vector<Source const*> sourceOrder;
Expand Down
8 changes: 5 additions & 3 deletions libsolidity/interface/CompilerStack.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ class CompilerStack: boost::noncopyable
enum State {
Empty,
SourcesSet,
ParsingPerformed,
Parsed,
ParsedAndImported,
AnalysisPerformed,
CompilationSuccessful
};
Expand Down Expand Up @@ -213,11 +214,11 @@ class CompilerStack: boost::noncopyable

/// Parses and analyzes all source units that were added
/// @returns false on error.
bool parseAndAnalyze();
bool parseAndAnalyze(State _stopAfter = State::CompilationSuccessful);

/// Compiles the source units that were previously added and parsed.
/// @returns false on error.
bool compile();
bool compile(State _stopAfter = State::CompilationSuccessful);

/// @returns the list of sources (paths) used
std::vector<std::string> sourceNames() const;
Expand Down Expand Up @@ -434,6 +435,7 @@ class CompilerStack: boost::noncopyable
ReadCallback::Callback m_readFile;
OptimiserSettings m_optimiserSettings;
RevertStrings m_revertStrings = RevertStrings::Default;
State m_stopAfter = State::CompilationSuccessful;
langutil::EVMVersion m_evmVersion;
smtutil::SMTSolverChoice m_enabledSMTSolvers;
std::map<std::string, std::set<std::string>> m_requestedContractNames;
Expand Down
21 changes: 19 additions & 2 deletions libsolidity/interface/StandardCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ std::optional<Json::Value> checkAuxiliaryInputKeys(Json::Value const& _input)

std::optional<Json::Value> checkSettingsKeys(Json::Value const& _input)
{
static set<string> keys{"parserErrorRecovery", "debug", "evmVersion", "libraries", "metadata", "optimizer", "outputSelection", "remappings"};
static set<string> keys{"parserErrorRecovery", "debug", "evmVersion", "libraries", "metadata", "optimizer", "outputSelection", "remappings", "stopAfter"};
return checkKeys(_input, keys, "settings");
}

Expand Down Expand Up @@ -694,6 +694,17 @@ std::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompiler:
if (auto result = checkSettingsKeys(settings))
return *result;

if (settings.isMember("stopAfter"))
{
if (!settings["stopAfter"].isString())
return formatFatalError("JSONError", "\"settings.stopAfter\" must be a string.");

if (settings["stopAfter"].asString() != "parsing")
return formatFatalError("JSONError", "Invalid value for \"settings.stopAfter\". Only valid value is \"parsing\".");

ret.stopAfter = CompilerStack::State::Parsed;
}

if (settings.isMember("parserErrorRecovery"))
{
if (!settings["parserErrorRecovery"].isBool())
Expand Down Expand Up @@ -819,6 +830,12 @@ std::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompiler:

ret.outputSelection = std::move(outputSelection);

if (ret.stopAfter != CompilerStack::State::CompilationSuccessful && isBinaryRequested(ret.outputSelection))
return formatFatalError(
"JSONError",
"Requested output selection conflicts with \"settings.stopAfter\"."
);

return { std::move(ret) };
}

Expand Down Expand Up @@ -853,7 +870,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
if (binariesRequested)
compilerStack.compile();
else
compilerStack.parseAndAnalyze();
compilerStack.parseAndAnalyze(_inputsAndSettings.stopAfter);

for (auto const& error: compilerStack.errors())
{
Expand Down
1 change: 1 addition & 0 deletions libsolidity/interface/StandardCompiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class StandardCompiler: boost::noncopyable
std::string language;
Json::Value errors;
bool parserErrorRecovery = false;
CompilerStack::State stopAfter = CompilerStack::State::CompilationSuccessful;
std::map<std::string, std::string> sources;
std::map<util::h256, std::string> smtLib2Responses;
langutil::EVMVersion evmVersion;
Expand Down
Loading

0 comments on commit 0a24a29

Please sign in to comment.