/* 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 . */ // SPDX-License-Identifier: GPL-3.0 /** * @author julius * @date 2019 * Converts an inlineAssembly AST from JSON format to AsmData */ #include #include #include #include #include #include #include #include using namespace solidity::langutil; namespace solidity::yul { using SourceLocation = langutil::SourceLocation; SourceLocation const AsmJsonImporter::createSourceLocation(Json::Value const& _node) { yulAssert(member(_node, "src").isString(), "'src' must be a string"); return solidity::langutil::parseSourceLocation(_node["src"].asString(), m_sourceNames); } template 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); return r; } Json::Value AsmJsonImporter::member(Json::Value const& _node, std::string const& _name) { if (!_node.isMember(_name)) return Json::nullValue; return _node[_name]; } TypedName AsmJsonImporter::createTypedName(Json::Value const& _node) { auto typedName = createAsmNode(_node); typedName.type = YulString{member(_node, "type").asString()}; typedName.name = YulString{member(_node, "name").asString()}; return typedName; } Statement AsmJsonImporter::createStatement(Json::Value const& _node) { Json::Value jsonNodeType = member(_node, "nodeType"); yulAssert(jsonNodeType.isString(), "Expected \"nodeType\" to be of type string!"); std::string nodeType = jsonNodeType.asString(); yulAssert(nodeType.substr(0, 3) == "Yul", "Invalid nodeType prefix"); 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); else 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(); } Expression AsmJsonImporter::createExpression(Json::Value const& _node) { Json::Value jsonNodeType = member(_node, "nodeType"); yulAssert(jsonNodeType.isString(), "Expected \"nodeType\" to be of type string!"); std::string nodeType = jsonNodeType.asString(); yulAssert(nodeType.substr(0, 3) == "Yul", "Invalid nodeType prefix"); 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 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(); } std::vector AsmJsonImporter::createExpressionVector(Json::Value const& _array) { std::vector ret; for (auto& var: _array) ret.emplace_back(createExpression(var)); return ret; } std::vector AsmJsonImporter::createStatementVector(Json::Value const& _array) { std::vector ret; for (auto& var: _array) ret.emplace_back(createStatement(var)); return ret; } Block AsmJsonImporter::createBlock(Json::Value const& _node) { auto block = createAsmNode(_node); block.statements = createStatementVector(_node["statements"]); return block; } Literal AsmJsonImporter::createLiteral(Json::Value const& _node) { auto lit = createAsmNode(_node); std::string kind = member(_node, "kind").asString(); 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()}; lit.type= YulString{member(_node, "type").asString()}; if (kind == "number") { langutil::CharStream charStream(lit.value.str(), ""); langutil::Scanner scanner{charStream}; lit.kind = LiteralKind::Number; yulAssert( scanner.currentToken() == Token::Number, "Expected number but got " + langutil::TokenTraits::friendlyName(scanner.currentToken()) + std::string(" while scanning ") + lit.value.str() ); } else if (kind == "bool") { langutil::CharStream charStream(lit.value.str(), ""); langutil::Scanner scanner{charStream}; lit.kind = LiteralKind::Boolean; yulAssert( scanner.currentToken() == Token::TrueLiteral || scanner.currentToken() == Token::FalseLiteral, "Expected true/false literal!" ); } else if (kind == "string") { lit.kind = LiteralKind::String; yulAssert( lit.value.str().size() <= 32, "String literal too long (" + std::to_string(lit.value.str().size()) + " > 32)" ); } else yulAssert(false, "unknown type of literal"); return lit; } Leave AsmJsonImporter::createLeave(Json::Value const& _node) { return createAsmNode(_node); } Identifier AsmJsonImporter::createIdentifier(Json::Value const& _node) { auto identifier = createAsmNode(_node); identifier.name = YulString(member(_node, "name").asString()); return identifier; } Assignment AsmJsonImporter::createAssignment(Json::Value const& _node) { auto assignment = createAsmNode(_node); if (_node.isMember("variableNames")) for (auto const& var: member(_node, "variableNames")) assignment.variableNames.emplace_back(createIdentifier(var)); assignment.value = std::make_unique(createExpression(member(_node, "value"))); return assignment; } FunctionCall AsmJsonImporter::createFunctionCall(Json::Value const& _node) { auto functionCall = createAsmNode(_node); 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) { auto statement = createAsmNode(_node); statement.expression = createExpression(member(_node, "expression")); return statement; } VariableDeclaration AsmJsonImporter::createVariableDeclaration(Json::Value const& _node) { auto varDec = createAsmNode(_node); for (auto const& var: member(_node, "variables")) varDec.variables.emplace_back(createTypedName(var)); varDec.value = std::make_unique(createExpression(member(_node, "value"))); return varDec; } FunctionDefinition AsmJsonImporter::createFunctionDefinition(Json::Value const& _node) { auto funcDef = createAsmNode(_node); 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) { auto ifStatement = createAsmNode(_node); ifStatement.condition = std::make_unique(createExpression(member(_node, "condition"))); ifStatement.body = createBlock(member(_node, "body")); return ifStatement; } Case AsmJsonImporter::createCase(Json::Value const& _node) { auto caseStatement = createAsmNode(_node); auto const& value = member(_node, "value"); if (value.isString()) yulAssert(value.asString() == "default", "Expected default case"); else caseStatement.value = std::make_unique(createLiteral(value)); caseStatement.body = createBlock(member(_node, "body")); return caseStatement; } Switch AsmJsonImporter::createSwitch(Json::Value const& _node) { auto switchStatement = createAsmNode(_node); switchStatement.expression = std::make_unique(createExpression(member(_node, "expression"))); for (auto const& var: member(_node, "cases")) switchStatement.cases.emplace_back(createCase(var)); return switchStatement; } ForLoop AsmJsonImporter::createForLoop(Json::Value const& _node) { auto forLoop = createAsmNode(_node); forLoop.pre = createBlock(member(_node, "pre")); forLoop.condition = std::make_unique(createExpression(member(_node, "condition"))); forLoop.post = createBlock(member(_node, "post")); forLoop.body = createBlock(member(_node, "body")); return forLoop; } Break AsmJsonImporter::createBreak(Json::Value const& _node) { return createAsmNode(_node); } Continue AsmJsonImporter::createContinue(Json::Value const& _node) { return createAsmNode(_node); } }