Skip to content

Commit 1667716

Browse files
committed
[solc] Enable ethdebug debug info and output selection.
1 parent 38088a1 commit 1667716

File tree

44 files changed

+560
-22
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+560
-22
lines changed

liblangutil/DebugInfoSelection.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,22 @@ DebugInfoSelection const DebugInfoSelection::Only(bool DebugInfoSelection::* _me
4949
return result;
5050
}
5151

52+
DebugInfoSelection const DebugInfoSelection::Except(std::vector<bool DebugInfoSelection::*> const& _members) noexcept
53+
{
54+
DebugInfoSelection result = All();
55+
for (bool DebugInfoSelection::* member: _members)
56+
result.*member = false;
57+
return result;
58+
}
59+
5260
std::optional<DebugInfoSelection> DebugInfoSelection::fromString(std::string_view _input)
5361
{
5462
// TODO: Make more stuff constexpr and make it a static_assert().
5563
solAssert(componentMap().count("all") == 0, "");
5664
solAssert(componentMap().count("none") == 0, "");
5765

5866
if (_input == "all")
59-
return All();
67+
return ExceptExperimental();
6068
if (_input == "none")
6169
return None();
6270

@@ -74,7 +82,7 @@ std::optional<DebugInfoSelection> DebugInfoSelection::fromComponents(
7482
for (auto const& component: _componentNames)
7583
{
7684
if (component == "*")
77-
return (_acceptWildcards ? std::make_optional(DebugInfoSelection::All()) : std::nullopt);
85+
return (_acceptWildcards ? std::make_optional(ExceptExperimental()) : std::nullopt);
7886

7987
if (!selection.enable(component))
8088
return std::nullopt;

liblangutil/DebugInfoSelection.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ struct DebugInfoSelection
4242
static DebugInfoSelection const All(bool _value = true) noexcept;
4343
static DebugInfoSelection const None() noexcept { return All(false); }
4444
static DebugInfoSelection const Only(bool DebugInfoSelection::* _member) noexcept;
45-
static DebugInfoSelection const Default() noexcept { return All(); }
45+
static DebugInfoSelection const Default() noexcept { return ExceptExperimental(); }
46+
static DebugInfoSelection const Except(std::vector<bool DebugInfoSelection::*> const& _members) noexcept;
47+
static DebugInfoSelection const ExceptExperimental() noexcept { return Except({&DebugInfoSelection::ethdebug}); }
4648

4749
static std::optional<DebugInfoSelection> fromString(std::string_view _input);
4850
static std::optional<DebugInfoSelection> fromComponents(
@@ -72,13 +74,15 @@ struct DebugInfoSelection
7274
{"location", &DebugInfoSelection::location},
7375
{"snippet", &DebugInfoSelection::snippet},
7476
{"ast-id", &DebugInfoSelection::astID},
77+
{"ethdebug", &DebugInfoSelection::ethdebug},
7578
};
7679
return components;
7780
}
7881

7982
bool location = false; ///< Include source location. E.g. `@src 3:50:100`
8083
bool snippet = false; ///< Include source code snippet next to location. E.g. `@src 3:50:100 "contract C {..."`
8184
bool astID = false; ///< Include ID of the Solidity AST node. E.g. `@ast-id 15`
85+
bool ethdebug = false; ///< Include ethdebug related debug information.
8286
};
8387

8488
std::ostream& operator<<(std::ostream& _stream, DebugInfoSelection const& _selection);

libsolidity/codegen/ir/IRGenerator.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ std::string IRGenerator::generate(
130130
);
131131
};
132132

