Coverage Report

Created: 2026-10-26 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/solidity/libyul/AsmJsonImporter.cpp
Line
Count
Source
1
/*
2
  This file is part of solidity.
3
4
  solidity is free software: you can redistribute it and/or modify
5
  it under the terms of the GNU General Public License as published by
6
  the Free Software Foundation, either version 3 of the License, or
7
  (at your option) any later version.
8
9
  solidity is distributed in the hope that it will be useful,
10
  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
  GNU General Public License for more details.
13
14
  You should have received a copy of the GNU General Public License
15
  along with solidity.  If not, see <http://www.gnu.org/licenses/>.
16
*/
17
// SPDX-License-Identifier: GPL-3.0
18
/**
19
 * @author julius <djudju@protonmail.com>
20
 * @date 2019
21
 * Converts an inlineAssembly AST from JSON format to AsmData
22
23
 */
24
25
#include <libyul/AsmJsonImporter.h>
26
27
#include <libyul/AST.h>
28
#include <libyul/Dialect.h>
29
#include <libyul/Exceptions.h>
30
#include <libyul/Utilities.h>
31
32
#include <liblangutil/Exceptions.h>
33
#include <liblangutil/Scanner.h>
34
35
#include <boost/algorithm/string/split.hpp>
36
#include <boost/algorithm/string.hpp>
37
38
#include <vector>
39
40
using namespace solidity::langutil;
41
42
namespace solidity::yul
43
{
44
45
using SourceLocation = langutil::SourceLocation;
46
47
SourceLocation const AsmJsonImporter::createSourceLocation(Json const& _node)
48
0
{
49
0
  yulAssert(member(_node, "src").is_string(), "'src' must be a string");
50
51
0
  return solidity::langutil::parseSourceLocation(_node["src"].get<std::string>(), m_sourceNames);
52
0
}
53
54
AST AsmJsonImporter::createAST(solidity::Json const& _node)
55
0
{
56
0
  return {m_dialect, createBlock(_node)};
57
0
}
58
59
template <class T>
60
T AsmJsonImporter::createAsmNode(Json const& _node)
61
0
{
62
0
  T r;
63
0
  SourceLocation nativeLocation = createSourceLocation(_node);
64
0
  yulAssert(nativeLocation.hasText(), "Invalid source location in Asm AST");
65
  // TODO: We should add originLocation to the AST.
66
  // While it's not included, we'll use nativeLocation for it because we only support importing
67
  // inline assembly as a part of a Solidity AST and there these locations are always the same.
68
0
  r.debugData = DebugData::create(nativeLocation, nativeLocation);
69
0
  return r;
70
0
}
Unexecuted instantiation: solidity::yul::NameWithDebugData solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::NameWithDebugData>(nlohmann::json_abi_v3_12_0::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::json_abi_v3_12_0::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >, void> const&)
Unexecuted instantiation: solidity::yul::Block solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::Block>(nlohmann::json_abi_v3_12_0::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::json_abi_v3_12_0::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >, void> const&)
Unexecuted instantiation: solidity::yul::Literal solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::Literal>(nlohmann::json_abi_v3_12_0::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::json_abi_v3_12_0::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >, void> const&)
Unexecuted instantiation: solidity::yul::Leave solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::Leave>(nlohmann::json_abi_v3_12_0::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::json_abi_v3_12_0::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >, void> const&)
Unexecuted instantiation: solidity::yul::Identifier solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::Identifier>(nlohmann::json_abi_v3_12_0::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::json_abi_v3_12_0::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >, void> const&)
Unexecuted instantiation: solidity::yul::Assignment solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::Assignment>(nlohmann::json_abi_v3_12_0::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::json_abi_v3_12_0::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >, void> const&)
Unexecuted instantiation: solidity::yul::FunctionCall solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::FunctionCall>(nlohmann::json_abi_v3_12_0::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::json_abi_v3_12_0::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >, void> const&)
Unexecuted instantiation: solidity::yul::BuiltinName solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::BuiltinName>(nlohmann::json_abi_v3_12_0::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::json_abi_v3_12_0::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >, void> const&)
Unexecuted instantiation: solidity::yul::ExpressionStatement solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::ExpressionStatement>(nlohmann::json_abi_v3_12_0::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::json_abi_v3_12_0::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >, void> const&)
Unexecuted instantiation: solidity::yul::VariableDeclaration solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::VariableDeclaration>(nlohmann::json_abi_v3_12_0::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::json_abi_v3_12_0::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >, void> const&)
Unexecuted instantiation: solidity::yul::FunctionDefinition solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::FunctionDefinition>(nlohmann::json_abi_v3_12_0::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::json_abi_v3_12_0::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >, void> const&)
Unexecuted instantiation: solidity::yul::If solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::If>(nlohmann::json_abi_v3_12_0::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::json_abi_v3_12_0::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >, void> const&)
Unexecuted instantiation: solidity::yul::Case solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::Case>(nlohmann::json_abi_v3_12_0::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::json_abi_v3_12_0::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >, void> const&)
Unexecuted instantiation: solidity::yul::Switch solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::Switch>(nlohmann::json_abi_v3_12_0::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::json_abi_v3_12_0::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >, void> const&)
Unexecuted instantiation: solidity::yul::ForLoop solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::ForLoop>(nlohmann::json_abi_v3_12_0::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::json_abi_v3_12_0::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >, void> const&)
Unexecuted instantiation: solidity::yul::Break solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::Break>(nlohmann::json_abi_v3_12_0::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::json_abi_v3_12_0::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >, void> const&)
Unexecuted instantiation: solidity::yul::Continue solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::Continue>(nlohmann::json_abi_v3_12_0::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::json_abi_v3_12_0::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >, void> const&)
71
72
Json AsmJsonImporter::member(Json const& _node, std::string const& _name)
73
0
{
74
0
  if (!_node.contains(_name))
75
0
    return Json();
76
0
  return _node[_name];
77
0
}
78
79
NameWithDebugData AsmJsonImporter::createNameWithDebugData(Json const& _node)
80
0
{
81
0
  auto nameWithDebugData = createAsmNode<NameWithDebugData>(_node);
82
0
  nameWithDebugData.name = YulName{member(_node, "name").get<std::string>()};
83
0
  return nameWithDebugData;
84
0
}
85
86
Statement AsmJsonImporter::createStatement(Json const& _node)
87
0
{
88
0
  Json jsonNodeType = member(_node, "nodeType");
89
0
  yulAssert(jsonNodeType.is_string(), "Expected \"nodeType\" to be of type string!");
90
0
  std::string nodeType = jsonNodeType.get<std::string>();
91
92
0
  yulAssert(nodeType.substr(0, 3) == "Yul", "Invalid nodeType prefix");
93
0
  nodeType = nodeType.substr(3);
94
95
0
  if (nodeType == "ExpressionStatement")
96
0
    return createExpressionStatement(_node);
97
0
  else if (nodeType == "Assignment")
98
0
    return createAssignment(_node);
99
0
  else if (nodeType == "VariableDeclaration")
100
0
    return createVariableDeclaration(_node);
101
0
  else if (nodeType == "FunctionDefinition")
102
0
    return createFunctionDefinition(_node);
103
0
  else if (nodeType == "If")
104
0
    return createIf(_node);
105
0
  else if (nodeType == "Switch")
106
0
    return createSwitch(_node);
107
0
  else if (nodeType == "ForLoop")
108
0
    return createForLoop(_node);
109
0
  else if (nodeType == "Break")
110
0
    return createBreak(_node);
111
0
  else if (nodeType == "Continue")
112
0
    return createContinue(_node);
113
0
  else if (nodeType == "Leave")
114
0
    return createLeave(_node);
115
0
  else if (nodeType == "Block")
116
0
    return createBlock(_node);
117
0
  else
118
0
    yulAssert(false, "Invalid nodeType as statement");
119
120
  // FIXME: Workaround for spurious GCC 12.1 warning (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105794)
121
0
  util::unreachable();
122
0
}
123
124
Expression AsmJsonImporter::createExpression(Json const& _node)
125
0
{
126
0
  Json jsonNodeType = member(_node, "nodeType");
127
0
  yulAssert(jsonNodeType.is_string(), "Expected \"nodeType\" to be of type string!");
128
0
  std::string nodeType = jsonNodeType.get<std::string>();
129
130
0
  yulAssert(nodeType.substr(0, 3) == "Yul", "Invalid nodeType prefix");
131
0
  nodeType = nodeType.substr(3);
132
133
0
  if (nodeType == "FunctionCall")
134
0
    return createFunctionCall(_node);
135
0
  else if (nodeType == "Identifier")
136
0
    return createIdentifier(_node);
137
0
  else if (nodeType == "Literal")
138
0
    return createLiteral(_node);
139
0
  else
140
0
    yulAssert(false, "Invalid nodeType as expression");
141
142
  // FIXME: Workaround for spurious GCC 12.1 warning (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105794)
143
0
  util::unreachable();
144
0
}
145
146
std::vector<Expression> AsmJsonImporter::createExpressionVector(Json const& _array)
147
0
{
148
0
  std::vector<Expression> ret;
149
0
  for (auto& var: _array)
150
0
    ret.emplace_back(createExpression(var));
151
0
  return ret;
152
0
}
153
154
std::vector<Statement> AsmJsonImporter::createStatementVector(Json const& _array)
155
0
{
156
0
  std::vector<Statement> ret;
157
0
  for (auto& var: _array)
158
0
    ret.emplace_back(createStatement(var));
159
0
  return ret;
160
0
}
161
162
Block AsmJsonImporter::createBlock(Json const& _node)
163
0
{
164
0
  auto block = createAsmNode<Block>(_node);
165
0
  block.statements = createStatementVector(_node["statements"]);
166
0
  return block;
167
0
}
168
169
Literal AsmJsonImporter::createLiteral(Json const& _node)
170
0
{
171
0
  auto lit = createAsmNode<Literal>(_node);
172
0
  std::string kind = member(_node, "kind").get<std::string>();
173
174
0
  solAssert(member(_node, "hexValue").is_string() || member(_node, "value").is_string(), "");
175
0
  std::string value;
176
0
  if (_node.contains("hexValue"))
177
0
    value = util::asString(util::fromHex(member(_node, "hexValue").get<std::string>()));
178
0
  else
179
0
    value = member(_node, "value").get<std::string>();
180
0
  {
181
0
    auto const typeNode = member(_node, "type");
182
0
    yulAssert(
183
0
      typeNode.empty() || typeNode.get<std::string>().empty(),
184
0
      fmt::format(
185
0
        "Expected literal types to be either empty or absent in the JSON. Got \"{}\".",
186
0
        typeNode.get<std::string>()
187
0
      )
188
0
    );
189
0
  }
190
0
  if (kind == "number")
191
0
  {
192
0
    langutil::CharStream charStream(value, "");
193
0
    langutil::Scanner scanner{charStream};
194
0
    lit.kind = LiteralKind::Number;
195
0
    yulAssert(
196
0
      scanner.currentToken() == Token::Number,
197
0
      "Expected number but got " + langutil::TokenTraits::friendlyName(scanner.currentToken()) + std::string(" while scanning ") + value
198
0
    );
199
0
  }
200
0
  else if (kind == "bool")
201
0
  {
202
0
    langutil::CharStream charStream(value, "");
203
0
    langutil::Scanner scanner{charStream};
204
0
    lit.kind = LiteralKind::Boolean;
205
0
    yulAssert(
206
0
      scanner.currentToken() == Token::TrueLiteral ||
207
0
      scanner.currentToken() == Token::FalseLiteral,
208
0
      "Expected true/false literal!"
209
0
    );
210
0
  }
211
0
  else if (kind == "string")
212
0
  {
213
0
    lit.kind = LiteralKind::String;
214
0
    yulAssert(
215
0
      value.size() <= 32,
216
0
      "String literal too long (" + std::to_string(value.size()) + " > 32)"
217
0
    );
218
0
  }
219
0
  else
220
0
    yulAssert(false, "unknown type of literal");
221
222
  // import only for inline assembly, no unlimited string literals there
223
0
  lit.value = valueOfLiteral(value, lit.kind, false /* _unlimitedLiteralArgument */);
224
225
0
  yulAssert(validLiteral(lit));
226
0
  return lit;
227
0
}
228
229
Leave AsmJsonImporter::createLeave(Json const& _node)
230
0
{
231
0
  return createAsmNode<Leave>(_node);
232
0
}
233
234
Identifier AsmJsonImporter::createIdentifier(Json const& _node)
235
0
{
236
0
  auto identifier = createAsmNode<Identifier>(_node);
237
0
  identifier.name = YulName(member(_node, "name").get<std::string>());
238
0
  return identifier;
239
0
}
240
241
Assignment AsmJsonImporter::createAssignment(Json const& _node)
242
0
{
243
0
  auto assignment = createAsmNode<Assignment>(_node);
244
245
0
  if (_node.contains("variableNames"))
246
0
    for (auto const& var: member(_node, "variableNames"))
247
0
      assignment.variableNames.emplace_back(createIdentifier(var));
248
249
0
  assignment.value = std::make_unique<Expression>(createExpression(member(_node, "value")));
250
0
  return assignment;
251
0
}
252
253
FunctionCall AsmJsonImporter::createFunctionCall(Json const& _node)
254
0
{
255
0
  auto functionCall = createAsmNode<FunctionCall>(_node);
256
257
0
  for (auto const& var: member(_node, "arguments"))
258
0
    functionCall.arguments.emplace_back(createExpression(var));
259
260
0
  auto const functionNameNode = member(_node, "functionName");
261
0
  auto const name = member(functionNameNode, "name").get<std::string>();
262
0
  if (std::optional<BuiltinHandle> builtinHandle = m_dialect.findBuiltin(name))
263
0
  {
264
0
    auto builtin = createAsmNode<BuiltinName>(functionNameNode);
265
0
    builtin.handle = *builtinHandle;
266
0
    functionCall.functionName = builtin;
267
0
  }
268
0
  else
269
0
    functionCall.functionName = createIdentifier(functionNameNode);
270
271
0
  return functionCall;
272
0
}
273
274
ExpressionStatement AsmJsonImporter::createExpressionStatement(Json const& _node)
275
0
{
276
0
  auto statement = createAsmNode<ExpressionStatement>(_node);
277
0
  statement.expression = createExpression(member(_node, "expression"));
278
0
  return statement;
279
0
}
280
281
VariableDeclaration AsmJsonImporter::createVariableDeclaration(Json const& _node)
282
0
{
283
0
  auto varDec = createAsmNode<VariableDeclaration>(_node);
284
0
  for (auto const& var: member(_node, "variables"))
285
0
    varDec.variables.emplace_back(createNameWithDebugData(var));
286
287
0
  if (_node.contains("value"))
288
0
    varDec.value = std::make_unique<Expression>(createExpression(member(_node, "value")));
289
290
0
  return varDec;
291
0
}
292
293
FunctionDefinition AsmJsonImporter::createFunctionDefinition(Json const& _node)
294
0
{
295
0
  auto funcDef = createAsmNode<FunctionDefinition>(_node);
296
0
  funcDef.name = YulName{member(_node, "name").get<std::string>()};
297
298
0
  if (_node.contains("parameters"))
299
0
    for (auto const& var: member(_node, "parameters"))
300
0
      funcDef.parameters.emplace_back(createNameWithDebugData(var));
301
302
0
  if (_node.contains("returnVariables"))
303
0
    for (auto const& var: member(_node, "returnVariables"))
304
0
      funcDef.returnVariables.emplace_back(createNameWithDebugData(var));
305
306
0
  funcDef.body = createBlock(member(_node, "body"));
307
0
  return funcDef;
308
0
}
309
310
If AsmJsonImporter::createIf(Json const& _node)
311
0
{
312
0
  auto ifStatement = createAsmNode<If>(_node);
313
0
  ifStatement.condition = std::make_unique<Expression>(createExpression(member(_node, "condition")));
314
0
  ifStatement.body = createBlock(member(_node, "body"));
315
0
  return ifStatement;
316
0
}
317
318
Case AsmJsonImporter::createCase(Json const& _node)
319
0
{
320
0
  auto caseStatement = createAsmNode<Case>(_node);
321
0
  auto const& value = member(_node, "value");
322
0
  if (value.is_string())
323
0
    yulAssert(value.get<std::string>() == "default", "Expected default case");
324
0
  else
325
0
    caseStatement.value = std::make_unique<Literal>(createLiteral(value));
326
0
  caseStatement.body = createBlock(member(_node, "body"));
327
0
  return caseStatement;
328
0
}
329
330
Switch AsmJsonImporter::createSwitch(Json const& _node)
331
0
{
332
0
  auto switchStatement = createAsmNode<Switch>(_node);
333
0
  switchStatement.expression = std::make_unique<Expression>(createExpression(member(_node, "expression")));
334
0
  for (auto const& var: member(_node, "cases"))
335
0
    switchStatement.cases.emplace_back(createCase(var));
336
0
  return switchStatement;
337
0
}
338
339
ForLoop AsmJsonImporter::createForLoop(Json const& _node)
340
0
{
341
0
  auto forLoop = createAsmNode<ForLoop>(_node);
342
0
  forLoop.pre = createBlock(member(_node, "pre"));
343
0
  forLoop.condition = std::make_unique<Expression>(createExpression(member(_node, "condition")));
344
0
  forLoop.post = createBlock(member(_node, "post"));
345
0
  forLoop.body = createBlock(member(_node, "body"));
346
0
  return forLoop;
347
0
}
348
349
Break AsmJsonImporter::createBreak(Json const& _node)
350
0
{
351
0
  return createAsmNode<Break>(_node);
352
0
}
353
354
Continue AsmJsonImporter::createContinue(Json const& _node)
355
0
{
356
0
  return createAsmNode<Continue>(_node);
357
0
}
358
359
}