Skip to content

Commit 1abbdbd

Browse files
committed
Enable ethdebug debug info and output selection.
1 parent 3c94682 commit 1abbdbd

File tree

55 files changed

+2284
-18
lines changed

Some content is hidden

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

55 files changed

+2284
-18
lines changed

libevmasm/AbstractAssemblyStack.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ class AbstractAssemblyStack
4040
virtual std::string const* sourceMapping(std::string const& _contractName) const = 0;
4141
virtual std::string const* runtimeSourceMapping(std::string const& _contractName) const = 0;
4242

43+
virtual Json ethdebug(std::string const& _contractName, bool _runtime) const = 0;
44+
45+
virtual Json ethdebug() const = 0;
46+
4347
virtual Json assemblyJSON(std::string const& _contractName) const = 0;
4448
virtual std::string assemblyString(std::string const& _contractName, StringMap const& _sourceCodes) const = 0;
4549

libevmasm/Assembly.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,7 @@ std::string Assembly::assemblyString(
444444
{
445445
std::ostringstream tmp;
446446
assemblyStream(tmp, _debugInfoSelection, "", _sourceCodes);
447-
return tmp.str();
447+
return (_debugInfoSelection.ethdebug ? "/// ethdebug: enabled\n" : "") + tmp.str();
448448
}
449449

450450
Json Assembly::assemblyJSON(std::map<std::string, unsigned> const& _sourceIndices, bool _includeSourceList) const

libevmasm/EVMAssemblyStack.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,17 @@ std::string const* EVMAssemblyStack::runtimeSourceMapping(std::string const& _co
103103
return &m_runtimeSourceMapping;
104104
}
105105

106+
Json EVMAssemblyStack::ethdebug(std::string const& _contractName, bool _runtime) const
107+
{
108+
solAssert(_contractName == m_name);
109+
return _runtime ? m_runtimeEthdebug : m_ethdebug;
110+
}
111+
112+
Json EVMAssemblyStack::ethdebug() const
113+
{
114+
return {};
115+
}
116+
106117
Json EVMAssemblyStack::assemblyJSON(std::string const& _contractName) const
107118
{
108119
solAssert(_contractName == m_name);

libevmasm/EVMAssemblyStack.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ class EVMAssemblyStack: public AbstractAssemblyStack
5959
virtual std::string const* sourceMapping(std::string const& _contractName) const override;
6060
virtual std::string const* runtimeSourceMapping(std::string const& _contractName) const override;
6161

62+
virtual Json ethdebug(std::string const& _contractName, bool _runtime) const override;
63+
virtual Json ethdebug() const override;
64+
6265
virtual Json assemblyJSON(std::string const& _contractName) const override;
6366
virtual std::string assemblyString(std::string const& _contractName, StringMap const& _sourceCodes) const override;
6467

@@ -87,6 +90,8 @@ class EVMAssemblyStack: public AbstractAssemblyStack
8790
langutil::DebugInfoSelection m_debugInfoSelection = langutil::DebugInfoSelection::Default();
8891
std::string m_sourceMapping;
8992
std::string m_runtimeSourceMapping;
93+
Json m_ethdebug;
94+
Json m_runtimeEthdebug;
9095
};
9196

9297
} // namespace solidity::evmasm

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
@@ -120,7 +120,7 @@ std::string IRGenerator::generate(
120120
);
121121
};
122122

