solidity/libyul/AsmJsonImporter.cpp

329 lines
10 KiB
C++
Raw Permalink Normal View History

2019-09-11 19:16:35 +00:00
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
2019-09-11 19:16:35 +00:00
/**
* @author julius <djudju@protonmail.com>
* @date 2019
* Converts an inlineAssembly AST from JSON format to AsmData
*/
#include <libyul/AsmJsonImporter.h>
#include <libyul/AST.h>
2020-10-29 14:21:12 +00:00
#include <libyul/Exceptions.h>
#include <liblangutil/Exceptions.h>
2019-09-11 19:16:35 +00:00
#include <liblangutil/Scanner.h>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string.hpp>
#include <vector>
2019-09-11 19:16:35 +00:00
using namespace solidity::langutil;
2019-09-11 19:16:35 +00:00
namespace solidity::yul
2019-09-11 19:16:35 +00:00
{
using SourceLocation = langutil::SourceLocation;
SourceLocation const AsmJsonImporter::createSourceLocation(Json::Value const& _node)
{
2020-10-29 14:21:12 +00:00
yulAssert(member(_node, "src").isString(), "'src' must be a string");
2019-09-11 19:16:35 +00:00
return solidity::langutil::parseSourceLocation(_node["src"].asString(), m_sourceNames);
2019-09-11 19:16:35 +00:00
}
template <class T>
T AsmJsonImporter::createAsmNode(Json::Value const& _node)
{
T r;
SourceLocation nativeLocation = createSourceLocation(_node);
yulAssert(nativeLocation.hasText(), "Invalid source location in Asm AST");
// TODO: We should add originLocation to the AST.
// While it's not included, we'll use nativeLocation for it because we only support importing
// inline assembly as a part of a Solidity AST and there these locations are always the same.
r.debugData = DebugData::create(nativeLocation, nativeLocation);
2019-09-11 19:16:35 +00:00
return r;
}
Json::Value AsmJsonImporter::member(Json::Value const& _node, std::string const& _name)
2019-09-11 19:16:35 +00:00
{
2020-04-27 08:28:54 +00:00
if (!_node.isMember(_name))
return Json::nullValue;
2019-09-11 19:16:35 +00:00
return _node[_name];
}
TypedName AsmJsonImporter::createTypedName(Json::Value const& _node)
2019-09-11 19:16:35 +00:00
{
auto typedName = createAsmNode<TypedName>(_node);
2019-09-11 19:16:35 +00:00
typedName.type = YulString{member(_node, "type").asString()};
typedName.name = YulString{member(_node, "name").asString()};
return typedName;
}
Statement AsmJsonImporter::createStatement(Json::Value const& _node)
2019-09-11 19:16:35 +00:00
{
Json::Value jsonNodeType = member(_node, "nodeType");
2020-10-29 14:21:12 +00:00
yulAssert(jsonNodeType.isString(), "Expected \"nodeType\" to be of type string!");
std::string nodeType = jsonNodeType.asString();
2019-09-11 19:16:35 +00:00
2020-10-29 14:21:12 +00:00
yulAssert(nodeType.substr(0, 3) == "Yul", "Invalid nodeType prefix");
2019-09-11 19:16:35 +00:00
nodeType = nodeType.substr(3);
if (nodeType == "ExpressionStatement")
return createExpressionStatement(_node);
else if (nodeType == "Assignment")
return createAssignment(_node);
else if (nodeType == "VariableDeclaration")
return createVariableDeclaration(_node);
else if (nodeType == "FunctionDefinition")
return createFunctionDefinition(_node);
else if (nodeType == "If")
return createIf(_node);
else if (nodeType == "Switch")
return createSwitch(_node);
else if (nodeType == "ForLoop")
return createForLoop(_node);
else if (nodeType == "Break")
return createBreak(_node);
else if (nodeType == "Continue")
return createContinue(_node);
else if (nodeType == "Leave")
return createLeave(_node);
else if (nodeType == "Block")
return createBlock(_node);
2019-09-11 19:16:35 +00:00
else
2020-10-29 14:21:12 +00:00
yulAssert(false, "Invalid nodeType as statement");
// FIXME: Workaround for spurious GCC 12.1 warning (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105794)
util::unreachable();
2019-09-11 19:16:35 +00:00
}
Expression AsmJsonImporter::createExpression(Json::Value const& _node)
2019-09-11 19:16:35 +00:00
{
Json::Value jsonNodeType = member(_node, "nodeType");
2020-10-29 14:21:12 +00:00
yulAssert(jsonNodeType.isString(), "Expected \"nodeType\" to be of type string!");
std::string nodeType = jsonNodeType.asString();
2019-09-11 19:16:35 +00:00
2020-10-29 14:21:12 +00:00
yulAssert(nodeType.substr(0, 3) == "Yul", "Invalid nodeType prefix");
2019-09-11 19:16:35 +00:00
nodeType = nodeType.substr(3);
if (nodeType == "FunctionCall")
return createFunctionCall(_node);
else if (nodeType == "Identifier")
return createIdentifier(_node);
else if (nodeType == "Literal")
return createLiteral(_node);
else
2020-10-29 14:21:12 +00:00
yulAssert(false, "Invalid nodeType as expression");
// FIXME: Workaround for spurious GCC 12.1 warning (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105794)
util::unreachable();
2019-09-11 19:16:35 +00:00
}
std::vector<Expression> AsmJsonImporter::createExpressionVector(Json::Value const& _array)
2019-09-11 19:16:35 +00:00
{
std::vector<Expression> ret;
2019-09-11 19:16:35 +00:00
for (auto& var: _array)
ret.emplace_back(createExpression(var));
return ret;
}
std::vector<Statement> AsmJsonImporter::createStatementVector(Json::Value const& _array)
2019-09-11 19:16:35 +00:00
{
std::vector<Statement> ret;
2019-09-11 19:16:35 +00:00
for (auto& var: _array)
ret.emplace_back(createStatement(var));
return ret;
}
Block AsmJsonImporter::createBlock(Json::Value const& _node)
2019-09-11 19:16:35 +00:00
{
auto block = createAsmNode<Block>(_node);
2019-09-11 19:16:35 +00:00
block.statements = createStatementVector(_node["statements"]);
return block;
}
Literal AsmJsonImporter::createLiteral(Json::Value const& _node)
2019-09-11 19:16:35 +00:00
{
auto lit = createAsmNode<Literal>(_node);
std::string kind = member(_node, "kind").asString();
2019-09-11 19:16:35 +00:00
solAssert(member(_node, "hexValue").isString() || member(_node, "value").isString(), "");
if (_node.isMember("hexValue"))
lit.value = YulString{util::asString(util::fromHex(member(_node, "hexValue").asString()))};
else
lit.value = YulString{member(_node, "value").asString()};
2019-09-11 19:16:35 +00:00
lit.type= YulString{member(_node, "type").asString()};
if (kind == "number")
{
2021-07-14 10:53:39 +00:00
langutil::CharStream charStream(lit.value.str(), "");
langutil::Scanner scanner{charStream};
lit.kind = LiteralKind::Number;
2020-10-29 14:21:12 +00:00
yulAssert(
2019-09-11 19:16:35 +00:00
scanner.currentToken() == Token::Number,
"Expected number but got " + langutil::TokenTraits::friendlyName(scanner.currentToken()) + std::string(" while scanning ") + lit.value.str()
2019-09-11 19:16:35 +00:00
);
}
else if (kind == "bool")
{
2021-07-14 10:53:39 +00:00
langutil::CharStream charStream(lit.value.str(), "");
langutil::Scanner scanner{charStream};
lit.kind = LiteralKind::Boolean;
2020-10-29 14:21:12 +00:00
yulAssert(
2019-09-11 19:16:35 +00:00
scanner.currentToken() == Token::TrueLiteral ||
scanner.currentToken() == Token::FalseLiteral,
"Expected true/false literal!"
);
}
else if (kind == "string")
{
lit.kind = LiteralKind::String;
2020-10-29 14:21:12 +00:00
yulAssert(
lit.value.str().size() <= 32,
"String literal too long (" + std::to_string(lit.value.str().size()) + " > 32)"
);
2019-09-11 19:16:35 +00:00
}
else
2020-10-29 14:21:12 +00:00
yulAssert(false, "unknown type of literal");
2019-09-11 19:16:35 +00:00
return lit;
}
Leave AsmJsonImporter::createLeave(Json::Value const& _node)
2019-09-11 19:16:35 +00:00
{
return createAsmNode<Leave>(_node);
2019-09-11 19:16:35 +00:00
}
Identifier AsmJsonImporter::createIdentifier(Json::Value const& _node)
2019-09-11 19:16:35 +00:00
{
auto identifier = createAsmNode<Identifier>(_node);
2019-09-11 19:16:35 +00:00
identifier.name = YulString(member(_node, "name").asString());
return identifier;
}
Assignment AsmJsonImporter::createAssignment(Json::Value const& _node)
2019-09-11 19:16:35 +00:00
{
auto assignment = createAsmNode<Assignment>(_node);
2019-09-11 19:16:35 +00:00
if (_node.isMember("variableNames"))
for (auto const& var: member(_node, "variableNames"))
assignment.variableNames.emplace_back(createIdentifier(var));
assignment.value = std::make_unique<Expression>(createExpression(member(_node, "value")));
2019-09-11 19:16:35 +00:00
return assignment;
}
FunctionCall AsmJsonImporter::createFunctionCall(Json::Value const& _node)
2019-09-11 19:16:35 +00:00
{
auto functionCall = createAsmNode<FunctionCall>(_node);
2019-09-11 19:16:35 +00:00
for (auto const& var: member(_node, "arguments"))
functionCall.arguments.emplace_back(createExpression(var));
functionCall.functionName = createIdentifier(member(_node, "functionName"));
return functionCall;
}
ExpressionStatement AsmJsonImporter::createExpressionStatement(Json::Value const& _node)
2019-09-11 19:16:35 +00:00
{
auto statement = createAsmNode<ExpressionStatement>(_node);
2019-09-11 19:16:35 +00:00
statement.expression = createExpression(member(_node, "expression"));
return statement;
}
VariableDeclaration AsmJsonImporter::createVariableDeclaration(Json::Value const& _node)
2019-09-11 19:16:35 +00:00
{
auto varDec = createAsmNode<VariableDeclaration>(_node);
2019-09-11 19:16:35 +00:00
for (auto const& var: member(_node, "variables"))
varDec.variables.emplace_back(createTypedName(var));
varDec.value = std::make_unique<Expression>(createExpression(member(_node, "value")));
2019-09-11 19:16:35 +00:00
return varDec;
}
FunctionDefinition AsmJsonImporter::createFunctionDefinition(Json::Value const& _node)
2019-09-11 19:16:35 +00:00
{
auto funcDef = createAsmNode<FunctionDefinition>(_node);
2019-09-11 19:16:35 +00:00
funcDef.name = YulString{member(_node, "name").asString()};
if (_node.isMember("parameters"))
for (auto const& var: member(_node, "parameters"))
funcDef.parameters.emplace_back(createTypedName(var));
if (_node.isMember("returnVariables"))
for (auto const& var: member(_node, "returnVariables"))
funcDef.returnVariables.emplace_back(createTypedName(var));
funcDef.body = createBlock(member(_node, "body"));
return funcDef;
}
If AsmJsonImporter::createIf(Json::Value const& _node)
2019-09-11 19:16:35 +00:00
{
auto ifStatement = createAsmNode<If>(_node);
ifStatement.condition = std::make_unique<Expression>(createExpression(member(_node, "condition")));
2019-09-11 19:16:35 +00:00
ifStatement.body = createBlock(member(_node, "body"));
return ifStatement;
}
Case AsmJsonImporter::createCase(Json::Value const& _node)
2019-09-11 19:16:35 +00:00
{
auto caseStatement = createAsmNode<Case>(_node);
auto const& value = member(_node, "value");
if (value.isString())
2020-10-29 14:21:12 +00:00
yulAssert(value.asString() == "default", "Expected default case");
else
caseStatement.value = std::make_unique<Literal>(createLiteral(value));
2019-09-11 19:16:35 +00:00
caseStatement.body = createBlock(member(_node, "body"));
return caseStatement;
}
Switch AsmJsonImporter::createSwitch(Json::Value const& _node)
2019-09-11 19:16:35 +00:00
{
auto switchStatement = createAsmNode<Switch>(_node);
switchStatement.expression = std::make_unique<Expression>(createExpression(member(_node, "expression")));
2019-09-11 19:16:35 +00:00
for (auto const& var: member(_node, "cases"))
switchStatement.cases.emplace_back(createCase(var));
return switchStatement;
}
ForLoop AsmJsonImporter::createForLoop(Json::Value const& _node)
2019-09-11 19:16:35 +00:00
{
auto forLoop = createAsmNode<ForLoop>(_node);
2019-09-11 19:16:35 +00:00
forLoop.pre = createBlock(member(_node, "pre"));
forLoop.condition = std::make_unique<Expression>(createExpression(member(_node, "condition")));
2019-09-11 19:16:35 +00:00
forLoop.post = createBlock(member(_node, "post"));
forLoop.body = createBlock(member(_node, "body"));
return forLoop;
}
Break AsmJsonImporter::createBreak(Json::Value const& _node)
2019-09-11 19:16:35 +00:00
{
return createAsmNode<Break>(_node);
2019-09-11 19:16:35 +00:00
}
Continue AsmJsonImporter::createContinue(Json::Value const& _node)
2019-09-11 19:16:35 +00:00
{
return createAsmNode<Continue>(_node);
2019-09-11 19:16:35 +00:00
}
}