/* 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 *Component that imports an AST from json format to the internal format */ #include #include #include #include #include #include #include #include #include #include #include #include namespace solidity::frontend { using SourceLocation = langutil::SourceLocation; template ASTPointer ASTJsonImporter::nullOrCast(Json::Value const& _json) { if (_json.isNull()) return nullptr; else return std::dynamic_pointer_cast(convertJsonToASTNode(_json)); } // ============ public =========================== std::map> ASTJsonImporter::jsonToSourceUnit(std::map const& _sourceList) { for (auto const& src: _sourceList) m_sourceNames.emplace_back(std::make_shared(src.first)); for (auto const& srcPair: _sourceList) { astAssert(!srcPair.second.isNull()); astAssert(member(srcPair.second,"nodeType") == "SourceUnit", "The 'nodeType' of the highest node must be 'SourceUnit'."); m_sourceUnits[srcPair.first] = createSourceUnit(srcPair.second, srcPair.first); } return m_sourceUnits; } // ============ private =========================== // =========== general creation functions ============== template ASTPointer ASTJsonImporter::createASTNode(Json::Value const& _node, Args&&... _args) { astAssert(member(_node, "id").isInt64(), "'id'-field must be 64bit integer."); int64_t id = _node["id"].asInt64(); astAssert(m_usedIDs.insert(id).second, "Found duplicate node ID!"); auto n = std::make_shared( id, createSourceLocation(_node), std::forward(_args)... ); return n; } SourceLocation const ASTJsonImporter::createSourceLocation(Json::Value const& _node) { astAssert(member(_node, "src").isString(), "'src' must be a string"); return solidity::langutil::parseSourceLocation(_node["src"].asString(), m_sourceNames); } std::optional> ASTJsonImporter::createSourceLocations(Json::Value const& _node) const { std::vector locations; if (_node.isMember("nameLocations") && _node["nameLocations"].isArray()) { for (auto const& val: _node["nameLocations"]) locations.emplace_back(langutil::parseSourceLocation(val.asString(), m_sourceNames)); return locations; } return std::nullopt; } SourceLocation ASTJsonImporter::createNameSourceLocation(Json::Value const& _node) { astAssert(member(_node, "nameLocation").isString(), "'nameLocation' must be a string"); return solidity::langutil::parseSourceLocation(_node["nameLocation"].asString(), m_sourceNames); } SourceLocation ASTJsonImporter::createKeyNameSourceLocation(Json::Value const& _node) { astAssert(member(_node, "keyNameLocation").isString(), "'keyNameLocation' must be a string"); return solidity::langutil::parseSourceLocation(_node["keyNameLocation"].asString(), m_sourceNames); } SourceLocation ASTJsonImporter::createValueNameSourceLocation(Json::Value const& _node) { astAssert(member(_node, "valueNameLocation").isString(), "'valueNameLocation' must be a string"); return solidity::langutil::parseSourceLocation(_node["valueNameLocation"].asString(), m_sourceNames); } template ASTPointer ASTJsonImporter::convertJsonToASTNode(Json::Value const& _node) { ASTPointer ret = std::dynamic_pointer_cast(convertJsonToASTNode(_node)); astAssert(ret, "cast of converted json-node must not be nullptr"); return ret; } ASTPointer ASTJsonImporter::convertJsonToASTNode(Json::Value const& _json) { astAssert(_json["nodeType"].isString() && _json.isMember("id"), "JSON-Node needs to have 'nodeType' and 'id' fields."); std::string nodeType = _json["nodeType"].asString(); if (nodeType == "PragmaDirective") return createPragmaDirective(_json); if (nodeType == "ImportDirective") return createImportDirective(_json); if (nodeType == "ContractDefinition") return createContractDefinition(_json); if (nodeType == "IdentifierPath") return createIdentifierPath(_json); if (nodeType == "InheritanceSpecifier") return createInheritanceSpecifier(_json); if (nodeType == "UsingForDirective") return createUsingForDirective(_json); if (nodeType == "StructDefinition") return createStructDefinition(_json); if (nodeType == "EnumDefinition") return createEnumDefinition(_json); if (nodeType == "EnumValue") return createEnumValue(_json); if (nodeType == "UserDefinedValueTypeDefinition") return createUserDefinedValueTypeDefinition(_json); if (nodeType == "ParameterList") return createParameterList(_json); if (nodeType == "OverrideSpecifier") return createOverrideSpecifier(_json); if (nodeType == "FunctionDefinition") return createFunctionDefinition(_json); if (nodeType == "VariableDeclaration") return createVariableDeclaration(_json); if (nodeType == "ModifierDefinition") return createModifierDefinition(_json); if (nodeType == "ModifierInvocation") return createModifierInvocation(_json); if (nodeType == "EventDefinition") return createEventDefinition(_json); if (nodeType == "ErrorDefinition") return createErrorDefinition(_json); if (nodeType == "ElementaryTypeName") return createElementaryTypeName(_json); if (nodeType == "UserDefinedTypeName") return createUserDefinedTypeName(_json); if (nodeType == "FunctionTypeName") return createFunctionTypeName(_json); if (nodeType == "Mapping") return createMapping(_json); if (nodeType == "ArrayTypeName") return createArrayTypeName(_json); if (nodeType == "InlineAssembly") return createInlineAssembly(_json); if (nodeType == "Block") return createBlock(_json, false); if (nodeType == "UncheckedBlock") return createBlock(_json, true); if (nodeType == "PlaceholderStatement") return createPlaceholderStatement(_json); if (nodeType == "IfStatement") return createIfStatement(_json); if (nodeType == "TryCatchClause") return createTryCatchClause(_json); if (nodeType == "TryStatement") return createTryStatement(_json); if (nodeType == "WhileStatement") return createWhileStatement(_json, false); if (nodeType == "DoWhileStatement") return createWhileStatement(_json, true); if (nodeType == "ForStatement") return createForStatement(_json); if (nodeType == "Continue") return createContinue(_json); if (nodeType == "Break") return createBreak(_json); if (nodeType == "Return") return createReturn(_json); if (nodeType == "EmitStatement") return createEmitStatement(_json); if (nodeType == "RevertStatement") return createRevertStatement(_json); if (nodeType == "Throw") return createThrow(_json); if (nodeType == "VariableDeclarationStatement") return createVariableDeclarationStatement(_json); if (nodeType == "ExpressionStatement") return createExpressionStatement(_json); if (nodeType == "Conditional") return createConditional(_json); if (nodeType == "Assignment") return createAssignment(_json); if (nodeType == "TupleExpression") return createTupleExpression(_json); if (nodeType == "UnaryOperation") return createUnaryOperation(_json); if (nodeType == "BinaryOperation") return createBinaryOperation(_json); if (nodeType == "FunctionCall") return createFunctionCall(_json); if (nodeType == "FunctionCallOptions") return createFunctionCallOptions(_json); if (nodeType == "NewExpression") return createNewExpression(_json); if (nodeType == "MemberAccess") return createMemberAccess(_json); if (nodeType == "IndexAccess") return createIndexAccess(_json); if (nodeType == "IndexRangeAccess") return createIndexRangeAccess(_json); if (nodeType == "Identifier") return createIdentifier(_json); if (nodeType == "ElementaryTypeNameExpression") return createElementaryTypeNameExpression(_json); if (nodeType == "Literal") return createLiteral(_json); if (nodeType == "StructuredDocumentation") return createDocumentation(_json); else astAssert(false, "Unknown type of ASTNode: " + nodeType); // FIXME: Workaround for spurious GCC 12.1 warning (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105794) util::unreachable(); } // ============ functions to instantiate the AST-Nodes from Json-Nodes ============== ASTPointer ASTJsonImporter::createSourceUnit(Json::Value const& _node, std::string const& _srcName) { std::optional license; if (_node.isMember("license") && !_node["license"].isNull()) license = _node["license"].asString(); bool experimentalSolidity = false; if (_node.isMember("experimentalSolidity") && !_node["experimentalSolidity"].isNull()) experimentalSolidity = _node["experimentalSolidity"].asBool(); std::vector> nodes; for (auto& child: member(_node, "nodes")) nodes.emplace_back(convertJsonToASTNode(child)); ASTPointer tmp = createASTNode(_node, license, nodes, experimentalSolidity); tmp->annotation().path = _srcName; return tmp; } ASTPointer ASTJsonImporter::createPragmaDirective(Json::Value const& _node) { std::vector tokens; std::vector literals; for (auto const& lit: member(_node, "literals")) { std::string l = lit.asString(); literals.push_back(l); tokens.push_back(scanSingleToken(l)); } return createASTNode(_node, tokens, literals); } ASTPointer ASTJsonImporter::createImportDirective(Json::Value const& _node) { ASTPointer unitAlias = memberAsASTString(_node, "unitAlias"); ASTPointer path = memberAsASTString(_node, "file"); ImportDirective::SymbolAliasList symbolAliases; for (auto& tuple: member(_node, "symbolAliases")) { astAssert(tuple["local"].isNull() || tuple["local"].isString(), "expected 'local' to be a string or null!"); symbolAliases.push_back({ createIdentifier(tuple["foreign"]), tuple["local"].isNull() ? nullptr : std::make_shared(tuple["local"].asString()), createSourceLocation(tuple["foreign"])} ); } ASTPointer tmp = createASTNode( _node, path, unitAlias, createNameSourceLocation(_node), std::move(symbolAliases) ); astAssert(_node["absolutePath"].isString(), "Expected 'absolutePath' to be a string!"); tmp->annotation().absolutePath = _node["absolutePath"].asString(); return tmp; } ASTPointer ASTJsonImporter::createContractDefinition(Json::Value const& _node) { astAssert(_node["name"].isString(), "Expected 'name' to be a string!"); std::vector> baseContracts; for (auto& base: _node["baseContracts"]) baseContracts.push_back(createInheritanceSpecifier(base)); std::vector> subNodes; for (auto& subnode: _node["nodes"]) subNodes.push_back(convertJsonToASTNode(subnode)); return createASTNode( _node, std::make_shared(_node["name"].asString()), createNameSourceLocation(_node), _node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")), baseContracts, subNodes, contractKind(_node), memberAsBool(_node, "abstract") ); } ASTPointer ASTJsonImporter::createIdentifierPath(Json::Value const& _node) { astAssert(_node["name"].isString(), "Expected 'name' to be a string!"); std::vector namePath; std::vector namePathLocations; std::vector strs; std::string nameString = member(_node, "name").asString(); boost::algorithm::split(strs, nameString, boost::is_any_of(".")); astAssert(!strs.empty(), "Expected at least one element in IdentifierPath."); for (std::string s: strs) { astAssert(!s.empty(), "Expected non-empty string for IdentifierPath element."); namePath.emplace_back(s); } if (_node.isMember("nameLocations") && _node["nameLocations"].isArray()) for (auto const& val: _node["nameLocations"]) namePathLocations.emplace_back(langutil::parseSourceLocation(val.asString(), m_sourceNames)); else namePathLocations.resize(namePath.size()); astAssert( namePath.size() == namePathLocations.size(), "SourceLocations don't match name paths." ); return createASTNode( _node, namePath, namePathLocations ); } ASTPointer ASTJsonImporter::createInheritanceSpecifier(Json::Value const& _node) { std::vector> arguments; for (auto& arg: member(_node, "arguments")) arguments.push_back(convertJsonToASTNode(arg)); return createASTNode( _node, createIdentifierPath(member(_node, "baseName")), member(_node, "arguments").isNull() ? nullptr : std::make_unique>>(arguments) ); } ASTPointer ASTJsonImporter::createUsingForDirective(Json::Value const& _node) { std::vector> functions; std::vector> operators; if (_node.isMember("libraryName")) { astAssert(!_node["libraryName"].isArray()); astAssert(!_node["libraryName"]["operator"]); functions.emplace_back(createIdentifierPath(_node["libraryName"])); operators.emplace_back(std::nullopt); } else if (_node.isMember("functionList")) for (Json::Value const& function: _node["functionList"]) { if (function.isMember("function")) { astAssert(!function.isMember("operator")); astAssert(!function.isMember("definition")); functions.emplace_back(createIdentifierPath(function["function"])); operators.emplace_back(std::nullopt); } else { astAssert(function.isMember("operator")); astAssert(function.isMember("definition")); Token const operatorName = scanSingleToken(function["operator"]); astAssert(util::contains(frontend::userDefinableOperators, operatorName)); functions.emplace_back(createIdentifierPath(function["definition"])); operators.emplace_back(operatorName); } } return createASTNode( _node, std::move(functions), std::move(operators), !_node.isMember("libraryName"), _node["typeName"].isNull() ? nullptr : convertJsonToASTNode(_node["typeName"]), memberAsBool(_node, "global") ); } ASTPointer ASTJsonImporter::createStructDefinition(Json::Value const& _node) { std::vector> members; for (auto& member: _node["members"]) members.push_back(createVariableDeclaration(member)); return createASTNode( _node, memberAsASTString(_node, "name"), createNameSourceLocation(_node), members, _node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")) ); } ASTPointer ASTJsonImporter::createEnumDefinition(Json::Value const& _node) { std::vector> members; for (auto& member: _node["members"]) members.push_back(createEnumValue(member)); return createASTNode( _node, memberAsASTString(_node, "name"), createNameSourceLocation(_node), members, _node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")) ); } ASTPointer ASTJsonImporter::createEnumValue(Json::Value const& _node) { return createASTNode( _node, memberAsASTString(_node, "name") ); } ASTPointer ASTJsonImporter::createUserDefinedValueTypeDefinition(Json::Value const& _node) { return createASTNode( _node, memberAsASTString(_node, "name"), createNameSourceLocation(_node), convertJsonToASTNode(member(_node, "underlyingType")) ); } ASTPointer ASTJsonImporter::createParameterList(Json::Value const& _node) { std::vector> parameters; for (auto& param: _node["parameters"]) parameters.push_back(createVariableDeclaration(param)); return createASTNode( _node, parameters ); } ASTPointer ASTJsonImporter::createOverrideSpecifier(Json::Value const& _node) { std::vector> overrides; for (auto& param: _node["overrides"]) overrides.push_back(createIdentifierPath(param)); return createASTNode( _node, overrides ); } ASTPointer ASTJsonImporter::createFunctionDefinition(Json::Value const& _node) { astAssert(_node["kind"].isString(), "Expected 'kind' to be a string!"); Token kind; bool freeFunction = false; std::string kindStr = member(_node, "kind").asString(); if (kindStr == "constructor") kind = Token::Constructor; else if (kindStr == "function") kind = Token::Function; else if (kindStr == "fallback") kind = Token::Fallback; else if (kindStr == "receive") kind = Token::Receive; else if (kindStr == "freeFunction") { kind = Token::Function; freeFunction = true; } else astAssert(false, "Expected 'kind' to be one of [constructor, function, fallback, receive]"); std::vector> modifiers; for (auto& mod: member(_node, "modifiers")) modifiers.push_back(createModifierInvocation(mod)); Visibility vis = Visibility::Default; // Ignore public visibility for constructors if (kind == Token::Constructor) vis = (visibility(_node) == Visibility::Public) ? Visibility::Default : visibility(_node); else if (!freeFunction) vis = visibility(_node); return createASTNode( _node, memberAsASTString(_node, "name"), createNameSourceLocation(_node), vis, stateMutability(_node), freeFunction, kind, memberAsBool(_node, "virtual"), _node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")), _node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")), createParameterList(member(_node, "parameters")), modifiers, createParameterList(member(_node, "returnParameters")), memberAsBool(_node, "implemented") ? createBlock(member(_node, "body"), false) : nullptr ); } ASTPointer ASTJsonImporter::createVariableDeclaration(Json::Value const& _node) { astAssert(_node["name"].isString(), "Expected 'name' to be a string!"); VariableDeclaration::Mutability mutability{}; astAssert(member(_node, "mutability").isString(), "'mutability' expected to be string."); std::string const mutabilityStr = member(_node, "mutability").asString(); if (mutabilityStr == "constant") { mutability = VariableDeclaration::Mutability::Constant; astAssert(memberAsBool(_node, "constant")); } else { astAssert(!memberAsBool(_node, "constant")); if (mutabilityStr == "mutable") mutability = VariableDeclaration::Mutability::Mutable; else if (mutabilityStr == "immutable") mutability = VariableDeclaration::Mutability::Immutable; else astAssert(false); } return createASTNode( _node, nullOrCast(member(_node, "typeName")), std::make_shared(member(_node, "name").asString()), createNameSourceLocation(_node), nullOrCast(member(_node, "value")), visibility(_node), _node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")), _node.isMember("indexed") ? memberAsBool(_node, "indexed") : false, mutability, _node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")), location(_node) ); } ASTPointer ASTJsonImporter::createModifierDefinition(Json::Value const& _node) { return createASTNode( _node, memberAsASTString(_node, "name"), createNameSourceLocation(_node), _node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")), createParameterList(member(_node, "parameters")), memberAsBool(_node, "virtual"), _node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")), _node["body"].isNull() ? nullptr: createBlock(member(_node, "body"), false) ); } ASTPointer ASTJsonImporter::createModifierInvocation(Json::Value const& _node) { std::vector> arguments; for (auto& arg: member(_node, "arguments")) arguments.push_back(convertJsonToASTNode(arg)); return createASTNode( _node, createIdentifierPath(member(_node, "modifierName")), member(_node, "arguments").isNull() ? nullptr : std::make_unique>>(arguments) ); } ASTPointer ASTJsonImporter::createEventDefinition(Json::Value const& _node) { return createASTNode( _node, memberAsASTString(_node, "name"), createNameSourceLocation(_node), _node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")), createParameterList(member(_node, "parameters")), memberAsBool(_node, "anonymous") ); } ASTPointer ASTJsonImporter::createErrorDefinition(Json::Value const& _node) { return createASTNode( _node, memberAsASTString(_node, "name"), createNameSourceLocation(_node), _node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")), createParameterList(member(_node, "parameters")) ); } ASTPointer ASTJsonImporter::createElementaryTypeName(Json::Value const& _node) { unsigned short firstNum; unsigned short secondNum; astAssert(_node["name"].isString(), "Expected 'name' to be a string!"); std::string name = member(_node, "name").asString(); Token token; std::tie(token, firstNum, secondNum) = TokenTraits::fromIdentifierOrKeyword(name); ElementaryTypeNameToken elem(token, firstNum, secondNum); std::optional mutability = {}; if (_node.isMember("stateMutability")) mutability = stateMutability(_node); return createASTNode(_node, elem, mutability); } ASTPointer ASTJsonImporter::createUserDefinedTypeName(Json::Value const& _node) { return createASTNode( _node, createIdentifierPath(member(_node, "pathNode")) ); } ASTPointer ASTJsonImporter::createFunctionTypeName(Json::Value const& _node) { return createASTNode( _node, createParameterList(member(_node, "parameterTypes")), createParameterList(member(_node, "returnParameterTypes")), visibility(_node), stateMutability(_node) ); } ASTPointer ASTJsonImporter::createMapping(Json::Value const& _node) { return createASTNode( _node, convertJsonToASTNode(member(_node, "keyType")), memberAsASTString(_node, "keyName"), createKeyNameSourceLocation(_node), convertJsonToASTNode(member(_node, "valueType")), memberAsASTString(_node, "valueName"), createValueNameSourceLocation(_node) ); } ASTPointer ASTJsonImporter::createArrayTypeName(Json::Value const& _node) { return createASTNode( _node, convertJsonToASTNode(member(_node, "baseType")), nullOrCast(member(_node, "length")) ); } ASTPointer ASTJsonImporter::createInlineAssembly(Json::Value const& _node) { astAssert(_node["evmVersion"].isString(), "Expected evmVersion to be a string!"); auto evmVersion = langutil::EVMVersion::fromString(_node["evmVersion"].asString()); astAssert(evmVersion.has_value(), "Invalid EVM version!"); astAssert(m_evmVersion == evmVersion, "Imported tree evm version differs from configured evm version!"); yul::Dialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(evmVersion.value()); ASTPointer>> flags; if (_node.isMember("flags")) { flags = std::make_shared>>(); Json::Value const& flagsNode = _node["flags"]; astAssert(flagsNode.isArray(), "Assembly flags must be an array."); for (Json::ArrayIndex i = 0; i < flagsNode.size(); ++i) { astAssert(flagsNode[i].isString(), "Assembly flag must be a string."); flags->emplace_back(std::make_shared(flagsNode[i].asString())); } } std::shared_ptr operations = std::make_shared(yul::AsmJsonImporter(m_sourceNames).createBlock(member(_node, "AST"))); return createASTNode( _node, nullOrASTString(_node, "documentation"), dialect, std::move(flags), operations ); } ASTPointer ASTJsonImporter::createBlock(Json::Value const& _node, bool _unchecked) { std::vector> statements; for (auto& stat: member(_node, "statements")) statements.push_back(convertJsonToASTNode(stat)); return createASTNode( _node, nullOrASTString(_node, "documentation"), _unchecked, statements ); } ASTPointer ASTJsonImporter::createPlaceholderStatement(Json::Value const& _node) { return createASTNode( _node, nullOrASTString(_node, "documentation") ); } ASTPointer ASTJsonImporter::createIfStatement(Json::Value const& _node) { return createASTNode( _node, nullOrASTString(_node, "documentation"), convertJsonToASTNode(member(_node, "condition")), convertJsonToASTNode(member(_node, "trueBody")), nullOrCast(member(_node, "falseBody")) ); } ASTPointer ASTJsonImporter::createTryCatchClause(Json::Value const& _node) { return createASTNode( _node, memberAsASTString(_node, "errorName"), nullOrCast(member(_node, "parameters")), convertJsonToASTNode(member(_node, "block")) ); } ASTPointer ASTJsonImporter::createTryStatement(Json::Value const& _node) { std::vector> clauses; for (auto& param: _node["clauses"]) clauses.emplace_back(createTryCatchClause(param)); return createASTNode( _node, nullOrASTString(_node, "documentation"), convertJsonToASTNode(member(_node, "externalCall")), clauses ); } ASTPointer ASTJsonImporter::createWhileStatement(Json::Value const& _node, bool _isDoWhile=false) { return createASTNode( _node, nullOrASTString(_node, "documentation"), convertJsonToASTNode(member(_node, "condition")), convertJsonToASTNode(member(_node, "body")), _isDoWhile ); } ASTPointer ASTJsonImporter::createForStatement(Json::Value const& _node) { return createASTNode( _node, nullOrASTString(_node, "documentation"), nullOrCast(member(_node, "initializationExpression")), nullOrCast(member(_node, "condition")), nullOrCast(member(_node, "loopExpression")), convertJsonToASTNode(member(_node, "body")) ); } ASTPointer ASTJsonImporter::createContinue(Json::Value const& _node) { return createASTNode( _node, nullOrASTString(_node, "documentation") ); } ASTPointer ASTJsonImporter::createBreak(Json::Value const& _node) { return createASTNode( _node, nullOrASTString(_node, "documentation") ); } ASTPointer ASTJsonImporter::createReturn(Json::Value const& _node) { return createASTNode( _node, nullOrASTString(_node, "documentation"), nullOrCast(member(_node, "expression")) ); } ASTPointer ASTJsonImporter::createThrow(Json::Value const& _node) { return createASTNode( _node, nullOrASTString(_node, "documentation") ); } ASTPointer ASTJsonImporter::createEmitStatement(Json::Value const& _node) { return createASTNode( _node, nullOrASTString(_node, "documentation"), createFunctionCall(member(_node, "eventCall")) ); } ASTPointer ASTJsonImporter::createRevertStatement(Json::Value const& _node) { return createASTNode( _node, nullOrASTString(_node, "documentation"), createFunctionCall(member(_node, "errorCall")) ); } ASTPointer ASTJsonImporter::createVariableDeclarationStatement(Json::Value const& _node) { std::vector> variables; for (auto& var: member(_node, "declarations")) variables.push_back(var.isNull() ? nullptr : createVariableDeclaration(var)); //unnamed components are empty pointers return createASTNode( _node, nullOrASTString(_node, "documentation"), variables, nullOrCast(member(_node, "initialValue")) ); } ASTPointer ASTJsonImporter::createExpressionStatement(Json::Value const& _node) { return createASTNode( _node, nullOrASTString(_node, "documentation"), convertJsonToASTNode(member(_node, "expression")) ); } ASTPointer ASTJsonImporter::createConditional(Json::Value const& _node) { return createASTNode( _node, convertJsonToASTNode(member(_node, "condition")), convertJsonToASTNode(member(_node, "trueExpression")), convertJsonToASTNode(member(_node, "falseExpression")) ); } ASTPointer ASTJsonImporter::createAssignment(Json::Value const& _node) { return createASTNode( _node, convertJsonToASTNode(member(_node, "leftHandSide")), scanSingleToken(member(_node, "operator")), convertJsonToASTNode(member(_node, "rightHandSide")) ); } ASTPointer ASTJsonImporter::createTupleExpression(Json::Value const& _node) { std::vector> components; for (auto& comp: member(_node, "components")) components.push_back(nullOrCast(comp)); return createASTNode( _node, components, memberAsBool(_node, "isInlineArray") ); } ASTPointer ASTJsonImporter::createUnaryOperation(Json::Value const& _node) { return createASTNode( _node, scanSingleToken(member(_node, "operator")), convertJsonToASTNode(member(_node, "subExpression")), memberAsBool(_node, "prefix") ); } ASTPointer ASTJsonImporter::createBinaryOperation(Json::Value const& _node) { return createASTNode( _node, convertJsonToASTNode(member(_node, "leftExpression")), scanSingleToken(member(_node, "operator")), convertJsonToASTNode(member(_node, "rightExpression")) ); } ASTPointer ASTJsonImporter::createFunctionCall(Json::Value const& _node) { std::vector> arguments; for (auto& arg: member(_node, "arguments")) arguments.push_back(convertJsonToASTNode(arg)); std::vector> names; for (auto& name: member(_node, "names")) { astAssert(name.isString(), "Expected 'names' members to be strings!"); names.push_back(std::make_shared(name.asString())); } std::optional> sourceLocations = createSourceLocations(_node); return createASTNode( _node, convertJsonToASTNode(member(_node, "expression")), arguments, names, sourceLocations ? *sourceLocations : std::vector(names.size()) ); } ASTPointer ASTJsonImporter::createFunctionCallOptions(Json::Value const& _node) { std::vector> options; for (auto& option: member(_node, "options")) options.push_back(convertJsonToASTNode(option)); std::vector> names; for (auto& name: member(_node, "names")) { astAssert(name.isString(), "Expected 'names' members to be strings!"); names.push_back(std::make_shared(name.asString())); } return createASTNode( _node, convertJsonToASTNode(member(_node, "expression")), options, names ); } ASTPointer ASTJsonImporter::createNewExpression(Json::Value const& _node) { return createASTNode( _node, convertJsonToASTNode(member(_node, "typeName")) ); } ASTPointer ASTJsonImporter::createMemberAccess(Json::Value const& _node) { SourceLocation memberLocation; if (member(_node, "memberLocation").isString()) memberLocation = solidity::langutil::parseSourceLocation(_node["memberLocation"].asString(), m_sourceNames); return createASTNode( _node, convertJsonToASTNode(member(_node, "expression")), memberAsASTString(_node, "memberName"), std::move(memberLocation) ); } ASTPointer ASTJsonImporter::createIndexAccess(Json::Value const& _node) { return createASTNode( _node, convertJsonToASTNode(member(_node, "baseExpression")), nullOrCast(member(_node, "indexExpression")) ); } ASTPointer ASTJsonImporter::createIndexRangeAccess(Json::Value const& _node) { return createASTNode( _node, convertJsonToASTNode(member(_node, "baseExpression")), nullOrCast(member(_node, "startExpression")), nullOrCast(member(_node, "endExpression")) ); } ASTPointer ASTJsonImporter::createIdentifier(Json::Value const& _node) { return createASTNode(_node, memberAsASTString(_node, "name")); } ASTPointer ASTJsonImporter::createElementaryTypeNameExpression(Json::Value const& _node) { return createASTNode( _node, createElementaryTypeName(member(_node, "typeName")) ); } ASTPointer ASTJsonImporter::createLiteral(Json::Value const& _node) { static std::string const valStr = "value"; static std::string const hexValStr = "hexValue"; astAssert(member(_node, valStr).isString() || member(_node, hexValStr).isString(), "Literal-value is unset."); ASTPointer value = _node.isMember(hexValStr) ? std::make_shared(util::asString(util::fromHex(_node[hexValStr].asString()))) : std::make_shared(_node[valStr].asString()); return createASTNode( _node, literalTokenKind(_node), value, member(_node, "subdenomination").isNull() ? Literal::SubDenomination::None : subdenomination(_node) ); } ASTPointer ASTJsonImporter::createDocumentation(Json::Value const& _node) { static std::string const textString = "text"; astAssert(member(_node, textString).isString(), "'text' must be a string"); return createASTNode( _node, std::make_shared(_node[textString].asString()) ); } // ===== helper functions ========== Json::Value ASTJsonImporter::member(Json::Value const& _node, std::string const& _name) { if (!_node.isMember(_name)) return Json::nullValue; return _node[_name]; } Token ASTJsonImporter::scanSingleToken(Json::Value const& _node) { langutil::CharStream charStream(_node.asString(), ""); langutil::Scanner scanner{charStream}; astAssert(scanner.peekNextToken() == Token::EOS, "Token string is too long."); return scanner.currentToken(); } ASTPointer ASTJsonImporter::nullOrASTString(Json::Value const& _json, std::string const& _name) { return _json[_name].isString() ? memberAsASTString(_json, _name) : nullptr; } ASTPointer ASTJsonImporter::memberAsASTString(Json::Value const& _node, std::string const& _name) { Json::Value value = member(_node, _name); astAssert(value.isString(), "field " + _name + " must be of type string."); return std::make_shared(_node[_name].asString()); } bool ASTJsonImporter::memberAsBool(Json::Value const& _node, std::string const& _name) { Json::Value value = member(_node, _name); astAssert(value.isBool(), "field " + _name + " must be of type boolean."); return _node[_name].asBool(); } // =========== JSON to definition helpers ======================= ContractKind ASTJsonImporter::contractKind(Json::Value const& _node) { ContractKind kind; astAssert(!member(_node, "contractKind").isNull(), "'Contract-kind' can not be null."); if (_node["contractKind"].asString() == "interface") kind = ContractKind::Interface; else if (_node["contractKind"].asString() == "contract") kind = ContractKind::Contract; else if (_node["contractKind"].asString() == "library") kind = ContractKind::Library; else astAssert(false, "Unknown ContractKind"); return kind; } Token ASTJsonImporter::literalTokenKind(Json::Value const& _node) { astAssert(member(_node, "kind").isString(), "Token-'kind' expected to be a string."); Token tok; if (_node["kind"].asString() == "number") tok = Token::Number; else if (_node["kind"].asString() == "string") tok = Token::StringLiteral; else if (_node["kind"].asString() == "unicodeString") tok = Token::UnicodeStringLiteral; else if (_node["kind"].asString() == "hexString") tok = Token::HexStringLiteral; else if (_node["kind"].asString() == "bool") tok = (member(_node, "value").asString() == "true") ? Token::TrueLiteral : Token::FalseLiteral; else astAssert(false, "Unknown kind of literalString"); return tok; } Visibility ASTJsonImporter::visibility(Json::Value const& _node) { Json::Value visibility = member(_node, "visibility"); astAssert(visibility.isString(), "'visibility' expected to be a string."); std::string const visibilityStr = visibility.asString(); if (visibilityStr == "default") return Visibility::Default; else if (visibilityStr == "private") return Visibility::Private; else if ( visibilityStr == "internal") return Visibility::Internal; else if (visibilityStr == "public") return Visibility::Public; else if (visibilityStr == "external") return Visibility::External; else astAssert(false, "Unknown visibility declaration"); // FIXME: Workaround for spurious GCC 12.1 warning (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105794) util::unreachable(); } VariableDeclaration::Location ASTJsonImporter::location(Json::Value const& _node) { Json::Value storageLoc = member(_node, "storageLocation"); astAssert(storageLoc.isString(), "'storageLocation' expected to be a string."); std::string const storageLocStr = storageLoc.asString(); if (storageLocStr == "default") return VariableDeclaration::Location::Unspecified; else if (storageLocStr == "storage") return VariableDeclaration::Location::Storage; else if (storageLocStr == "memory") return VariableDeclaration::Location::Memory; else if (storageLocStr == "calldata") return VariableDeclaration::Location::CallData; else astAssert(false, "Unknown location declaration"); // FIXME: Workaround for spurious GCC 12.1 warning (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105794) util::unreachable(); } Literal::SubDenomination ASTJsonImporter::subdenomination(Json::Value const& _node) { Json::Value subDen = member(_node, "subdenomination"); if (subDen.isNull()) return Literal::SubDenomination::None; astAssert(subDen.isString(), "'subDenomination' expected to be string."); std::string const subDenStr = subDen.asString(); if (subDenStr == "wei") return Literal::SubDenomination::Wei; else if (subDenStr == "gwei") return Literal::SubDenomination::Gwei; else if (subDenStr == "ether") return Literal::SubDenomination::Ether; else if (subDenStr == "seconds") return Literal::SubDenomination::Second; else if (subDenStr == "minutes") return Literal::SubDenomination::Minute; else if (subDenStr == "hours") return Literal::SubDenomination::Hour; else if (subDenStr == "days") return Literal::SubDenomination::Day; else if (subDenStr == "weeks") return Literal::SubDenomination::Week; else if (subDenStr == "years") return Literal::SubDenomination::Year; else astAssert(false, "Unknown subdenomination"); // FIXME: Workaround for spurious GCC 12.1 warning (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105794) util::unreachable(); } StateMutability ASTJsonImporter::stateMutability(Json::Value const& _node) { astAssert(member(_node, "stateMutability").isString(), "StateMutability' expected to be string."); std::string const mutabilityStr = member(_node, "stateMutability").asString(); if (mutabilityStr == "pure") return StateMutability::Pure; else if (mutabilityStr == "view") return StateMutability::View; else if (mutabilityStr == "nonpayable") return StateMutability::NonPayable; else if (mutabilityStr == "payable") return StateMutability::Payable; else astAssert(false, "Unknown stateMutability"); // FIXME: Workaround for spurious GCC 12.1 warning (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105794) util::unreachable(); } }