Skip to content

Commit

Permalink
Merge pull request #15261 from ethereum/yul_object_names_as_strings
Browse files Browse the repository at this point in the history
Yul object node names are std string instead of YulString
  • Loading branch information
clonker authored Jul 15, 2024
2 parents 64facd4 + 678e352 commit 33ececa
Show file tree
Hide file tree
Showing 13 changed files with 57 additions and 54 deletions.
2 changes: 1 addition & 1 deletion libsolidity/interface/StandardCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1638,7 +1638,7 @@ Json StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
return output;
}

std::string contractName = stack.parserResult()->name.str();
std::string contractName = stack.parserResult()->name;

bool const wildcardMatchesExperimental = true;
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "ir", wildcardMatchesExperimental))
Expand Down
2 changes: 1 addition & 1 deletion libyul/AsmAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ std::vector<YulString> AsmAnalyzer::operator()(FunctionCall const& _funCall)
if (functionName == "datasize" || functionName == "dataoffset")
{
auto const& argumentAsLiteral = std::get<Literal>(arg);
if (!m_dataNames.count(YulString(formatLiteral(argumentAsLiteral))))
if (!m_dataNames.count(formatLiteral(argumentAsLiteral)))
m_errorReporter.typeError(
3517_error,
nativeLocationOf(arg),
Expand Down
4 changes: 2 additions & 2 deletions libyul/AsmAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class AsmAnalyzer
langutil::ErrorReporter& _errorReporter,
Dialect const& _dialect,
ExternalIdentifierAccess::Resolver _resolver = ExternalIdentifierAccess::Resolver(),
std::set<YulString> _dataNames = {}
std::set<std::string> _dataNames = {}
):
m_resolver(std::move(_resolver)),
m_info(_analysisInfo),
Expand Down Expand Up @@ -128,7 +128,7 @@ class AsmAnalyzer
langutil::EVMVersion m_evmVersion;
Dialect const& m_dialect;
/// Names of data objects to be referenced by builtin functions with literal arguments.
std::set<YulString> m_dataNames;
std::set<std::string> m_dataNames;
ForLoop const* m_currentForLoop = nullptr;
/// Worst side effects encountered during analysis (including within defined functions).
SideEffects m_sideEffects;
Expand Down
41 changes: 20 additions & 21 deletions libyul/Object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ using namespace solidity::yul;

std::string Data::toString(Dialect const*, DebugInfoSelection const&, CharStreamProvider const*) const
{
return "data \"" + name.str() + "\" hex\"" + util::toHex(data) + "\"";
return "data \"" + name + "\" hex\"" + util::toHex(data) + "\"";
}

std::string Object::toString(
Expand Down Expand Up @@ -72,7 +72,7 @@ std::string Object::toString(
for (auto const& obj: subObjects)
inner += "\n" + obj->toString(_dialect, _debugInfoSelection, _soliditySourceProvider);

return useSrcComment + "object \"" + name.str() + "\" {\n" + indent(inner) + "\n}";
return useSrcComment + "object \"" + name + "\" {\n" + indent(inner) + "\n}";
}

Json Data::toJson() const
Expand All @@ -97,62 +97,61 @@ Json Object::toJson() const

Json ret;
ret["nodeType"] = "YulObject";
ret["name"] = name.str();
ret["name"] = name;
ret["code"] = codeJson;
ret["subObjects"] = subObjectsJson;
return ret;
}

std::set<YulString> Object::qualifiedDataNames() const
std::set<std::string> Object::qualifiedDataNames() const
{
std::set<YulString> qualifiedNames =
name.empty() || util::contains(name.str(), '.') ?
std::set<YulString>{} :
std::set<YulString>{name};
std::set<std::string> qualifiedNames =
name.empty() || util::contains(name, '.') ?
std::set<std::string>{} :
std::set<std::string>{name};
for (std::shared_ptr<ObjectNode> const& subObjectNode: subObjects)
{
yulAssert(qualifiedNames.count(subObjectNode->name) == 0, "");
if (util::contains(subObjectNode->name.str(), '.'))
if (util::contains(subObjectNode->name, '.'))
continue;
qualifiedNames.insert(subObjectNode->name);
if (auto const* subObject = dynamic_cast<Object const*>(subObjectNode.get()))
for (YulString const& subSubObj: subObject->qualifiedDataNames())
for (auto const& subSubObj: subObject->qualifiedDataNames())
if (subObject->name != subSubObj)
{
yulAssert(qualifiedNames.count(YulString{subObject->name.str() + "." + subSubObj.str()}) == 0, "");
qualifiedNames.insert(YulString{subObject->name.str() + "." + subSubObj.str()});
yulAssert(qualifiedNames.count(subObject->name + "." + subSubObj) == 0, "");
qualifiedNames.insert(subObject->name + "." + subSubObj);
}
}

yulAssert(qualifiedNames.count(YulString{}) == 0, "");
qualifiedNames.erase(YulString{});
yulAssert(qualifiedNames.count("") == 0, "");
return qualifiedNames;
}

std::vector<size_t> Object::pathToSubObject(YulString _qualifiedName) const
std::vector<size_t> Object::pathToSubObject(std::string_view _qualifiedName) const
{
yulAssert(_qualifiedName != name, "");
yulAssert(subIndexByName.count(name) == 0, "");

if (boost::algorithm::starts_with(_qualifiedName.str(), name.str() + "."))
_qualifiedName = YulString{_qualifiedName.str().substr(name.str().length() + 1)};
if (boost::algorithm::starts_with(_qualifiedName, name + "."))
_qualifiedName = _qualifiedName.substr(name.length() + 1);
yulAssert(!_qualifiedName.empty(), "");

std::vector<std::string> subObjectPathComponents;
boost::algorithm::split(subObjectPathComponents, _qualifiedName.str(), boost::is_any_of("."));
boost::algorithm::split(subObjectPathComponents, _qualifiedName, boost::is_any_of("."));

std::vector<size_t> path;
Object const* object = this;
for (std::string const& currentSubObjectName: subObjectPathComponents)
{
yulAssert(!currentSubObjectName.empty(), "");
auto subIndexIt = object->subIndexByName.find(YulString{currentSubObjectName});
auto subIndexIt = object->subIndexByName.find(currentSubObjectName);
yulAssert(
subIndexIt != object->subIndexByName.end(),
"Assembly object <" + _qualifiedName.str() + "> not found or does not contain code."
"Assembly object <" + std::string(_qualifiedName) + "> not found or does not contain code."
);
object = dynamic_cast<Object const*>(object->subObjects[subIndexIt->second].get());
yulAssert(object, "Assembly object <" + _qualifiedName.str() + "> not found or does not contain code.");
yulAssert(object, "Assembly object <" + std::string(_qualifiedName) + "> not found or does not contain code.");
yulAssert(object->subId != std::numeric_limits<size_t>::max(), "");
path.push_back({object->subId});
}
Expand Down
10 changes: 5 additions & 5 deletions libyul/Object.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ struct ObjectNode

/// Name of the object.
/// Can be empty since .yul files can also just contain code, without explicitly placing it in an object.
YulString name;
std::string name;
virtual std::string toString(
Dialect const* _dialect,
langutil::DebugInfoSelection const& _debugInfoSelection,
Expand All @@ -66,7 +66,7 @@ struct ObjectNode
*/
struct Data: public ObjectNode
{
Data(YulString _name, bytes _data): data(std::move(_data)) { name = _name; }
Data(std::string _name, bytes _data): data(std::move(_data)) { name = _name; }

bytes data;

Expand Down Expand Up @@ -102,7 +102,7 @@ struct Object: public ObjectNode
/// @returns the set of names of data objects accessible from within the code of
/// this object, including the name of object itself
/// Handles all names containing dots as reserved identifiers, not accessible as data.
std::set<YulString> qualifiedDataNames() const;
std::set<std::string> qualifiedDataNames() const;

/// @returns vector of subIDs if possible to reach subobject with @a _qualifiedName, throws otherwise
/// For "B.C" should return vector of two values if success (subId of B and subId of C in B).
Expand All @@ -114,14 +114,14 @@ struct Object: public ObjectNode
/// pathToSubObject("E2.F3.H4") == {1, 0, 2}
/// pathToSubObject("A1.E2") == {1}
/// The path must not lead to a @a Data object (will throw in that case).
std::vector<size_t> pathToSubObject(YulString _qualifiedName) const;
std::vector<size_t> pathToSubObject(std::string_view _qualifiedName) const;

/// sub id for object if it is subobject of another object, max value if it is not subobject
size_t subId = std::numeric_limits<size_t>::max();

std::shared_ptr<Block> code;
std::vector<std::shared_ptr<ObjectNode>> subObjects;
std::map<YulString, size_t> subIndexByName;
std::map<std::string, size_t, std::less<>> subIndexByName;
std::shared_ptr<yul::AsmAnalysisInfo> analysisInfo;

std::shared_ptr<ObjectDebugData const> debugData;
Expand Down
18 changes: 11 additions & 7 deletions libyul/ObjectParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ std::shared_ptr<Object> ObjectParser::parse(std::shared_ptr<Scanner> const& _sca
{
// Special case: Code-only form.
object = std::make_shared<Object>();
object->name = "object"_yulstring;
object->name = "object";
auto sourceNameMapping = tryParseSourceNameMapping();
object->debugData = std::make_shared<ObjectDebugData>(ObjectDebugData{sourceNameMapping});
object->code = parseBlock(sourceNameMapping);
Expand Down Expand Up @@ -185,7 +185,7 @@ void ObjectParser::parseData(Object& _containingObject)
);
advance();

YulString name = parseUniqueName(&_containingObject);
auto const name = parseUniqueName(&_containingObject);

if (currentToken() == Token::HexStringLiteral)
expectToken(Token::HexStringLiteral, false);
Expand All @@ -195,22 +195,26 @@ void ObjectParser::parseData(Object& _containingObject)
advance();
}

YulString ObjectParser::parseUniqueName(Object const* _containingObject)
std::string ObjectParser::parseUniqueName(Object const* _containingObject)
{
expectToken(Token::StringLiteral, false);
YulString name{currentLiteral()};
auto const name = currentLiteral();
if (name.empty())
parserError(3287_error, "Object name cannot be empty.");
else if (_containingObject && _containingObject->name == name)
parserError(8311_error, "Object name cannot be the same as the name of the containing object.");
else if (_containingObject && _containingObject->subIndexByName.count(name))
parserError(8794_error, "Object name \"" + name.str() + "\" already exists inside the containing object.");
parserError(8794_error, "Object name \"" + name + "\" already exists inside the containing object.");
advance();
return name;
}

void ObjectParser::addNamedSubObject(Object& _container, YulString _name, std::shared_ptr<ObjectNode> _subObject)
void ObjectParser::addNamedSubObject(Object& _container, std::string_view const _name, std::shared_ptr<ObjectNode> _subObject)
{
_container.subIndexByName[_name] = _container.subObjects.size();
_container.subIndexByName.emplace(
std::piecewise_construct,
std::forward_as_tuple(_name),
std::forward_as_tuple(_container.subObjects.size())
);
_container.subObjects.emplace_back(std::move(_subObject));
}
4 changes: 2 additions & 2 deletions libyul/ObjectParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ class ObjectParser: public langutil::ParserBase
void parseData(Object& _containingObject);

/// Tries to parse a name that is non-empty and unique inside the containing object.
YulString parseUniqueName(Object const* _containingObject);
void addNamedSubObject(Object& _container, YulString _name, std::shared_ptr<ObjectNode> _subObject);
std::string parseUniqueName(Object const* _containingObject);
void addNamedSubObject(Object& _container, std::string_view _name, std::shared_ptr<ObjectNode> _subObject);

Dialect const& m_dialect;
};
Expand Down
2 changes: 1 addition & 1 deletion libyul/YulStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ void YulStack::optimize(Object& _object, bool _isCreation)
for (auto& subNode: _object.subObjects)
if (auto subObject = dynamic_cast<Object*>(subNode.get()))
{
bool isCreation = !boost::ends_with(subObject->name.str(), "_deployed");
bool isCreation = !boost::ends_with(subObject->name, "_deployed");
optimize(*subObject, isCreation);
}

Expand Down
16 changes: 8 additions & 8 deletions libyul/backends/evm/EVMDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,14 +249,14 @@ std::map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _
yulAssert(_call.arguments.size() == 1, "");
Expression const& arg = _call.arguments.front();
YulString const dataName (formatLiteral(std::get<Literal>(arg)));
if (_context.currentObject->name == dataName)
if (_context.currentObject->name == dataName.str())
_assembly.appendAssemblySize();
else
{
std::vector<size_t> subIdPath =
_context.subIDs.count(dataName) == 0 ?
_context.currentObject->pathToSubObject(dataName) :
std::vector<size_t>{_context.subIDs.at(dataName)};
_context.subIDs.count(dataName.str()) == 0 ?
_context.currentObject->pathToSubObject(dataName.str()) :
std::vector<size_t>{_context.subIDs.at(dataName.str())};
yulAssert(!subIdPath.empty(), "Could not find assembly object <" + dataName.str() + ">.");
_assembly.appendDataSize(subIdPath);
}
Expand All @@ -270,14 +270,14 @@ std::map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _
yulAssert(_call.arguments.size() == 1, "");
Expression const& arg = _call.arguments.front();
YulString const dataName (formatLiteral(std::get<Literal>(arg)));
if (_context.currentObject->name == dataName)
if (_context.currentObject->name == dataName.str())
_assembly.appendConstant(0);
else
{
std::vector<size_t> subIdPath =
_context.subIDs.count(dataName) == 0 ?
_context.currentObject->pathToSubObject(dataName) :
std::vector<size_t>{_context.subIDs.at(dataName)};
_context.subIDs.count(dataName.str()) == 0 ?
_context.currentObject->pathToSubObject(dataName.str()) :
std::vector<size_t>{_context.subIDs.at(dataName.str())};
yulAssert(!subIdPath.empty(), "Could not find assembly object <" + dataName.str() + ">.");
_assembly.appendDataOffset(subIdPath);
}
Expand Down
2 changes: 1 addition & 1 deletion libyul/backends/evm/EVMDialect.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ struct BuiltinContext
{
Object const* currentObject = nullptr;
/// Mapping from named objects to abstract assembly sub IDs.
std::map<YulString, AbstractAssembly::SubID> subIDs;
std::map<std::string, AbstractAssembly::SubID> subIDs;
};

struct BuiltinFunctionForEVM: public BuiltinFunction
Expand Down
6 changes: 3 additions & 3 deletions libyul/backends/evm/EVMObjectCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ void EVMObjectCompiler::run(Object& _object, bool _optimize)
for (auto const& subNode: _object.subObjects)
if (auto* subObject = dynamic_cast<Object*>(subNode.get()))
{
bool isCreation = !boost::ends_with(subObject->name.str(), "_deployed");
auto subAssemblyAndID = m_assembly.createSubAssembly(isCreation, subObject->name.str());
bool isCreation = !boost::ends_with(subObject->name, "_deployed");
auto subAssemblyAndID = m_assembly.createSubAssembly(isCreation, subObject->name);
context.subIDs[subObject->name] = subAssemblyAndID.second;
subObject->subId = subAssemblyAndID.second;
compile(*subObject, *subAssemblyAndID.first, m_dialect, _optimize, m_eofVersion);
Expand All @@ -65,7 +65,7 @@ void EVMObjectCompiler::run(Object& _object, bool _optimize)
{
Data const& data = dynamic_cast<Data const&>(*subNode);
// Special handling of metadata.
if (data.name.str() == Object::metadataName())
if (data.name == Object::metadataName())
m_assembly.appendToAuxiliaryData(data.data);
else
context.subIDs[data.name] = m_assembly.appendData(data.data);
Expand Down
2 changes: 1 addition & 1 deletion test/libsolidity/MemoryGuardTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ TestCase::TestResult MemoryGuardTest::run(std::ostream& _stream, std::string con
};
handleObject("creation", *object);
size_t deployedIndex = object->subIndexByName.at(
YulString(IRNames::deployedObject(compiler().contractDefinition(contractName)))
IRNames::deployedObject(compiler().contractDefinition(contractName))
);
handleObject("runtime", dynamic_cast<Object const&>(*object->subObjects[deployedIndex]));
}
Expand Down
2 changes: 1 addition & 1 deletion tools/yulPhaser/Program.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ std::variant<std::unique_ptr<Block>, ErrorList> Program::parseObject(Dialect con
// The other object references the nested one which makes analysis fail. Below we try to
// extract just the nested one for that reason. This is just a heuristic. If there's no
// subobject with such a suffix we fall back to accepting the whole object as is.
if (subObject != nullptr && subObject->name.str() == object->name.str() + "_deployed")
if (subObject != nullptr && subObject->name == object->name + "_deployed")
{
deployedObject = dynamic_cast<Object*>(subObject.get());
if (deployedObject != nullptr)
Expand Down

0 comments on commit 33ececa

Please sign in to comment.