123-
Whiskers t(R"(
123+
Whiskers t(R"(<?isEthdebugEnabled>/// ethdebug: enabled</isEthdebugEnabled>
124124
/// @use-src <useSrcMapCreation>
125125
object "<CreationObject>" {
126126
code {
@@ -164,6 +164,7 @@ std::string IRGenerator::generate(
164164
for (VariableDeclaration const* var: ContractType(_contract).immutableVariables())
165165
m_context.registerImmutableVariable(*var);
166166

167+
t("isEthdebugEnabled", m_context.debugInfoSelection().ethdebug);
167168
t("CreationObject", IRNames::creationObject(_contract));
168169
t("sourceLocationCommentCreation", dispenseLocationComment(_contract));
169170
t("library", _contract.isLibrary());

libsolidity/interface/CompilerStack.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1181,6 +1181,33 @@ Json CompilerStack::interfaceSymbols(std::string const& _contractName) const
11811181
return interfaceSymbols;
11821182
}
11831183

1184+
Json CompilerStack::ethdebug() const
1185+
{
1186+
Json result = Json::object();
1187+
result["sources"] = sourceNames();
1188+
return result;
1189+
}
1190+
1191+
Json CompilerStack::ethdebug(std::string const& _contractName, bool _runtime) const
1192+
{
1193+
solAssert(m_stackState >= AnalysisSuccessful, "Analysis was not successful.");
1194+
return ethdebug(contract(_contractName), _runtime);
1195+
}
1196+
1197+
Json CompilerStack::ethdebug(Contract const& _contract, bool _runtime) const
1198+
{
1199+
solAssert(m_stackState >= AnalysisSuccessful, "Analysis was not successful.");
1200+
solAssert(_contract.contract);
1201+
solUnimplementedAssert(!isExperimentalSolidity());
1202+
if (_runtime)
1203+
{
1204+
Json result = Json::object();
1205+
return result;
1206+
}
1207+
Json result = Json::object();
1208+
return result;
1209+
}
1210+
11841211
bytes CompilerStack::cborMetadata(std::string const& _contractName, bool _forIR) const
11851212
{
11861213
solAssert(m_stackState >= AnalysisSuccessful, "Analysis was not successful.");

libsolidity/interface/CompilerStack.h

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

394+
/// @returns a JSON representing the ethdebug data of the specified contract.
395+
/// Prerequisite: Successful call to parse or compile.
396+
Json ethdebug(std::string const& _contractName, bool _runtime) const override;
397+
398+
/// @returns a JSON representing the top-level ethdebug data (types, etc.).
399+
/// Prerequisite: Successful call to parse or compile.
400+
Json ethdebug() const override;
401+
394402
/// @returns the Contract Metadata matching the pipeline selected using the viaIR setting.
395403
std::string const& metadata(std::string const& _contractName) const { return metadata(contract(_contractName)); }
396404

@@ -571,6 +579,10 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac
571579
/// This will generate the metadata and store it in the Contract object if it is not present yet.
572580
std::string const& metadata(Contract const& _contract) const;
573581

582+
/// @returns the Contract ethdebug data.
583+
/// This will generate the JSON object and store it in the Contract object if it is not present yet.
584+
Json ethdebug(Contract const& _contract, bool _runtime) const;
585+
574586
/// @returns the offset of the entry point of the given function into the list of assembly items
575587
/// or zero if it is not found or does not exist.
576588
size_t functionEntryPoint(

libsolidity/interface/StandardCompiler.cpp

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,21 +165,28 @@ bool hashMatchesContent(std::string const& _hash, std::string const& _content)
165165

166166
bool isArtifactRequested(Json const& _outputSelection, std::string const& _artifact, bool _wildcardMatchesExperimental)
167167
{
168-
static std::set<std::string> experimental{"ir", "irAst", "irOptimized", "irOptimizedAst", "yulCFGJson"};
168+
static std::set<std::string> experimental{"ir", "irAst", "irOptimized", "irOptimizedAst", "yulCFGJson", "ethdebug"};
169169
for (auto const& selectedArtifactJson: _outputSelection)
170170
{
171171
std::string const& selectedArtifact = selectedArtifactJson.get<std::string>();
172172
if (
173173
_artifact == selectedArtifact ||
174174
boost::algorithm::starts_with(_artifact, selectedArtifact + ".")
175175
)
176+
{
177+
if (_artifact.find("ethdebug") != std::string::npos)
178+
// only accept exact matches for ethdebug, e.g. evm.bytecode.ethdebug
179+
return selectedArtifact == _artifact;
176180
return true;
181+
}
177182
else if (selectedArtifact == "*")
178183
{
179184
// TODO: yulCFGJson is only experimental now, so it should not be matched by "*".
180185
if (_artifact == "yulCFGJson")
181186
return false;
182187
// "ir", "irOptimized" can only be matched by "*" if activated.
188+
if (_artifact.find("ethdebug") != std::string::npos)
189+
return false;
183190
if (experimental.count(_artifact) == 0 || _wildcardMatchesExperimental)
184191
return true;
185192
}
@@ -237,7 +244,7 @@ bool isArtifactRequested(Json const& _outputSelection, std::string const& _file,
237244
std::vector<std::string> evmObjectComponents(std::string const& _objectKind)
238245
{
239246
solAssert(_objectKind == "bytecode" || _objectKind == "deployedBytecode", "");
240-
std::vector<std::string> components{"", ".object", ".opcodes", ".sourceMap", ".functionDebugData", ".generatedSources", ".linkReferences"};
247+
std::vector<std::string> components{"", ".object", ".opcodes", ".sourceMap", ".functionDebugData", ".generatedSources", ".linkReferences", ".ethdebug"};
241248
if (_objectKind == "deployedBytecode")
242249
components.push_back(".immutableReferences");
243250
return util::applyMap(components, [&](auto const& _s) { return "evm." + _objectKind + _s; });
@@ -253,7 +260,7 @@ bool isBinaryRequested(Json const& _outputSelection)
253260
static std::vector<std::string> const outputsThatRequireBinaries = std::vector<std::string>{
254261
"*",
255262
"ir", "irAst", "irOptimized", "irOptimizedAst", "yulCFGJson",
256-
"evm.gasEstimates", "evm.legacyAssembly", "evm.assembly"
263+
"evm.gasEstimates", "evm.legacyAssembly", "evm.assembly", "ethdebug"
257264
} + evmObjectComponents("bytecode") + evmObjectComponents("deployedBytecode");
258265

259266
for (auto const& fileRequests: _outputSelection)
@@ -283,6 +290,21 @@ bool isEvmBytecodeRequested(Json const& _outputSelection)
283290
return false;
284291
}
285292

293+
/// @returns true if ethdebug was requested.
294+
bool isEthdebugRequested(Json const& _outputSelection)
295+
{
296+
if (!_outputSelection.is_object())
297+
return false;
298+
299+
for (auto const& fileRequests: _outputSelection)
300+
for (auto const& requests: fileRequests)
301+
for (auto const& request: requests)
302+
if (request == "evm.bytecode.ethdebug" || request == "evm.deployedBytecode.ethdebug")
303+
return true;
304+
305+
return false;
306+
}
307+
286308
/// @returns The set of selected contracts, along with their compiler pipeline configuration, based
287309
/// on outputs requested in the JSON. Translates wildcards to the ones understood by CompilerStack.
288310
/// Note that as an exception, '*' does not yet match "ir", "irAst", "irOptimized" or "irOptimizedAst".
@@ -1152,6 +1174,35 @@ std::variant<StandardCompiler::InputsAndSettings, Json> StandardCompiler::parseI
11521174
ret.modelCheckerSettings.timeout = modelCheckerSettings["timeout"].get<Json::number_unsigned_t>();
11531175
}
11541176

1177+
if ((ret.debugInfoSelection.has_value() && ret.debugInfoSelection->ethdebug) || isEthdebugRequested(ret.outputSelection))
1178+
{
1179+
if (ret.language != "Solidity" && ret.language != "Yul")
1180+
return formatFatalError(Error::Type::FatalError, "'settings.debug.debugInfo' 'ethdebug' is only supported for languages 'Solidity' and 'Yul'.");
1181+
}
1182+
1183+
if (isEthdebugRequested(ret.outputSelection))
1184+
{
1185+
if (ret.language == "Solidity" && !ret.viaIR)
1186+
return formatFatalError(Error::Type::FatalError, "'evm.bytecode.ethdebug' or 'evm.deployedBytecode.ethdebug' can only be selected as output, if 'viaIR' was set.");
1187+
1188+
if (!ret.debugInfoSelection.has_value())
1189+
{
1190+
ret.debugInfoSelection = DebugInfoSelection::Default();
1191+
ret.debugInfoSelection->enable("ethdebug");
1192+
}
1193+
else
1194+
{
1195+
if (!ret.debugInfoSelection->ethdebug && ret.language == "Solidity")
1196+
return formatFatalError(Error::Type::FatalError, "'ethdebug' needs to be enabled in 'settings.debug.debugInfo', if 'evm.bytecode.ethdebug' or 'evm.deployedBytecode.ethdebug' was selected as output.");
1197+
}
1198+
}
1199+
1200+
if (
1201+
ret.debugInfoSelection.has_value() && ret.debugInfoSelection->ethdebug && ret.language == "Solidity" &&
1202+
!pipelineConfig(ret.outputSelection)[""][""].irCodegen && !isEthdebugRequested(ret.outputSelection)
1203+
)
1204+
return formatFatalError(Error::Type::FatalError, "'settings.debug.debugInfo' can only include 'ethdebug', if output 'ir', 'irOptimized', 'evm.bytecode.ethdebug' or 'evm.deployedBytecode.ethdebug' was selected.");
1205+
11551206
return {std::move(ret)};
11561207
}
11571208

@@ -1235,6 +1286,8 @@ Json StandardCompiler::importEVMAssembly(StandardCompiler::InputsAndSettings _in
12351286
creationJSON["functionDebugData"] = formatFunctionDebugData(stack.object(sourceName).functionDebugData);
12361287
if (evmCreationArtifactRequested("linkReferences"))
12371288
creationJSON["linkReferences"] = formatLinkReferences(stack.object(sourceName).linkReferences);
1289+
if (evmCreationArtifactRequested("ethdebug"))
1290+
creationJSON["ethdebug"] = stack.ethdebug(sourceName, false);
12381291
evmData["bytecode"] = creationJSON;
12391292
}
12401293

@@ -1263,6 +1316,8 @@ Json StandardCompiler::importEVMAssembly(StandardCompiler::InputsAndSettings _in
12631316
deployedJSON["linkReferences"] = formatLinkReferences(stack.runtimeObject(sourceName).linkReferences);
12641317
if (evmDeployedArtifactRequested("immutableReferences"))
12651318
deployedJSON["immutableReferences"] = formatImmutableReferences(stack.runtimeObject(sourceName).immutableReferences);
1319+
if (evmDeployedArtifactRequested("ethdebug"))
1320+
deployedJSON["ethdebug"] = stack.ethdebug(sourceName, true);
12661321
evmData["deployedBytecode"] = deployedJSON;
12671322
}
12681323

@@ -1503,6 +1558,8 @@ Json StandardCompiler::compileSolidity(StandardCompiler::InputsAndSettings _inpu
15031558
creationJSON["linkReferences"] = formatLinkReferences(compilerStack.object(contractName).linkReferences);
15041559
if (evmCreationArtifactRequested("generatedSources"))
15051560
creationJSON["generatedSources"] = compilerStack.generatedSources(contractName, /* _runtime */ false);
1561+
if (evmCreationArtifactRequested("ethdebug"))
1562+
creationJSON["ethdebug"] = compilerStack.ethdebug(contractName, false);
15061563
evmData["bytecode"] = creationJSON;
15071564
}
15081565

@@ -1533,6 +1590,8 @@ Json StandardCompiler::compileSolidity(StandardCompiler::InputsAndSettings _inpu
15331590
deployedJSON["immutableReferences"] = formatImmutableReferences(compilerStack.runtimeObject(contractName).immutableReferences);
15341591
if (evmDeployedArtifactRequested("generatedSources"))
15351592
deployedJSON["generatedSources"] = compilerStack.generatedSources(contractName, /* _runtime */ true);
1593+
if (evmDeployedArtifactRequested("ethdebug"))
1594+
deployedJSON["ethdebug"] = compilerStack.ethdebug(contractName, true);
15361595
evmData["deployedBytecode"] = deployedJSON;
15371596
}
15381597

@@ -1546,6 +1605,10 @@ Json StandardCompiler::compileSolidity(StandardCompiler::InputsAndSettings _inpu
15461605
contractsOutput[file][name] = contractData;
15471606
}
15481607
}
1608+
1609+
if (isEthdebugRequested(_inputsAndSettings.outputSelection))
1610+
output["ethdebug"] = compilerStack.ethdebug();
1611+
15491612
if (!contractsOutput.empty())
15501613
output["contracts"] = contractsOutput;
15511614

@@ -1597,6 +1660,19 @@ Json StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
15971660
return output;
15981661
}
15991662

1663+
for (auto const& fileRequests: _inputsAndSettings.outputSelection)
1664+
for (auto const& requests: fileRequests)
1665+
for (auto const& request: requests)
1666+
if (request == "evm.deployedBytecode.ethdebug")
1667+
{
1668+
output["errors"].emplace_back(formatError(
1669+
Error::Type::JSONError,
1670+
"general",
1671+
"\"evm.deployedBytecode.ethdebug\" cannot be used for Yul."
1672+
));
1673+
return output;
1674+
}
1675+
16001676
YulStack stack(
16011677
_inputsAndSettings.evmVersion,
16021678
_inputsAndSettings.eofVersion,
@@ -1687,6 +1763,8 @@ Json StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
16871763
bytecodeJSON["functionDebugData"] = formatFunctionDebugData(selectedObject.bytecode->functionDebugData);
16881764
if (evmArtifactRequested(kind, "linkReferences"))
16891765
bytecodeJSON["linkReferences"] = formatLinkReferences(selectedObject.bytecode->linkReferences);
1766+
if (evmArtifactRequested(kind, "ethdebug"))
1767+
bytecodeJSON["ethdebug"] = selectedObject.ethdebug;
16901768
if (isDeployed && evmArtifactRequested(kind, "immutableReferences"))
16911769
bytecodeJSON["immutableReferences"] = formatImmutableReferences(selectedObject.bytecode->immutableReferences);
16921770
output["contracts"][sourceName][contractName]["evm"][kind] = bytecodeJSON;
@@ -1700,6 +1778,9 @@ Json StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
17001778
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "yulCFGJson", wildcardMatchesExperimental))
17011779
output["contracts"][sourceName][contractName]["yulCFGJson"] = stack.cfgJson();
17021780

1781+
if (isEthdebugRequested(_inputsAndSettings.outputSelection))
1782+
output["ethdebug"] = stack.ethdebug();
1783+
17031784
return output;
17041785
}
17051786

0 commit comments

Comments
 (0)