133-
Whiskers t(R"(
133+
Whiskers t(R"(<?isEthdebugEnabled>/// ethdebug: enabled</isEthdebugEnabled>
134134
/// @use-src <useSrcMapCreation>
135135
object "<CreationObject>" {
136136
code {
@@ -167,6 +167,7 @@ std::string IRGenerator::generate(
167167
for (VariableDeclaration const* var: ContractType(_contract).immutableVariables())
168168
m_context.registerImmutableVariable(*var);
169169

170+
t("isEthdebugEnabled", m_context.debugInfoSelection().ethdebug);
170171
t("CreationObject", IRNames::creationObject(_contract));
171172
t("sourceLocationCommentCreation", dispenseLocationComment(_contract));
172173
t("library", _contract.isLibrary());

libsolidity/interface/CompilerStack.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,6 +1091,14 @@ Json CompilerStack::interfaceSymbols(std::string const& _contractName) const
10911091
return interfaceSymbols;
10921092
}
10931093

1094+
Json CompilerStack::ethdebug(std::string const& _contractName) const
1095+
{
1096+
(void)_contractName;
1097+
1098+
Json ethdebug = Json::object();
1099+
return ethdebug;
1100+
}
1101+
10941102
bytes CompilerStack::cborMetadata(std::string const& _contractName, bool _forIR) const
10951103
{
10961104
solAssert(m_stackState >= AnalysisSuccessful, "Analysis was not successful.");

libsolidity/interface/CompilerStack.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,10 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac
353353
/// @returns a JSON object with the three members ``methods``, ``events``, ``errors``. Each is a map, mapping identifiers (hashes) to function names.
354354
Json interfaceSymbols(std::string const& _contractName) const;
355355

356+
/// @returns a JSON representing the ethdebug data of the specified contract.
357+
/// Prerequisite: Successful call to parse or compile.
358+
Json ethdebug(std::string const& _contractName) const;
359+
356360
/// @returns the Contract Metadata matching the pipeline selected using the viaIR setting.
357361
std::string const& metadata(std::string const& _contractName) const { return metadata(contract(_contractName)); }
358362

libsolidity/interface/StandardCompiler.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1321,10 +1321,18 @@ Json StandardCompiler::compileSolidity(StandardCompiler::InputsAndSettings _inpu
13211321
compilerStack.setModelCheckerSettings(_inputsAndSettings.modelCheckerSettings);
13221322

13231323
compilerStack.enableEvmBytecodeGeneration(isEvmBytecodeRequested(_inputsAndSettings.outputSelection));
1324-
compilerStack.requestIROutputs(irOutputSelection(_inputsAndSettings.outputSelection));
1324+
CompilerStack::IROutputSelection selectedIrOutput = irOutputSelection(_inputsAndSettings.outputSelection);
1325+
compilerStack.requestIROutputs(selectedIrOutput);
13251326

13261327
Json errors = std::move(_inputsAndSettings.errors);
13271328

1329+
if (
1330+
_inputsAndSettings.debugInfoSelection.has_value() &&
1331+
_inputsAndSettings.debugInfoSelection->ethdebug &&
1332+
(selectedIrOutput == CompilerStack::IROutputSelection::None && !_inputsAndSettings.viaIR)
1333+
)
1334+
errors.emplace_back(formatError(Error::Type::FatalError, "general", "'ethdebug' can only be selected in 'settings.debug.debugInfo' when at least one of the IR outputs is selected or 'viaIR' was set."));
1335+
13281336
bool const binariesRequested = isBinaryRequested(_inputsAndSettings.outputSelection);
13291337

13301338
try

libyul/YulStack.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -360,12 +360,13 @@ std::string YulStack::print(
360360
yulAssert(m_stackState >= Parsed);
361361
yulAssert(m_parserResult, "");
362362
yulAssert(m_parserResult->code, "");
363-
return m_parserResult->toString(
364-
languageToDialect(m_language, m_evmVersion),
365-
AsmPrinter::TypePrinting::OmitDefault,
366-
m_debugInfoSelection,
367-
_soliditySourceProvider
368-
) + "\n";
363+
return (m_debugInfoSelection.ethdebug ? "/// ethdebug: enabled\n" : "") +
364+
m_parserResult->toString(
365+
languageToDialect(m_language, m_evmVersion),
366+
AsmPrinter::TypePrinting::OmitDefault,
367+
m_debugInfoSelection,
368+
_soliditySourceProvider
369+
) + "\n";
369370
}
370371

371372
Json YulStack::astJson() const

solc/CommandLineInterface.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,8 @@ static bool needsHumanTargetedStdout(CommandLineOptions const& _options)
156156
_options.compiler.outputs.opcodes ||
157157
_options.compiler.outputs.signatureHashes ||
158158
_options.compiler.outputs.storageLayout ||
159-
_options.compiler.outputs.transientStorageLayout;
159+
_options.compiler.outputs.transientStorageLayout ||
160+
_options.compiler.outputs.ethdebug;
160161
}
161162

162163
static bool coloredOutput(CommandLineOptions const& _options)
@@ -520,6 +521,20 @@ void CommandLineInterface::handleGasEstimation(std::string const& _contract)
520521
}
521522
}
522523

524+
void CommandLineInterface::handleEthdebug(std::string const& _contract)
525+
{
526+
solAssert(CompilerInputModes.count(m_options.input.mode) == 1);
527+
528+
if (!m_options.compiler.outputs.ethdebug)
529+
return;
530+
531+
std::string data = jsonPrint(removeNullMembers(m_compiler->ethdebug(_contract)), m_options.formatting.json);
532+
if (!m_options.output.dir.empty())
533+
createFile(m_compiler->filesystemFriendlyName(_contract) + "_ethdebug.json", data);
534+
else
535+
sout() << "Debug Data (ethdebug):" << std::endl << data << std::endl;
536+
}
537+
523538
void CommandLineInterface::readInputFiles()
524539
{
525540
solAssert(!m_standardJsonInput.has_value());
@@ -1342,6 +1357,7 @@ void CommandLineInterface::outputCompilationResults()
13421357
handleTransientStorageLayout(contract);
13431358
handleNatspec(true, contract);
13441359
handleNatspec(false, contract);
1360+
handleEthdebug(contract);
13451361
} // end of contracts iteration
13461362
}
13471363

solc/CommandLineInterface.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ class CommandLineInterface
116116
void handleGasEstimation(std::string const& _contract);
117117
void handleStorageLayout(std::string const& _contract);
118118
void handleTransientStorageLayout(std::string const& _contract);
119+
void handleEthdebug(std::string const& _contract);
119120

120121
/// Tries to read @ m_sourceCodes as a JSONs holding ASTs
121122
/// such that they can be imported into the compiler (importASTs())

solc/CommandLineParser.cpp

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,7 @@ void CommandLineParser::parseOutputSelection()
473473
CompilerOutputs::componentName(&CompilerOutputs::irOptimized),
474474
CompilerOutputs::componentName(&CompilerOutputs::astCompactJson),
475475
CompilerOutputs::componentName(&CompilerOutputs::asmJson),
476+
CompilerOutputs::componentName(&CompilerOutputs::ethdebug),
476477
};
477478
static std::set<std::string> const evmAssemblyJsonImportModeOutputs = {
478479
CompilerOutputs::componentName(&CompilerOutputs::asm_),
@@ -638,7 +639,13 @@ General Information)").c_str(),
638639
po::value<std::string>()->default_value(util::toString(DebugInfoSelection::Default())),
639640
("Debug info components to be included in the produced EVM assembly and Yul code. "
640641
"Value can be all, none or a comma-separated list containing one or more of the "
641-
"following components: " + util::joinHumanReadable(DebugInfoSelection::componentMap() | ranges::views::keys) + ".").c_str()
642+
"following components: " +
643+
util::joinHumanReadable(
644+
DebugInfoSelection::componentMap() | ranges::views::keys |
645+
// Note: We intentionally keep ethdebug undocumented for now.
646+
ranges::views::filter([](std::string const& key) { return key != "ethdebug"; }) |
647+
ranges::to<std::vector>()
648+
) + ".").c_str()
642649
)
643650
(
644651
g_strStopAfter.c_str(),
@@ -763,6 +770,13 @@ General Information)").c_str(),
763770
(CompilerOutputs::componentName(&CompilerOutputs::storageLayout).c_str(), "Slots, offsets and types of the contract's state variables located in storage.")
764771
(CompilerOutputs::componentName(&CompilerOutputs::transientStorageLayout).c_str(), "Slots, offsets and types of the contract's state variables located in transient storage.")
765772
;
773+
if (!_forHelp) // Note: We intentionally keep this undocumented for now.
774+
outputComponents.add_options()
775+
(
776+
CompilerOutputs::componentName(&CompilerOutputs::ethdebug).c_str(),
777+
"Ethdebug output of all contracts."
778+
)
779+
;
766780
desc.add(outputComponents);
767781

