/* 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 std; using namespace solidity::langutil; namespace solidity::yul { using SourceLocation = langutil::SourceLocation; SourceLocation const AsmJsonImporter::createSourceLocation(Json const& _node) { yulAssert(member(_node, "src").is_string(), "'src' must be a string"); return solidity::langutil::parseSourceLocation(_node["src"].get(), m_sourceNames); } template T AsmJsonImporter::createAsmNode(Json 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 AsmJsonImporter::member(Json const& _node, string const& _name) { if (!_node.contains(_name)) return Json{}; return _node[_name]; } TypedName AsmJsonImporter::createTypedName(Json const& _node) { auto typedName = createAsmNode(_node); typedName.type = YulString{member(_node, "type").get()}; typedName.name = YulString{member(_node, "name").get()}; return typedName; } Statement AsmJsonImporter::createStatement(Json const& _node) { Json jsonNodeType = member(_node, "nodeType"); yulAssert(jsonNodeType.is_string(), "Expected \"nodeType\" to be of type string!"); string nodeType = jsonNodeType.get(); 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 const& _node) { Json jsonNodeType = member(_node, "nodeType"); yulAssert(jsonNodeType.is_string(), "Expected \"nodeType\" to be of type string!"); string nodeType = jsonNodeType.get(); 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(); } vector AsmJsonImporter::createExpressionVector(Json const& _array) { vector ret; for (auto& var: _array) ret.emplace_back(createExpression(var)); return ret; } vector AsmJsonImporter::createStatementVector(Json const& _array) { vector ret; for (auto& var: _array) ret.emplace_back(createStatement(var)); return ret; } Block AsmJsonImporter::createBlock(Json const& _node) { auto block = createAsmNode(_node); block.statements = createStatementVector(_node["statements"]); return block; } Literal AsmJsonImporter::createLiteral(Json const& _node) { auto lit = createAsmNode(_node); string kind = member(_node, "kind").get(); solAssert(member(_node, "hexValue").is_string() || member(_node, "value").is_string(), ""); if (_node.contains("hexValue")) lit.value = YulString{util::asString(util::fromHex(member(_node, "hexValue").get()))}; else lit.value = YulString{member(_node, "value").get()}; lit.type= YulString{member(_node, "type").get()}; 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()) + 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 (" + to_string(lit.value.str().size()) + " > 32)" ); } else yulAssert(false, "unknown type of literal"); return lit; } Leave AsmJsonImporter::createLeave(Json const& _node) { return createAsmNode(_node); } Identifier AsmJsonImporter::createIdentifier(Json const& _node) { auto identifier = createAsmNode(_node); identifier.name = YulString(member(_node, "name").get()); return identifier; } Assignment AsmJsonImporter::createAssignment(Json const& _node) { auto assignment = createAsmNode(_node); if (_node.contains("variableNames")) for (auto const& var: member(_node, "variableNames")) assignment.variableNames.emplace_back(createIdentifier(var)); assignment.value = make_unique(createExpression(member(_node, "value"))); return assignment; } FunctionCall AsmJsonImporter::createFunctionCall(Json 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 const& _node) { auto statement = createAsmNode(_node); statement.expression = createExpression(member(_node, "expression")); return statement; } VariableDeclaration AsmJsonImporter::createVariableDeclaration(Json const& _node) { auto varDec = createAsmNode(_node); for (auto const& var: member(_node, "variables")) varDec.variables.emplace_back(createTypedName(var)); varDec.value = make_unique(createExpression(member(_node, "value"))); return varDec; } FunctionDefinition AsmJsonImporter::createFunctionDefinition(Json const& _node) { auto funcDef = createAsmNode(_node); funcDef.name = YulString{member(_node, "name").get()}; if (_node.contains("parameters")) for (auto const& var: member(_node, "parameters")) funcDef.parameters.emplace_back(createTypedName(var)); if (_node.contains("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 const& _node) { auto ifStatement = createAsmNode(_node); ifStatement.condition = make_unique(createExpression(member(_node, "condition"))); ifStatement.body = createBlock(member(_node, "body")); return ifStatement; } Case AsmJsonImporter::createCase(Json const& _node) { auto caseStatement = createAsmNode(_node); auto const& value = member(_node, "value"); if (value.is_string()) yulAssert(value.get() == "default", "Expected default case"); else caseStatement.value = make_unique(createLiteral(value)); caseStatement.body = createBlock(member(_node, "body")); return caseStatement; } Switch AsmJsonImporter::createSwitch(Json const& _node) { auto switchStatement = createAsmNode(_node); switchStatement.expression = 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 const& _node) { auto forLoop = createAsmNode(_node); forLoop.pre = createBlock(member(_node, "pre")); forLoop.condition = make_unique(createExpression(member(_node, "condition"))); forLoop.post = createBlock(member(_node, "post")); forLoop.body = createBlock(member(_node, "body")); return forLoop; } Break AsmJsonImporter::createBreak(Json const& _node) { return createAsmNode(_node); } Continue AsmJsonImporter::createContinue(Json const& _node) { return createAsmNode(_node); } }