[solc] Add --import-asm-json input mode.

This commit is contained in:
Alexander Arlt 2021-11-04 16:53:32 -05:00
parent 7334420423
commit 235af2b4d1
4 changed files with 190 additions and 142 deletions

View File

@ -634,6 +634,7 @@ bool CommandLineInterface::processInput()
break;
case InputMode::Compiler:
case InputMode::CompilerWithASTImport:
case InputMode::CompilerWithEvmAssemblyJsonImport:
if (!compile())
return false;
outputCompilationResults();
@ -657,105 +658,112 @@ void CommandLineInterface::printLicense()
bool CommandLineInterface::compile()
{
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
solAssert(
m_options.input.mode == InputMode::Compiler ||
m_options.input.mode == InputMode::CompilerWithASTImport ||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport, ""
);
m_compiler = make_unique<CompilerStack>(m_fileReader.reader());
SourceReferenceFormatter formatter(serr(false), *m_compiler, coloredOutput(m_options), m_options.formatting.withErrorIds);
try
if (m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport)
{
if (m_options.metadata.literalSources)
m_compiler->useMetadataLiteralSources(true);
m_compiler->setMetadataHash(m_options.metadata.hash);
if (m_options.modelChecker.initialize)
m_compiler->setModelCheckerSettings(m_options.modelChecker.settings);
m_compiler->setRemappings(m_options.input.remappings);
m_compiler->setLibraries(m_options.linker.libraries);
m_compiler->setViaIR(m_options.output.experimentalViaIR);
m_compiler->setEVMVersion(m_options.output.evmVersion);
m_compiler->setRevertStringBehaviour(m_options.output.revertStrings);
if (m_options.output.debugInfoSelection.has_value())
m_compiler->selectDebugInfo(m_options.output.debugInfoSelection.value());
// TODO: Perhaps we should not compile unless requested
m_compiler->enableIRGeneration(m_options.compiler.outputs.ir || m_options.compiler.outputs.irOptimized);
m_compiler->enableEwasmGeneration(m_options.compiler.outputs.ewasm);
m_compiler->enableEvmBytecodeGeneration(
m_options.compiler.estimateGas ||
m_options.compiler.outputs.asm_ ||
m_options.compiler.outputs.asmJson ||
m_options.compiler.outputs.opcodes ||
m_options.compiler.outputs.binary ||
m_options.compiler.outputs.binaryRuntime ||
(m_options.compiler.combinedJsonRequests && (
m_options.compiler.combinedJsonRequests->binary ||
m_options.compiler.combinedJsonRequests->binaryRuntime ||
m_options.compiler.combinedJsonRequests->opcodes ||
m_options.compiler.combinedJsonRequests->asm_ ||
m_options.compiler.combinedJsonRequests->generatedSources ||
m_options.compiler.combinedJsonRequests->generatedSourcesRuntime ||
m_options.compiler.combinedJsonRequests->srcMap ||
m_options.compiler.combinedJsonRequests->srcMapRuntime ||
m_options.compiler.combinedJsonRequests->funDebug ||
m_options.compiler.combinedJsonRequests->funDebugRuntime
))
);
}
else
{
SourceReferenceFormatter
formatter(serr(false), *m_compiler, coloredOutput(m_options), m_options.formatting.withErrorIds);
m_compiler->setOptimiserSettings(m_options.optimiserSettings());
if (m_options.input.mode == InputMode::CompilerWithASTImport)
try
{
try
{
m_compiler->importASTs(parseAstFromInput());
if (m_options.metadata.literalSources)
m_compiler->useMetadataLiteralSources(true);
m_compiler->setMetadataHash(m_options.metadata.hash);
if (m_options.modelChecker.initialize)
m_compiler->setModelCheckerSettings(m_options.modelChecker.settings);
m_compiler->setRemappings(m_options.input.remappings);
m_compiler->setLibraries(m_options.linker.libraries);
m_compiler->setViaIR(m_options.output.experimentalViaIR);
m_compiler->setEVMVersion(m_options.output.evmVersion);
m_compiler->setRevertStringBehaviour(m_options.output.revertStrings);
if (m_options.output.debugInfoSelection.has_value())
m_compiler->selectDebugInfo(m_options.output.debugInfoSelection.value());
// TODO: Perhaps we should not compile unless requested
if (!m_compiler->analyze())
m_compiler->enableIRGeneration(m_options.compiler.outputs.ir || m_options.compiler.outputs.irOptimized);
m_compiler->enableEwasmGeneration(m_options.compiler.outputs.ewasm);
m_compiler->enableEvmBytecodeGeneration(
m_options.compiler.estimateGas || m_options.compiler.outputs.asm_ || m_options.compiler.outputs.asmJson
|| m_options.compiler.outputs.opcodes || m_options.compiler.outputs.binary
|| m_options.compiler.outputs.binaryRuntime
|| (m_options.compiler.combinedJsonRequests
&& (m_options.compiler.combinedJsonRequests->binary
|| m_options.compiler.combinedJsonRequests->binaryRuntime
|| m_options.compiler.combinedJsonRequests->opcodes
|| m_options.compiler.combinedJsonRequests->asm_
|| m_options.compiler.combinedJsonRequests->generatedSources
|| m_options.compiler.combinedJsonRequests->generatedSourcesRuntime
|| m_options.compiler.combinedJsonRequests->srcMap
|| m_options.compiler.combinedJsonRequests->srcMapRuntime
|| m_options.compiler.combinedJsonRequests->funDebug
|| m_options.compiler.combinedJsonRequests->funDebugRuntime)));
m_compiler->setOptimiserSettings(m_options.optimiserSettings());
if (m_options.input.mode == InputMode::CompilerWithASTImport)
{
try
{
formatter.printErrorInformation(m_compiler->errors());
astAssert(false, "Analysis of the AST failed");
m_compiler->importASTs(parseAstFromInput());
if (!m_compiler->analyze())
{
formatter.printErrorInformation(m_compiler->errors());
astAssert(false, "Analysis of the AST failed");
}
}
catch (Exception const& _exc)
{
serr() << string("Failed to import AST: ") << _exc.what() << endl;
return false;
}
}
catch (Exception const& _exc)
else
{
serr() << string("Failed to import AST: ") << _exc.what() << endl;
return false;
m_compiler->setSources(m_fileReader.sourceCodes());
m_compiler->setParserErrorRecovery(m_options.input.errorRecovery);
}
}
else
{
m_compiler->setSources(m_fileReader.sourceCodes());
m_compiler->setParserErrorRecovery(m_options.input.errorRecovery);
}
bool successful = m_compiler->compile(m_options.output.stopAfter);
bool successful = m_compiler->compile(m_options.output.stopAfter);
for (auto const& error: m_compiler->errors())
for (auto const& error: m_compiler->errors())
{
m_hasOutput = true;
formatter.printErrorInformation(*error);
}
if (!successful)
return m_options.input.errorRecovery;
}
catch (CompilerError const& _exception)
{
m_hasOutput = true;
formatter.printErrorInformation(*error);
formatter.printExceptionInformation(_exception, "Compiler error");
return false;
}
if (!successful)
return m_options.input.errorRecovery;
}
catch (CompilerError const& _exception)
{
m_hasOutput = true;
formatter.printExceptionInformation(_exception, "Compiler error");
return false;
}
catch (Error const& _error)
{
if (_error.type() == Error::Type::DocstringParsingError)
serr() << "Documentation parsing error: " << *boost::get_error_info<errinfo_comment>(_error) << endl;
else
catch (Error const& _error)
{
m_hasOutput = true;
formatter.printExceptionInformation(_error, _error.typeName());
}
if (_error.type() == Error::Type::DocstringParsingError)
serr() << "Documentation parsing error: " << *boost::get_error_info<errinfo_comment>(_error) << endl;
else
{
m_hasOutput = true;
formatter.printExceptionInformation(_error, _error.typeName());
}
return false;
return false;
}
}
return true;
@ -763,7 +771,11 @@ bool CommandLineInterface::compile()
void CommandLineInterface::handleCombinedJSON()
{
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
solAssert(
m_options.input.mode == InputMode::Compiler ||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport ||
m_options.input.mode == InputMode::CompilerWithASTImport, ""
);
if (!m_options.compiler.combinedJsonRequests.has_value())
return;
@ -1095,68 +1107,77 @@ bool CommandLineInterface::assemble(yul::AssemblyStack::Language _language, yul:
void CommandLineInterface::outputCompilationResults()
{
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
solAssert(
m_options.input.mode == InputMode::Compiler ||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport ||
m_options.input.mode == InputMode::CompilerWithASTImport, ""
);
handleCombinedJSON();
// do we need AST output?
handleAst();
if (
!m_compiler->compilationSuccessful() &&
m_options.output.stopAfter == CompilerStack::State::CompilationSuccessful
)
if (m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport)
{
serr() << endl << "Compilation halted after AST generation due to errors." << endl;
return;
handleBytecode("");
}
vector<string> contracts = m_compiler->contractNames();
for (string const& contract: contracts)
else
{
if (needsHumanTargetedStdout(m_options))
sout() << endl << "======= " << contract << " =======" << endl;
// do we need AST output?
handleAst();
// do we need EVM assembly?
if (m_options.compiler.outputs.asm_ || m_options.compiler.outputs.asmJson)
if (!m_compiler->compilationSuccessful()
&& m_options.output.stopAfter == CompilerStack::State::CompilationSuccessful)
{
string ret;
if (m_options.compiler.outputs.asmJson)
ret = jsonPrettyPrint(removeNullMembers(m_compiler->assemblyJSON(contract)));
else
ret = m_compiler->assemblyString(contract, m_fileReader.sourceCodes());
if (!m_options.output.dir.empty())
{
createFile(m_compiler->filesystemFriendlyName(contract) + (m_options.compiler.outputs.asmJson ? "_evm.json" : ".evm"), ret);
}
else
{
sout() << "EVM assembly:" << endl << ret << endl;
}
serr() << endl << "Compilation halted after AST generation due to errors." << endl;
return;
}
if (m_options.compiler.estimateGas)
handleGasEstimation(contract);
vector<string> contracts = m_compiler->contractNames();
for (string const& contract: contracts)
{
if (needsHumanTargetedStdout(m_options))
sout() << endl << "======= " << contract << " =======" << endl;
handleBytecode(contract);
handleIR(contract);
handleIROptimized(contract);
handleEwasm(contract);
handleSignatureHashes(contract);
handleMetadata(contract);
handleABI(contract);
handleStorageLayout(contract);
handleNatspec(true, contract);
handleNatspec(false, contract);
} // end of contracts iteration
// do we need EVM assembly?
if (m_options.compiler.outputs.asm_ || m_options.compiler.outputs.asmJson)
{
string ret;
if (m_options.compiler.outputs.asmJson)
ret = jsonPrettyPrint(removeNullMembers(m_compiler->assemblyJSON(contract)));
else
ret = m_compiler->assemblyString(contract, m_fileReader.sourceCodes());
if (!m_hasOutput)
{
if (!m_options.output.dir.empty())
sout() << "Compiler run successful. Artifact(s) can be found in directory " << m_options.output.dir << "." << endl;
else
serr() << "Compiler run successful, no output requested." << endl;
if (!m_options.output.dir.empty())
{
createFile(m_compiler->filesystemFriendlyName(contract) + (m_options.compiler.outputs.asmJson ? "_evm.json" : ".evm"), ret);
}
else
{
sout() << "EVM assembly:" << endl << ret << endl;
}
}
if (m_options.compiler.estimateGas)
handleGasEstimation(contract);
handleBytecode(contract);
handleIR(contract);
handleIROptimized(contract);
handleEwasm(contract);
handleSignatureHashes(contract);
handleMetadata(contract);
handleABI(contract);
handleStorageLayout(contract);
handleNatspec(true, contract);
handleNatspec(false, contract);
} // end of contracts iteration
if (!m_hasOutput)
{
if (!m_options.output.dir.empty())
sout() << "Compiler run successful. Artifact(s) can be found in directory " << m_options.output.dir << "." << endl;
else
serr() << "Compiler run successful, no output requested." << endl;
}
}
}

View File

@ -46,6 +46,7 @@ static string const g_strAllowPaths = "allow-paths";
static string const g_strBasePath = "base-path";
static string const g_strIncludePath = "include-path";
static string const g_strAssemble = "assemble";
static string const g_strImportEvmAssemblerJson = "import-asm-json";
static string const g_strCombinedJson = "combined-json";
static string const g_strErrorRecovery = "error-recovery";
static string const g_strEVM = "evm";
@ -134,6 +135,7 @@ static map<InputMode, string> const g_inputModeName = {
{InputMode::Compiler, "compiler"},
{InputMode::CompilerWithASTImport, "compiler (AST import)"},
{InputMode::Assembler, "assembler"},
{InputMode::CompilerWithEvmAssemblyJsonImport, "assembler (EVM ASM JSON import)"},
{InputMode::StandardJson, "standard JSON"},
{InputMode::Linker, "linker"},
};
@ -321,7 +323,20 @@ bool CommandLineParser::parseInputPathsAndRemappings()
m_options.input.paths.insert(positionalArg);
}
if (m_options.input.mode == InputMode::StandardJson)
if (m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport)
{
if (m_options.input.paths.size() > 1 || (m_options.input.paths.size() == 1 && m_options.input.addStdin))
{
serr() << "Too many input files for --" << g_strImportEvmAssemblerJson << "." << endl;
serr() << "Please either specify a single file name or provide its content on standard input." << endl;
return false;
}
else if (m_options.input.paths.size() == 0)
// Standard JSON mode input used to be handled separately and zero files meant "read from stdin".
// Keep it working that way for backwards-compatibility.
m_options.input.addStdin = true;
}
else if (m_options.input.mode == InputMode::StandardJson)
{
if (m_options.input.paths.size() > 1 || (m_options.input.paths.size() == 1 && m_options.input.addStdin))
{
@ -466,6 +481,7 @@ bool CommandLineParser::parseOutputSelection()
case InputMode::Version:
solAssert(false);
case InputMode::Compiler:
case InputMode::CompilerWithEvmAssemblyJsonImport:
case InputMode::CompilerWithASTImport:
return contains(compilerModeOutputs, _outputName);
case InputMode::Assembler:
@ -641,8 +657,12 @@ General Information)").c_str(),
(
g_strImportAst.c_str(),
("Import ASTs to be compiled, assumes input holds the AST in compact JSON format. "
"Supported Inputs is the output of the --" + g_strStandardJSON + " or the one produced by "
"--" + g_strCombinedJson + " " + CombinedJsonRequests::componentName(&CombinedJsonRequests::ast)).c_str()
"Supported Inputs is the output of the --" + g_strStandardJSON + " or the one produced by "
"--" + g_strCombinedJson + " " + CombinedJsonRequests::componentName(&CombinedJsonRequests::ast)).c_str()
)
(
g_strImportEvmAssemblerJson.c_str(),
"Import evm assembler json to be compiled, assumes input holds the evm assembly in JSON format."
)
;
desc.add(alternativeInputModes);
@ -879,6 +899,7 @@ bool CommandLineParser::processArgs()
g_strStrictAssembly,
g_strYul,
g_strImportAst,
g_strImportEvmAssemblerJson,
}))
return false;
@ -892,6 +913,8 @@ bool CommandLineParser::processArgs()
m_options.input.mode = InputMode::StandardJson;
else if (m_args.count(g_strAssemble) > 0 || m_args.count(g_strStrictAssembly) > 0 || m_args.count(g_strYul) > 0)
m_options.input.mode = InputMode::Assembler;
else if (m_args.count(g_strImportEvmAssemblerJson) > 0)
m_options.input.mode = InputMode::CompilerWithEvmAssemblyJsonImport;
else if (m_args.count(g_strLink) > 0)
m_options.input.mode = InputMode::Linker;
else if (m_args.count(g_strImportAst) > 0)
@ -946,8 +969,8 @@ bool CommandLineParser::processArgs()
if (
m_options.input.mode != InputMode::Compiler &&
m_options.input.mode != InputMode::CompilerWithASTImport &&
m_options.input.mode != InputMode::Assembler
)
m_options.input.mode != InputMode::Assembler &&
m_options.input.mode != InputMode::CompilerWithEvmAssemblyJsonImport)
{
if (!m_args[g_strOptimizeRuns].defaulted())
{
@ -1127,16 +1150,18 @@ bool CommandLineParser::processArgs()
m_options.optimizer.yulSteps = m_args[g_strYulOptimizations].as<string>();
}
if (m_options.input.mode == InputMode::Assembler)
if (m_options.input.mode == InputMode::Assembler ||
m_options.input.mode == InputMode::CompilerWithEvmAssemblyJsonImport)
{
vector<string> const nonAssemblyModeOptions = {
vector<string> nonAssemblyModeOptions = {
// TODO: The list is not complete. Add more.
g_strOutputDir,
g_strGas,
g_strCombinedJson,
g_strOptimizeYul,
g_strNoOptimizeYul,
};
if (m_options.input.mode == InputMode::Assembler)
nonAssemblyModeOptions.emplace_back(g_strCombinedJson);
if (countEnabledOptions(nonAssemblyModeOptions) >= 1)
{
auto optionEnabled = [&](string const& name){ return m_args.count(name) > 0; };
@ -1206,9 +1231,10 @@ bool CommandLineParser::processArgs()
serr() << "and automatic translation is not available." << endl;
return false;
}
serr() <<
"Warning: Yul is still experimental. Please use the output with care." <<
endl;
if (m_options.input.mode == InputMode::Assembler)
serr() <<
"Warning: Yul is still experimental. Please use the output with care." <<
endl;
return true;
}

View File

@ -56,6 +56,7 @@ enum class InputMode
StandardJson,
Linker,
Assembler,
CompilerWithEvmAssemblyJsonImport,
};
struct CompilerOutputs

View File

@ -157,7 +157,7 @@ BOOST_AUTO_TEST_CASE(multiple_input_modes)
};
string expectedMessage =
"The following options are mutually exclusive: "
"--help, --license, --version, --standard-json, --link, --assemble, --strict-assembly, --yul, --import-ast. "
"--help, --license, --version, --standard-json, --link, --assemble, --strict-assembly, --yul, --import-ast, --import-asm-json. "
"Select at most one.\n";
for (string const& mode1: inputModeOptions)