/src/solidity/libyul/AsmJsonImporter.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | #include <libyul/AST.h> |
27 | | #include <libyul/Exceptions.h> |
28 | | |
29 | | #include <liblangutil/Exceptions.h> |
30 | | #include <liblangutil/Scanner.h> |
31 | | |
32 | | #include <boost/algorithm/string/split.hpp> |
33 | | #include <boost/algorithm/string.hpp> |
34 | | |
35 | | #include <vector> |
36 | | |
37 | | using namespace std; |
38 | | using namespace solidity::langutil; |
39 | | |
40 | | namespace solidity::yul |
41 | | { |
42 | | |
43 | | using SourceLocation = langutil::SourceLocation; |
44 | | |
45 | | SourceLocation const AsmJsonImporter::createSourceLocation(Json::Value const& _node) |
46 | 0 | { |
47 | 0 | yulAssert(member(_node, "src").isString(), "'src' must be a string"); |
48 | | |
49 | 0 | return solidity::langutil::parseSourceLocation(_node["src"].asString(), m_sourceNames); |
50 | 0 | } |
51 | | |
52 | | template <class T> |
53 | | T AsmJsonImporter::createAsmNode(Json::Value const& _node) |
54 | 0 | { |
55 | 0 | T r; |
56 | 0 | SourceLocation nativeLocation = createSourceLocation(_node); |
57 | 0 | yulAssert(nativeLocation.hasText(), "Invalid source location in Asm AST"); |
58 | | // TODO: We should add originLocation to the AST. |
59 | | // While it's not included, we'll use nativeLocation for it because we only support importing |
60 | | // inline assembly as a part of a Solidity AST and there these locations are always the same. |
61 | 0 | r.debugData = DebugData::create(nativeLocation, nativeLocation); |
62 | 0 | return r; |
63 | 0 | } Unexecuted instantiation: solidity::yul::TypedName solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::TypedName>(Json::Value const&) Unexecuted instantiation: solidity::yul::Block solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::Block>(Json::Value const&) Unexecuted instantiation: solidity::yul::Literal solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::Literal>(Json::Value const&) Unexecuted instantiation: solidity::yul::Leave solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::Leave>(Json::Value const&) Unexecuted instantiation: solidity::yul::Identifier solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::Identifier>(Json::Value const&) Unexecuted instantiation: solidity::yul::Assignment solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::Assignment>(Json::Value const&) Unexecuted instantiation: solidity::yul::FunctionCall solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::FunctionCall>(Json::Value const&) Unexecuted instantiation: solidity::yul::ExpressionStatement solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::ExpressionStatement>(Json::Value const&) Unexecuted instantiation: solidity::yul::VariableDeclaration solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::VariableDeclaration>(Json::Value const&) Unexecuted instantiation: solidity::yul::FunctionDefinition solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::FunctionDefinition>(Json::Value const&) Unexecuted instantiation: solidity::yul::If solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::If>(Json::Value const&) Unexecuted instantiation: solidity::yul::Case solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::Case>(Json::Value const&) Unexecuted instantiation: solidity::yul::Switch solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::Switch>(Json::Value const&) Unexecuted instantiation: solidity::yul::ForLoop solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::ForLoop>(Json::Value const&) Unexecuted instantiation: solidity::yul::Break solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::Break>(Json::Value const&) Unexecuted instantiation: solidity::yul::Continue solidity::yul::AsmJsonImporter::createAsmNode<solidity::yul::Continue>(Json::Value const&) |
64 | | |
65 | | Json::Value AsmJsonImporter::member(Json::Value const& _node, string const& _name) |
66 | 0 | { |
67 | 0 | if (!_node.isMember(_name)) |
68 | 0 | return Json::nullValue; |
69 | 0 | return _node[_name]; |
70 | 0 | } |
71 | | |
72 | | TypedName AsmJsonImporter::createTypedName(Json::Value const& _node) |
73 | 0 | { |
74 | 0 | auto typedName = createAsmNode<TypedName>(_node); |
75 | 0 | typedName.type = YulString{member(_node, "type").asString()}; |
76 | 0 | typedName.name = YulString{member(_node, "name").asString()}; |
77 | 0 | return typedName; |
78 | 0 | } |
79 | | |
80 | | Statement AsmJsonImporter::createStatement(Json::Value const& _node) |
81 | 0 | { |
82 | 0 | Json::Value jsonNodeType = member(_node, "nodeType"); |
83 | 0 | yulAssert(jsonNodeType.isString(), "Expected \"nodeType\" to be of type string!"); |
84 | 0 | string nodeType = jsonNodeType.asString(); |
85 | |
|
86 | 0 | yulAssert(nodeType.substr(0, 3) == "Yul", "Invalid nodeType prefix"); |
87 | 0 | nodeType = nodeType.substr(3); |
88 | |
|
89 | 0 | if (nodeType == "ExpressionStatement") |
90 | 0 | return createExpressionStatement(_node); |
91 | 0 | else if (nodeType == "Assignment") |
92 | 0 | return createAssignment(_node); |
93 | 0 | else if (nodeType == "VariableDeclaration") |
94 | 0 | return createVariableDeclaration(_node); |
95 | 0 | else if (nodeType == "FunctionDefinition") |
96 | 0 | return createFunctionDefinition(_node); |
97 | 0 | else if (nodeType == "If") |
98 | 0 | return createIf(_node); |
99 | 0 | else if (nodeType == "Switch") |
100 | 0 | return createSwitch(_node); |
101 | 0 | else if (nodeType == "ForLoop") |
102 | 0 | return createForLoop(_node); |
103 | 0 | else if (nodeType == "Break") |
104 | 0 | return createBreak(_node); |
105 | 0 | else if (nodeType == "Continue") |
106 | 0 | return createContinue(_node); |
107 | 0 | else if (nodeType == "Leave") |
108 | 0 | return createLeave(_node); |
109 | 0 | else if (nodeType == "Block") |
110 | 0 | return createBlock(_node); |
111 | 0 | else |
112 | 0 | yulAssert(false, "Invalid nodeType as statement"); |
113 | | |
114 | | // FIXME: Workaround for spurious GCC 12.1 warning (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105794) |
115 | 0 | util::unreachable(); |
116 | 0 | } |
117 | | |
118 | | Expression AsmJsonImporter::createExpression(Json::Value const& _node) |
119 | 0 | { |
120 | 0 | Json::Value jsonNodeType = member(_node, "nodeType"); |
121 | 0 | yulAssert(jsonNodeType.isString(), "Expected \"nodeType\" to be of type string!"); |
122 | 0 | string nodeType = jsonNodeType.asString(); |
123 | |
|
124 | 0 | yulAssert(nodeType.substr(0, 3) == "Yul", "Invalid nodeType prefix"); |
125 | 0 | nodeType = nodeType.substr(3); |
126 | |
|
127 | 0 | if (nodeType == "FunctionCall") |
128 | 0 | return createFunctionCall(_node); |
129 | 0 | else if (nodeType == "Identifier") |
130 | 0 | return createIdentifier(_node); |
131 | 0 | else if (nodeType == "Literal") |
132 | 0 | return createLiteral(_node); |
133 | 0 | else |
134 | 0 | yulAssert(false, "Invalid nodeType as expression"); |
135 | | |
136 | | // FIXME: Workaround for spurious GCC 12.1 warning (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105794) |
137 | 0 | util::unreachable(); |
138 | 0 | } |
139 | | |
140 | | vector<Expression> AsmJsonImporter::createExpressionVector(Json::Value const& _array) |
141 | 0 | { |
142 | 0 | vector<Expression> ret; |
143 | 0 | for (auto& var: _array) |
144 | 0 | ret.emplace_back(createExpression(var)); |
145 | 0 | return ret; |
146 | 0 | } |
147 | | |
148 | | vector<Statement> AsmJsonImporter::createStatementVector(Json::Value const& _array) |
149 | 0 | { |
150 | 0 | vector<Statement> ret; |
151 | 0 | for (auto& var: _array) |
152 | 0 | ret.emplace_back(createStatement(var)); |
153 | 0 | return ret; |
154 | 0 | } |
155 | | |
156 | | Block AsmJsonImporter::createBlock(Json::Value const& _node) |
157 | 0 | { |
158 | 0 | auto block = createAsmNode<Block>(_node); |
159 | 0 | block.statements = createStatementVector(_node["statements"]); |
160 | 0 | return block; |
161 | 0 | } |
162 | | |
163 | | Literal AsmJsonImporter::createLiteral(Json::Value const& _node) |
164 | 0 | { |
165 | 0 | auto lit = createAsmNode<Literal>(_node); |
166 | 0 | string kind = member(_node, "kind").asString(); |
167 | |
|
168 | 0 | solAssert(member(_node, "hexValue").isString() || member(_node, "value").isString(), ""); |
169 | 0 | if (_node.isMember("hexValue")) |
170 | 0 | lit.value = YulString{util::asString(util::fromHex(member(_node, "hexValue").asString()))}; |
171 | 0 | else |
172 | 0 | lit.value = YulString{member(_node, "value").asString()}; |
173 | |
|
174 | 0 | lit.type= YulString{member(_node, "type").asString()}; |
175 | |
|
176 | 0 | if (kind == "number") |
177 | 0 | { |
178 | 0 | langutil::CharStream charStream(lit.value.str(), ""); |
179 | 0 | langutil::Scanner scanner{charStream}; |
180 | 0 | lit.kind = LiteralKind::Number; |
181 | 0 | yulAssert( |
182 | 0 | scanner.currentToken() == Token::Number, |
183 | 0 | "Expected number but got " + langutil::TokenTraits::friendlyName(scanner.currentToken()) + string(" while scanning ") + lit.value.str() |
184 | 0 | ); |
185 | 0 | } |
186 | 0 | else if (kind == "bool") |
187 | 0 | { |
188 | 0 | langutil::CharStream charStream(lit.value.str(), ""); |
189 | 0 | langutil::Scanner scanner{charStream}; |
190 | 0 | lit.kind = LiteralKind::Boolean; |
191 | 0 | yulAssert( |
192 | 0 | scanner.currentToken() == Token::TrueLiteral || |
193 | 0 | scanner.currentToken() == Token::FalseLiteral, |
194 | 0 | "Expected true/false literal!" |
195 | 0 | ); |
196 | 0 | } |
197 | 0 | else if (kind == "string") |
198 | 0 | { |
199 | 0 | lit.kind = LiteralKind::String; |
200 | 0 | yulAssert( |
201 | 0 | lit.value.str().size() <= 32, |
202 | 0 | "String literal too long (" + to_string(lit.value.str().size()) + " > 32)" |
203 | 0 | ); |
204 | 0 | } |
205 | 0 | else |
206 | 0 | yulAssert(false, "unknown type of literal"); |
207 | | |
208 | 0 | return lit; |
209 | 0 | } |
210 | | |
211 | | Leave AsmJsonImporter::createLeave(Json::Value const& _node) |
212 | 0 | { |
213 | 0 | return createAsmNode<Leave>(_node); |
214 | 0 | } |
215 | | |
216 | | Identifier AsmJsonImporter::createIdentifier(Json::Value const& _node) |
217 | 0 | { |
218 | 0 | auto identifier = createAsmNode<Identifier>(_node); |
219 | 0 | identifier.name = YulString(member(_node, "name").asString()); |
220 | 0 | return identifier; |
221 | 0 | } |
222 | | |
223 | | Assignment AsmJsonImporter::createAssignment(Json::Value const& _node) |
224 | 0 | { |
225 | 0 | auto assignment = createAsmNode<Assignment>(_node); |
226 | |
|
227 | 0 | if (_node.isMember("variableNames")) |
228 | 0 | for (auto const& var: member(_node, "variableNames")) |
229 | 0 | assignment.variableNames.emplace_back(createIdentifier(var)); |
230 | |
|
231 | 0 | assignment.value = make_unique<Expression>(createExpression(member(_node, "value"))); |
232 | 0 | return assignment; |
233 | 0 | } |
234 | | |
235 | | FunctionCall AsmJsonImporter::createFunctionCall(Json::Value const& _node) |
236 | 0 | { |
237 | 0 | auto functionCall = createAsmNode<FunctionCall>(_node); |
238 | |
|
239 | 0 | for (auto const& var: member(_node, "arguments")) |
240 | 0 | functionCall.arguments.emplace_back(createExpression(var)); |
241 | |
|
242 | 0 | functionCall.functionName = createIdentifier(member(_node, "functionName")); |
243 | |
|
244 | 0 | return functionCall; |
245 | 0 | } |
246 | | |
247 | | ExpressionStatement AsmJsonImporter::createExpressionStatement(Json::Value const& _node) |
248 | 0 | { |
249 | 0 | auto statement = createAsmNode<ExpressionStatement>(_node); |
250 | 0 | statement.expression = createExpression(member(_node, "expression")); |
251 | 0 | return statement; |
252 | 0 | } |
253 | | |
254 | | VariableDeclaration AsmJsonImporter::createVariableDeclaration(Json::Value const& _node) |
255 | 0 | { |
256 | 0 | auto varDec = createAsmNode<VariableDeclaration>(_node); |
257 | 0 | for (auto const& var: member(_node, "variables")) |
258 | 0 | varDec.variables.emplace_back(createTypedName(var)); |
259 | 0 | varDec.value = make_unique<Expression>(createExpression(member(_node, "value"))); |
260 | 0 | return varDec; |
261 | 0 | } |
262 | | |
263 | | FunctionDefinition AsmJsonImporter::createFunctionDefinition(Json::Value const& _node) |
264 | 0 | { |
265 | 0 | auto funcDef = createAsmNode<FunctionDefinition>(_node); |
266 | 0 | funcDef.name = YulString{member(_node, "name").asString()}; |
267 | |
|
268 | 0 | if (_node.isMember("parameters")) |
269 | 0 | for (auto const& var: member(_node, "parameters")) |
270 | 0 | funcDef.parameters.emplace_back(createTypedName(var)); |
271 | |
|
272 | 0 | if (_node.isMember("returnVariables")) |
273 | 0 | for (auto const& var: member(_node, "returnVariables")) |
274 | 0 | funcDef.returnVariables.emplace_back(createTypedName(var)); |
275 | |
|
276 | 0 | funcDef.body = createBlock(member(_node, "body")); |
277 | 0 | return funcDef; |
278 | 0 | } |
279 | | |
280 | | If AsmJsonImporter::createIf(Json::Value const& _node) |
281 | 0 | { |
282 | 0 | auto ifStatement = createAsmNode<If>(_node); |
283 | 0 | ifStatement.condition = make_unique<Expression>(createExpression(member(_node, "condition"))); |
284 | 0 | ifStatement.body = createBlock(member(_node, "body")); |
285 | 0 | return ifStatement; |
286 | 0 | } |
287 | | |
288 | | Case AsmJsonImporter::createCase(Json::Value const& _node) |
289 | 0 | { |
290 | 0 | auto caseStatement = createAsmNode<Case>(_node); |
291 | 0 | auto const& value = member(_node, "value"); |
292 | 0 | if (value.isString()) |
293 | 0 | yulAssert(value.asString() == "default", "Expected default case"); |
294 | 0 | else |
295 | 0 | caseStatement.value = make_unique<Literal>(createLiteral(value)); |
296 | 0 | caseStatement.body = createBlock(member(_node, "body")); |
297 | 0 | return caseStatement; |
298 | 0 | } |
299 | | |
300 | | Switch AsmJsonImporter::createSwitch(Json::Value const& _node) |
301 | 0 | { |
302 | 0 | auto switchStatement = createAsmNode<Switch>(_node); |
303 | 0 | switchStatement.expression = make_unique<Expression>(createExpression(member(_node, "expression"))); |
304 | 0 | for (auto const& var: member(_node, "cases")) |
305 | 0 | switchStatement.cases.emplace_back(createCase(var)); |
306 | 0 | return switchStatement; |
307 | 0 | } |
308 | | |
309 | | ForLoop AsmJsonImporter::createForLoop(Json::Value const& _node) |
310 | 0 | { |
311 | 0 | auto forLoop = createAsmNode<ForLoop>(_node); |
312 | 0 | forLoop.pre = createBlock(member(_node, "pre")); |
313 | 0 | forLoop.condition = make_unique<Expression>(createExpression(member(_node, "condition"))); |
314 | 0 | forLoop.post = createBlock(member(_node, "post")); |
315 | 0 | forLoop.body = createBlock(member(_node, "body")); |
316 | 0 | return forLoop; |
317 | 0 | } |
318 | | |
319 | | Break AsmJsonImporter::createBreak(Json::Value const& _node) |
320 | 0 | { |
321 | 0 | return createAsmNode<Break>(_node); |
322 | 0 | } |
323 | | |
324 | | Continue AsmJsonImporter::createContinue(Json::Value const& _node) |
325 | 0 | { |
326 | 0 | return createAsmNode<Continue>(_node); |
327 | 0 | } |
328 | | |
329 | | } |