/* 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 /** * Yul code and data object container. */ #include #include #include #include #include #include #include #include #include using namespace solidity; using namespace solidity::langutil; using namespace solidity::util; using namespace solidity::yul; std::string Data::toString(DebugInfoSelection const&, CharStreamProvider const*) const { return "data \"" + name + "\" hex\"" + util::toHex(data) + "\""; } std::string Object::toString( DebugInfoSelection const& _debugInfoSelection, CharStreamProvider const* _soliditySourceProvider ) const { yulAssert(hasCode(), "No code"); yulAssert(dialect(), "No dialect"); yulAssert(debugData, "No debug data"); std::string inner = "code " + AsmPrinter::format( *code(), debugData->sourceNames, _debugInfoSelection, _soliditySourceProvider ); for (auto const& obj: subObjects) inner += "\n" + obj->toString(_debugInfoSelection, _soliditySourceProvider); return debugData->formatUseSrcComment() + "object \"" + name + "\" {\n" + indent(inner) + "\n" + "}"; } Json Data::toJson() const { Json ret; ret["nodeType"] = "YulData"; ret["value"] = util::toHex(data); return ret; } std::string ObjectDebugData::formatUseSrcComment() const { if (!sourceNames) return ""; auto formatIdNamePair = [](auto&& _pair) { return std::to_string(_pair.first) + ":" + util::escapeAndQuoteString(*_pair.second); }; std::string serializedSourceNames = joinHumanReadable( ranges::views::transform(*sourceNames, formatIdNamePair) ); return "/// @use-src " + serializedSourceNames + "\n"; } Json Object::toJson() const { yulAssert(hasCode(), "No code"); yulAssert(dialect(), "No dialect"); Json codeJson; codeJson["nodeType"] = "YulCode"; codeJson["block"] = AsmJsonConverter(*dialect(), 0 /* sourceIndex */)(code()->root()); Json subObjectsJson = Json::array(); for (std::shared_ptr const& subObject: subObjects) subObjectsJson.emplace_back(subObject->toJson()); Json ret; ret["nodeType"] = "YulObject"; ret["name"] = name; ret["code"] = codeJson; ret["subObjects"] = subObjectsJson; return ret; } std::set Object::Structure::topLevelSubObjectNames() const { std::set topLevelObjectNames; for (auto const& path: objectPaths) if (!util::contains(path, '.') && path != objectName) topLevelObjectNames.insert(path); return topLevelObjectNames; } Object::Structure Object::summarizeStructure() const { Structure structure; structure.objectPaths = name.empty() || util::contains(name, '.') ? std::set{} : std::set{name}; structure.objectName = name; for (std::shared_ptr const& subObjectNode: subObjects) { yulAssert(!structure.contains(subObjectNode->name)); if (util::contains(subObjectNode->name, '.')) continue; if (auto const* subObject = dynamic_cast(subObjectNode.get())) { structure.objectPaths.insert(subObjectNode->name); auto const subObjectStructure = subObject->summarizeStructure(); for (auto const& subSubObj: subObjectStructure.objectPaths) if (subObject->name != subSubObj) { yulAssert(!structure.contains(subObject->name + "." + subSubObj)); structure.objectPaths.insert(subObject->name + "." + subSubObj); } for (auto const& subSubObjData: subObjectStructure.dataPaths) if (subObject->name != subSubObjData) { yulAssert(!structure.contains(subObject->name + "." + subSubObjData)); structure.dataPaths.insert(subObject->name + "." + subSubObjData); } } else structure.dataPaths.insert(subObjectNode->name); } yulAssert(!structure.contains("")); return structure; } std::vector Object::pathToSubObject(std::string_view _qualifiedName) const { yulAssert(_qualifiedName != name, ""); yulAssert(subIndexByName.count(name) == 0, ""); if (boost::algorithm::starts_with(_qualifiedName, name + ".")) _qualifiedName = _qualifiedName.substr(name.length() + 1); yulAssert(!_qualifiedName.empty(), ""); std::vector subObjectPathComponents; boost::algorithm::split(subObjectPathComponents, _qualifiedName, boost::is_any_of(".")); std::vector path; Object const* object = this; for (std::string const& currentSubObjectName: subObjectPathComponents) { yulAssert(!currentSubObjectName.empty(), ""); auto subIndexIt = object->subIndexByName.find(currentSubObjectName); yulAssert( subIndexIt != object->subIndexByName.end(), "Assembly object <" + std::string(_qualifiedName) + "> not found or does not contain code." ); object = dynamic_cast(object->subObjects[subIndexIt->second].get()); yulAssert(object, "Assembly object <" + std::string(_qualifiedName) + "> not found or does not contain code."); yulAssert(object->subId != std::numeric_limits::max(), ""); path.push_back({object->subId}); } return path; } std::shared_ptr Object::code() const { return m_code; } bool Object::hasCode() const { return code() != nullptr; } void Object::setCode(std::shared_ptr const& _ast, std::shared_ptr _analysisInfo) { m_code = _ast; analysisInfo = std::move(_analysisInfo); } void Object::collectSourceIndices(std::map& _indices) const { if (debugData && debugData->sourceNames.has_value()) for (auto const& [sourceIndex, sourceName]: debugData->sourceNames.value()) { solAssert(_indices.count(*sourceName) == 0 || _indices[*sourceName] == sourceIndex); _indices[*sourceName] = sourceIndex; } for (std::shared_ptr const& subNode: subObjects) if (auto subObject = dynamic_cast(subNode.get())) subObject->collectSourceIndices(_indices); } bool Object::hasContiguousSourceIndices() const { std::map sourceIndices; collectSourceIndices(sourceIndices); unsigned maxSourceIndex = 0; std::set indices; for (auto const& [sources, sourceIndex]: sourceIndices) { maxSourceIndex = std::max(sourceIndex, maxSourceIndex); indices.insert(sourceIndex); } solAssert(maxSourceIndex + 1 >= indices.size()); return indices.size() == 0 || indices.size() == maxSourceIndex + 1; } Dialect const* Object::dialect() const { if (!m_code) return nullptr; return &m_code->dialect(); }