768782
po::options_description extraOutput("Extra Output");
@@ -1441,6 +1455,29 @@ void CommandLineParser::processArgs()
14411455
m_options.input.mode == InputMode::CompilerWithASTImport ||
14421456
m_options.input.mode == InputMode::EVMAssemblerJSON
14431457
);
1458+
1459+
if (m_options.compiler.outputs.ethdebug)
1460+
{
1461+
m_options.output.viaIR = true;
1462+
if (m_options.output.debugInfoSelection.has_value())
1463+
m_options.output.debugInfoSelection->ethdebug = true;
1464+
else
1465+
{
1466+
m_options.output.debugInfoSelection = DebugInfoSelection::Default();
1467+
m_options.output.debugInfoSelection->enable("ethdebug");
1468+
}
1469+
}
1470+
1471+
if (
1472+
m_options.output.debugInfoSelection.has_value() && m_options.output.debugInfoSelection->ethdebug &&
1473+
!(m_options.compiler.outputs.ir || m_options.compiler.outputs.irOptimized || m_options.compiler.outputs.ethdebug)
1474+
)
1475+
solThrow(
1476+
CommandLineValidationError,
1477+
"--debug-info ethdebug can only be used with --" + CompilerOutputs::componentName(&CompilerOutputs::ir) +
1478+
", --" + CompilerOutputs::componentName(&CompilerOutputs::irOptimized) +
1479+
" and/or --" + CompilerOutputs::componentName(&CompilerOutputs::ethdebug) + "."
1480+
);
14441481
}
14451482

14461483
void CommandLineParser::parseCombinedJsonOption()

0 commit comments

Comments
 (0)