mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
[yul] Adding support for accessing subobjects via .
Co-authored-by: Kamil Śliwak <kamil.sliwak@codepoets.it>
This commit is contained in:
parent
952101996c
commit
6f97e6153c
@ -92,8 +92,8 @@ string locationFromSources(StringMap const& _sourceCodes, SourceLocation const&
|
||||
class Functionalizer
|
||||
{
|
||||
public:
|
||||
Functionalizer (ostream& _out, string const& _prefix, StringMap const& _sourceCodes):
|
||||
m_out(_out), m_prefix(_prefix), m_sourceCodes(_sourceCodes)
|
||||
Functionalizer (ostream& _out, string const& _prefix, StringMap const& _sourceCodes, Assembly const& _assembly):
|
||||
m_out(_out), m_prefix(_prefix), m_sourceCodes(_sourceCodes), m_assembly(_assembly)
|
||||
{}
|
||||
|
||||
void feed(AssemblyItem const& _item)
|
||||
@ -104,6 +104,9 @@ public:
|
||||
m_location = _item.location();
|
||||
printLocation();
|
||||
}
|
||||
|
||||
string expression = _item.toAssemblyText(m_assembly);
|
||||
|
||||
if (!(
|
||||
_item.canBeFunctional() &&
|
||||
_item.returnValues() <= 1 &&
|
||||
@ -111,10 +114,9 @@ public:
|
||||
))
|
||||
{
|
||||
flush();
|
||||
m_out << m_prefix << (_item.type() == Tag ? "" : " ") << _item.toAssemblyText() << endl;
|
||||
m_out << m_prefix << (_item.type() == Tag ? "" : " ") << expression << endl;
|
||||
return;
|
||||
}
|
||||
string expression = _item.toAssemblyText();
|
||||
if (_item.arguments() > 0)
|
||||
{
|
||||
expression += "(";
|
||||
@ -160,13 +162,14 @@ private:
|
||||
ostream& m_out;
|
||||
string const& m_prefix;
|
||||
StringMap const& m_sourceCodes;
|
||||
Assembly const& m_assembly;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void Assembly::assemblyStream(ostream& _out, string const& _prefix, StringMap const& _sourceCodes) const
|
||||
{
|
||||
Functionalizer f(_out, _prefix, _sourceCodes);
|
||||
Functionalizer f(_out, _prefix, _sourceCodes, *this);
|
||||
|
||||
for (auto const& i: m_items)
|
||||
f.feed(i);
|
||||
@ -639,7 +642,7 @@ LinkerObject const& Assembly::assemble() const
|
||||
case PushSubSize:
|
||||
{
|
||||
assertThrow(i.data() <= numeric_limits<size_t>::max(), AssemblyException, "");
|
||||
auto s = m_subs.at(static_cast<size_t>(i.data()))->assemble().bytecode.size();
|
||||
auto s = subAssemblyById(static_cast<size_t>(i.data()))->assemble().bytecode.size();
|
||||
i.setPushedValue(u256(s));
|
||||
uint8_t b = max<unsigned>(1, util::bytesRequired(s));
|
||||
ret.bytecode.push_back((uint8_t)Instruction::PUSH1 - 1 + b);
|
||||
@ -707,25 +710,20 @@ LinkerObject const& Assembly::assemble() const
|
||||
// Append an INVALID here to help tests find miscompilation.
|
||||
ret.bytecode.push_back(uint8_t(Instruction::INVALID));
|
||||
|
||||
for (size_t i = 0; i < m_subs.size(); ++i)
|
||||
for (auto const& [subIdPath, bytecodeOffset]: subRef)
|
||||
{
|
||||
auto references = subRef.equal_range(i);
|
||||
if (references.first == references.second)
|
||||
continue;
|
||||
for (auto ref = references.first; ref != references.second; ++ref)
|
||||
{
|
||||
bytesRef r(ret.bytecode.data() + ref->second, bytesPerDataRef);
|
||||
toBigEndian(ret.bytecode.size(), r);
|
||||
}
|
||||
ret.append(m_subs[i]->assemble());
|
||||
bytesRef r(ret.bytecode.data() + bytecodeOffset, bytesPerDataRef);
|
||||
toBigEndian(ret.bytecode.size(), r);
|
||||
ret.append(subAssemblyById(subIdPath)->assemble());
|
||||
}
|
||||
|
||||
for (auto const& i: tagRef)
|
||||
{
|
||||
size_t subId;
|
||||
size_t tagId;
|
||||
tie(subId, tagId) = i.second;
|
||||
assertThrow(subId == numeric_limits<size_t>::max() || subId < m_subs.size(), AssemblyException, "Invalid sub id");
|
||||
std::vector<size_t> const& tagPositions =
|
||||
vector<size_t> const& tagPositions =
|
||||
subId == numeric_limits<size_t>::max() ?
|
||||
m_tagPositionsInBytecode :
|
||||
m_subs[subId]->m_tagPositionsInBytecode;
|
||||
@ -758,3 +756,51 @@ LinkerObject const& Assembly::assemble() const
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
vector<size_t> Assembly::decodeSubPath(size_t _subObjectId) const
|
||||
{
|
||||
if (_subObjectId < m_subs.size())
|
||||
return {_subObjectId};
|
||||
|
||||
auto subIdPathIt = find_if(
|
||||
m_subPaths.begin(),
|
||||
m_subPaths.end(),
|
||||
[_subObjectId](auto const& subId) { return subId.second == _subObjectId; }
|
||||
);
|
||||
|
||||
assertThrow(subIdPathIt != m_subPaths.end(), AssemblyException, "");
|
||||
return subIdPathIt->first;
|
||||
}
|
||||
|
||||
size_t Assembly::encodeSubPath(vector<size_t> const& _subPath)
|
||||
{
|
||||
assertThrow(!_subPath.empty(), AssemblyException, "");
|
||||
if (_subPath.size() == 1)
|
||||
{
|
||||
assertThrow(_subPath[0] < m_subs.size(), AssemblyException, "");
|
||||
return _subPath[0];
|
||||
}
|
||||
|
||||
if (m_subPaths.find(_subPath) == m_subPaths.end())
|
||||
{
|
||||
size_t objectId = numeric_limits<size_t>::max() - m_subPaths.size();
|
||||
assertThrow(objectId >= m_subs.size(), AssemblyException, "");
|
||||
m_subPaths[_subPath] = objectId;
|
||||
}
|
||||
|
||||
return m_subPaths[_subPath];
|
||||
}
|
||||
|
||||
Assembly const* Assembly::subAssemblyById(size_t _subId) const
|
||||
{
|
||||
vector<size_t> subIds = decodeSubPath(_subId);
|
||||
Assembly const* currentAssembly = this;
|
||||
for (size_t currentSubId: subIds)
|
||||
{
|
||||
currentAssembly = currentAssembly->m_subs.at(currentSubId).get();
|
||||
assertThrow(currentAssembly, AssemblyException, "");
|
||||
}
|
||||
|
||||
assertThrow(currentAssembly != this, AssemblyException, "");
|
||||
return currentAssembly;
|
||||
}
|
||||
|
@ -146,6 +146,9 @@ public:
|
||||
/// Mark this assembly as invalid. Calling ``assemble`` on it will throw.
|
||||
void markAsInvalid() { m_invalid = true; }
|
||||
|
||||
std::vector<size_t> decodeSubPath(size_t _subObjectId) const;
|
||||
size_t encodeSubPath(std::vector<size_t> const& _subPath);
|
||||
|
||||
protected:
|
||||
/// Does the same operations as @a optimise, but should only be applied to a sub and
|
||||
/// returns the replaced tags. Also takes an argument containing the tags of this assembly
|
||||
@ -166,6 +169,9 @@ private:
|
||||
static std::string toStringInHex(u256 _value);
|
||||
|
||||
bool m_invalid = false;
|
||||
|
||||
Assembly const* subAssemblyById(size_t _subId) const;
|
||||
|
||||
protected:
|
||||
/// 0 is reserved for exception
|
||||
unsigned m_usedTags = 1;
|
||||
@ -179,6 +185,10 @@ protected:
|
||||
std::map<util::h256, std::string> m_libraries; ///< Identifiers of libraries to be linked.
|
||||
std::map<util::h256, std::string> m_immutables; ///< Identifiers of immutables.
|
||||
|
||||
/// Map from a vector representing a path to a particular sub assembly to sub assembly id.
|
||||
/// This map is used only for sub-assemblies which are not direct sub-assemblies (where path is having more than one value).
|
||||
std::map<std::vector<size_t>, size_t> m_subPaths;
|
||||
|
||||
mutable LinkerObject m_assembledObject;
|
||||
mutable std::vector<size_t> m_tagPositionsInBytecode;
|
||||
|
||||
|
@ -18,7 +18,10 @@
|
||||
|
||||
#include <libevmasm/AssemblyItem.h>
|
||||
|
||||
#include <libevmasm/Assembly.h>
|
||||
|
||||
#include <libsolutil/CommonData.h>
|
||||
#include <libsolutil/StringUtils.h>
|
||||
#include <libsolutil/FixedHash.h>
|
||||
#include <liblangutil/SourceLocation.h>
|
||||
|
||||
@ -170,7 +173,7 @@ string AssemblyItem::getJumpTypeAsString() const
|
||||
}
|
||||
}
|
||||
|
||||
string AssemblyItem::toAssemblyText() const
|
||||
string AssemblyItem::toAssemblyText(Assembly const& _assembly) const
|
||||
{
|
||||
string text;
|
||||
switch (type())
|
||||
@ -208,11 +211,18 @@ string AssemblyItem::toAssemblyText() const
|
||||
text = string("data_") + util::toHex(data());
|
||||
break;
|
||||
case PushSub:
|
||||
text = string("dataOffset(sub_") + to_string(static_cast<size_t>(data())) + ")";
|
||||
break;
|
||||
case PushSubSize:
|
||||
text = string("dataSize(sub_") + to_string(static_cast<size_t>(data())) + ")";
|
||||
{
|
||||
vector<string> subPathComponents;
|
||||
for (size_t subPathComponentId: _assembly.decodeSubPath(static_cast<size_t>(data())))
|
||||
subPathComponents.emplace_back("sub_" + to_string(subPathComponentId));
|
||||
text =
|
||||
(type() == PushSub ? "dataOffset"s : "dataSize"s) +
|
||||
"(" +
|
||||
solidity::util::joinHumanReadable(subPathComponents, ".") +
|
||||
")";
|
||||
break;
|
||||
}
|
||||
case PushProgramSize:
|
||||
text = string("bytecodeSize");
|
||||
break;
|
||||
|
@ -152,7 +152,7 @@ public:
|
||||
void setPushedValue(u256 const& _value) const { m_pushedValue = std::make_shared<u256>(_value); }
|
||||
u256 const* pushedValue() const { return m_pushedValue.get(); }
|
||||
|
||||
std::string toAssemblyText() const;
|
||||
std::string toAssemblyText(Assembly const& _assembly) const;
|
||||
|
||||
size_t m_modifierDepth = 0;
|
||||
|
||||
|
@ -72,7 +72,7 @@ AsmAnalysisInfo AsmAnalyzer::analyzeStrictAssertCorrect(Dialect const& _dialect,
|
||||
errors,
|
||||
_dialect,
|
||||
{},
|
||||
_object.dataNames()
|
||||
_object.qualifiedDataNames()
|
||||
).analyze(*_object.code);
|
||||
yulAssert(success && !errors.hasErrors(), "Invalid assembly/yul code.");
|
||||
return analysisInfo;
|
||||
|
@ -138,7 +138,7 @@ bool AssemblyStack::analyzeParsed(Object& _object)
|
||||
m_errorReporter,
|
||||
languageToDialect(m_language, m_evmVersion),
|
||||
{},
|
||||
_object.dataNames()
|
||||
_object.qualifiedDataNames()
|
||||
);
|
||||
bool success = analyzer.analyze(*_object.code);
|
||||
for (auto& subNode: _object.subObjects)
|
||||
|
@ -39,7 +39,7 @@ map<YulString, int> CompilabilityChecker::run(
|
||||
bool _optimizeStackAllocation
|
||||
)
|
||||
{
|
||||
if (EVMDialect const* evmDialect = dynamic_cast<EVMDialect const*>(&_dialect))
|
||||
if (auto const* evmDialect = dynamic_cast<EVMDialect const*>(&_dialect))
|
||||
{
|
||||
NoOutputEVMDialect noOutputDialect(*evmDialect);
|
||||
|
||||
@ -48,8 +48,10 @@ map<YulString, int> CompilabilityChecker::run(
|
||||
|
||||
BuiltinContext builtinContext;
|
||||
builtinContext.currentObject = &_object;
|
||||
for (auto name: _object.dataNames())
|
||||
builtinContext.subIDs[name] = 1;
|
||||
if (!_object.name.empty())
|
||||
builtinContext.subIDs[_object.name] = 1;
|
||||
for (auto const& subNode: _object.subObjects)
|
||||
builtinContext.subIDs[subNode->name] = 1;
|
||||
NoOutputAssembly assembly;
|
||||
CodeTransform transform(
|
||||
assembly,
|
||||
|
@ -24,9 +24,10 @@
|
||||
#include <libyul/AsmPrinter.h>
|
||||
#include <libyul/Exceptions.h>
|
||||
|
||||
#include <libsolutil/Visitor.h>
|
||||
#include <libsolutil/CommonData.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
using namespace std;
|
||||
@ -62,13 +63,54 @@ string Object::toString(Dialect const* _dialect) const
|
||||
return "object \"" + name.str() + "\" {\n" + indent(inner) + "\n}";
|
||||
}
|
||||
|
||||
set<YulString> Object::dataNames() const
|
||||
set<YulString> Object::qualifiedDataNames() const
|
||||
{
|
||||
set<YulString> names;
|
||||
names.insert(name);
|
||||
for (auto const& subObject: subIndexByName)
|
||||
names.insert(subObject.first);
|
||||
// The empty name is not valid
|
||||
names.erase(YulString{});
|
||||
return names;
|
||||
set<YulString> qualifiedNames = name.empty() ? set<YulString>{} : set<YulString>{name};
|
||||
for (shared_ptr<ObjectNode> const& subObjectNode: subObjects)
|
||||
{
|
||||
yulAssert(qualifiedNames.count(subObjectNode->name) == 0, "");
|
||||
qualifiedNames.insert(subObjectNode->name);
|
||||
if (auto const* subObject = dynamic_cast<Object const*>(subObjectNode.get()))
|
||||
for (YulString 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(YulString{}) == 0, "");
|
||||
qualifiedNames.erase(YulString{});
|
||||
return qualifiedNames;
|
||||
}
|
||||
|
||||
vector<size_t> Object::pathToSubObject(YulString _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)};
|
||||
yulAssert(!_qualifiedName.empty(), "");
|
||||
|
||||
vector<string> subObjectPathComponents;
|
||||
boost::algorithm::split(subObjectPathComponents, _qualifiedName.str(), boost::is_any_of("."));
|
||||
|
||||
vector<size_t> path;
|
||||
Object const* object = this;
|
||||
for (string const& currentSubObjectName: subObjectPathComponents)
|
||||
{
|
||||
yulAssert(!currentSubObjectName.empty(), "");
|
||||
auto subIndexIt = object->subIndexByName.find(YulString{currentSubObjectName});
|
||||
yulAssert(
|
||||
subIndexIt != object->subIndexByName.end(),
|
||||
"Assembly object <" + _qualifiedName.str() + "> 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->subId != numeric_limits<size_t>::max(), "");
|
||||
path.push_back({object->subId});
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
@ -44,6 +44,8 @@ struct ObjectNode
|
||||
virtual std::string toString(Dialect const* _dialect) const = 0;
|
||||
std::string toString() { return toString(nullptr); }
|
||||
|
||||
/// Name of the object.
|
||||
/// Can be empty since .yul files can also just contain code, without explicitly placing it in an object.
|
||||
YulString name;
|
||||
};
|
||||
|
||||
@ -68,8 +70,23 @@ public:
|
||||
std::string toString(Dialect const* _dialect) const override;
|
||||
|
||||
/// @returns the set of names of data objects accessible from within the code of
|
||||
/// this object.
|
||||
std::set<YulString> dataNames() const;
|
||||
/// this object, including the name of object itself
|
||||
std::set<YulString> 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).
|
||||
/// In object "A" if called for "A.B" will return only one value (subId for B)
|
||||
/// will return empty vector for @a _qualifiedName that equals to object name.
|
||||
/// Example:
|
||||
/// A1{ B2{ C3, D3 }, E2{ F3{ G4, K4, H4{ I5 } } } }
|
||||
/// pathToSubObject("A1.E2.F3.H4") == {1, 0, 2}
|
||||
/// 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;
|
||||
|
||||
/// 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;
|
||||
|
@ -102,9 +102,9 @@ public:
|
||||
/// Creates a new sub-assembly, which can be referenced using dataSize and dataOffset.
|
||||
virtual std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly() = 0;
|
||||
/// Appends the offset of the given sub-assembly or data.
|
||||
virtual void appendDataOffset(SubID _sub) = 0;
|
||||
virtual void appendDataOffset(std::vector<SubID> const& _subPath) = 0;
|
||||
/// Appends the size of the given sub-assembly or data.
|
||||
virtual void appendDataSize(SubID _sub) = 0;
|
||||
virtual void appendDataSize(std::vector<SubID> const& _subPath) = 0;
|
||||
/// Appends the given data to the assembly and returns its ID.
|
||||
virtual SubID appendData(bytes const& _data) = 0;
|
||||
|
||||
|
@ -147,22 +147,28 @@ pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> EthAssemblyAdapter::
|
||||
return {make_shared<EthAssemblyAdapter>(*assembly), static_cast<size_t>(sub.data())};
|
||||
}
|
||||
|
||||
void EthAssemblyAdapter::appendDataOffset(AbstractAssembly::SubID _sub)
|
||||
void EthAssemblyAdapter::appendDataOffset(vector<AbstractAssembly::SubID> const& _subPath)
|
||||
{
|
||||
auto it = m_dataHashBySubId.find(_sub);
|
||||
if (it == m_dataHashBySubId.end())
|
||||
m_assembly.pushSubroutineOffset(_sub);
|
||||
else
|
||||
if (auto it = m_dataHashBySubId.find(_subPath[0]); it != m_dataHashBySubId.end())
|
||||
{
|
||||
yulAssert(_subPath.size() == 1, "");
|
||||
m_assembly << evmasm::AssemblyItem(evmasm::PushData, it->second);
|
||||
return;
|
||||
}
|
||||
|
||||
m_assembly.pushSubroutineOffset(m_assembly.encodeSubPath(_subPath));
|
||||
}
|
||||
|
||||
void EthAssemblyAdapter::appendDataSize(AbstractAssembly::SubID _sub)
|
||||
void EthAssemblyAdapter::appendDataSize(vector<AbstractAssembly::SubID> const& _subPath)
|
||||
{
|
||||
auto it = m_dataHashBySubId.find(_sub);
|
||||
if (it == m_dataHashBySubId.end())
|
||||
m_assembly.pushSubroutineSize(static_cast<size_t>(_sub));
|
||||
else
|
||||
if (auto it = m_dataHashBySubId.find(_subPath[0]); it != m_dataHashBySubId.end())
|
||||
{
|
||||
yulAssert(_subPath.size() == 1, "");
|
||||
m_assembly << u256(m_assembly.data(h256(it->second)).size());
|
||||
return;
|
||||
}
|
||||
|
||||
m_assembly.pushSubroutineSize(m_assembly.encodeSubPath(_subPath));
|
||||
}
|
||||
|
||||
AbstractAssembly::SubID EthAssemblyAdapter::appendData(bytes const& _data)
|
||||
|
@ -58,8 +58,8 @@ public:
|
||||
void appendReturnsub(int, int) override;
|
||||
void appendAssemblySize() override;
|
||||
std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly() override;
|
||||
void appendDataOffset(SubID _sub) override;
|
||||
void appendDataSize(SubID _sub) override;
|
||||
void appendDataOffset(std::vector<SubID> const& _subPath) override;
|
||||
void appendDataSize(std::vector<SubID> const& _subPath) override;
|
||||
SubID appendData(bytes const& _data) override;
|
||||
|
||||
void appendImmutable(std::string const& _identifier) override;
|
||||
|
@ -202,12 +202,12 @@ pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> EVMAssembly::createS
|
||||
return {};
|
||||
}
|
||||
|
||||
void EVMAssembly::appendDataOffset(AbstractAssembly::SubID)
|
||||
void EVMAssembly::appendDataOffset(vector<AbstractAssembly::SubID> const&)
|
||||
{
|
||||
yulAssert(false, "Data not implemented.");
|
||||
}
|
||||
|
||||
void EVMAssembly::appendDataSize(AbstractAssembly::SubID)
|
||||
void EVMAssembly::appendDataSize(vector<AbstractAssembly::SubID> const&)
|
||||
{
|
||||
yulAssert(false, "Data not implemented.");
|
||||
}
|
||||
|
@ -80,8 +80,8 @@ public:
|
||||
/// Append the assembled size as a constant.
|
||||
void appendAssemblySize() override;
|
||||
std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly() override;
|
||||
void appendDataOffset(SubID _sub) override;
|
||||
void appendDataSize(SubID _sub) override;
|
||||
void appendDataOffset(std::vector<SubID> const& _subPath) override;
|
||||
void appendDataSize(std::vector<SubID> const& _subPath) override;
|
||||
SubID appendData(bytes const& _data) override;
|
||||
|
||||
void appendImmutable(std::string const& _identifier) override;
|
||||
|
@ -144,7 +144,7 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
|
||||
FunctionCall const& _call,
|
||||
AbstractAssembly& _assembly,
|
||||
BuiltinContext& _context,
|
||||
std::function<void(Expression const&)>
|
||||
std::function<void(Expression const&)> const&
|
||||
) {
|
||||
yulAssert(_context.currentObject, "No object available.");
|
||||
yulAssert(_call.arguments.size() == 1, "");
|
||||
@ -154,18 +154,19 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
|
||||
_assembly.appendAssemblySize();
|
||||
else
|
||||
{
|
||||
yulAssert(
|
||||
_context.subIDs.count(dataName) != 0,
|
||||
"Could not find assembly object <" + dataName.str() + ">."
|
||||
);
|
||||
_assembly.appendDataSize(_context.subIDs.at(dataName));
|
||||
vector<size_t> subIdPath =
|
||||
_context.subIDs.count(dataName) == 0 ?
|
||||
_context.currentObject->pathToSubObject(dataName) :
|
||||
vector<size_t>{_context.subIDs.at(dataName)};
|
||||
yulAssert(!subIdPath.empty(), "Could not find assembly object <" + dataName.str() + ">.");
|
||||
_assembly.appendDataSize(subIdPath);
|
||||
}
|
||||
}));
|
||||
builtins.emplace(createFunction("dataoffset", 1, 1, SideEffects{}, {true}, [](
|
||||
FunctionCall const& _call,
|
||||
AbstractAssembly& _assembly,
|
||||
BuiltinContext& _context,
|
||||
std::function<void(Expression const&)>
|
||||
std::function<void(Expression const&)> const&
|
||||
) {
|
||||
yulAssert(_context.currentObject, "No object available.");
|
||||
yulAssert(_call.arguments.size() == 1, "");
|
||||
@ -175,11 +176,12 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
|
||||
_assembly.appendConstant(0);
|
||||
else
|
||||
{
|
||||
yulAssert(
|
||||
_context.subIDs.count(dataName) != 0,
|
||||
"Could not find assembly object <" + dataName.str() + ">."
|
||||
);
|
||||
_assembly.appendDataOffset(_context.subIDs.at(dataName));
|
||||
vector<size_t> subIdPath =
|
||||
_context.subIDs.count(dataName) == 0 ?
|
||||
_context.currentObject->pathToSubObject(dataName) :
|
||||
vector<size_t>{_context.subIDs.at(dataName)};
|
||||
yulAssert(!subIdPath.empty(), "Could not find assembly object <" + dataName.str() + ">.");
|
||||
_assembly.appendDataOffset(subIdPath);
|
||||
}
|
||||
}));
|
||||
builtins.emplace(createFunction(
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
#include <libyul/Object.h>
|
||||
#include <libyul/Exceptions.h>
|
||||
#include <libevmasm/Assembly.h>
|
||||
|
||||
using namespace solidity::yul;
|
||||
using namespace std;
|
||||
@ -41,11 +42,12 @@ void EVMObjectCompiler::run(Object& _object, bool _optimize)
|
||||
BuiltinContext context;
|
||||
context.currentObject = &_object;
|
||||
|
||||
for (auto& subNode: _object.subObjects)
|
||||
if (Object* subObject = dynamic_cast<Object*>(subNode.get()))
|
||||
for (auto const& subNode: _object.subObjects)
|
||||
if (auto* subObject = dynamic_cast<Object*>(subNode.get()))
|
||||
{
|
||||
auto subAssemblyAndID = m_assembly.createSubAssembly();
|
||||
context.subIDs[subObject->name] = subAssemblyAndID.second;
|
||||
subObject->subId = subAssemblyAndID.second;
|
||||
compile(*subObject, *subAssemblyAndID.first, m_dialect, m_evm15, _optimize);
|
||||
}
|
||||
else
|
||||
|
@ -132,12 +132,12 @@ pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> NoOutputAssembly::cr
|
||||
return {};
|
||||
}
|
||||
|
||||
void NoOutputAssembly::appendDataOffset(AbstractAssembly::SubID)
|
||||
void NoOutputAssembly::appendDataOffset(std::vector<AbstractAssembly::SubID> const&)
|
||||
{
|
||||
appendInstruction(evmasm::Instruction::PUSH1);
|
||||
}
|
||||
|
||||
void NoOutputAssembly::appendDataSize(AbstractAssembly::SubID)
|
||||
void NoOutputAssembly::appendDataSize(std::vector<AbstractAssembly::SubID> const&)
|
||||
{
|
||||
appendInstruction(evmasm::Instruction::PUSH1);
|
||||
}
|
||||
|
@ -68,8 +68,8 @@ public:
|
||||
|
||||
void appendAssemblySize() override;
|
||||
std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly() override;
|
||||
void appendDataOffset(SubID _sub) override;
|
||||
void appendDataSize(SubID _sub) override;
|
||||
void appendDataOffset(std::vector<SubID> const& _subPath) override;
|
||||
void appendDataSize(std::vector<SubID> const& _subPath) override;
|
||||
SubID appendData(bytes const& _data) override;
|
||||
|
||||
void appendImmutable(std::string const& _identifier) override;
|
||||
|
@ -1249,7 +1249,7 @@ Object EVMToEwasmTranslator::run(Object const& _object)
|
||||
|
||||
ErrorList errors;
|
||||
ErrorReporter errorReporter(errors);
|
||||
AsmAnalyzer analyzer(*ret.analysisInfo, errorReporter, WasmDialect::instance(), {}, _object.dataNames());
|
||||
AsmAnalyzer analyzer(*ret.analysisInfo, errorReporter, WasmDialect::instance(), {}, _object.qualifiedDataNames());
|
||||
if (!analyzer.analyze(*ret.code))
|
||||
{
|
||||
string message = "Invalid code generated after EVM to wasm translation.\n";
|
||||
|
17
test/cmdlineTests/standard_yul_object_invalid_sub/input.json
Normal file
17
test/cmdlineTests/standard_yul_object_invalid_sub/input.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"language": "Yul",
|
||||
"sources":
|
||||
{
|
||||
"A":
|
||||
{
|
||||
"content": "object \"NamedObject\" { code { let x := dataoffset(\"NamedObject.\") sstore(add(x, 0), 0) } object \"OtherObject\" { code { revert(0, 0) } } }"
|
||||
}
|
||||
},
|
||||
"settings":
|
||||
{
|
||||
"outputSelection":
|
||||
{
|
||||
"*": { "*": ["*"], "": [ "*" ] }
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
{"errors":[{"component":"general","formattedMessage":"A:1:40: TypeError: Unknown data object \"NamedObject.\".
|
||||
object \"NamedObject\" { code { let x := dataoffset(\"NamedObject.\") sstore(add(x, 0), 0) } object \"OtherObject\" { code { revert(0, 0) } } }
|
||||
^--------^
|
||||
","message":"Unknown data object \"NamedObject.\".","severity":"error","sourceLocation":{"end":49,"file":"A","start":39},"type":"TypeError"}]}
|
@ -29,6 +29,7 @@
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <memory>
|
||||
#include <libyul/Exceptions.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity::langutil;
|
||||
@ -254,6 +255,24 @@ BOOST_AUTO_TEST_CASE(immutable)
|
||||
);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(subobject_encode_decode)
|
||||
{
|
||||
Assembly assembly;
|
||||
|
||||
shared_ptr<Assembly> subAsmPtr = make_shared<Assembly>();
|
||||
shared_ptr<Assembly> subSubAsmPtr = make_shared<Assembly>();
|
||||
|
||||
assembly.appendSubroutine(subAsmPtr);
|
||||
subAsmPtr->appendSubroutine(subSubAsmPtr);
|
||||
|
||||
BOOST_CHECK(assembly.encodeSubPath({0}) == 0);
|
||||
BOOST_REQUIRE_THROW(assembly.encodeSubPath({1}), solidity::evmasm::AssemblyException);
|
||||
BOOST_REQUIRE_THROW(assembly.decodeSubPath(1), solidity::evmasm::AssemblyException);
|
||||
|
||||
vector<size_t> subPath{0, 0};
|
||||
BOOST_CHECK(assembly.decodeSubPath(assembly.encodeSubPath(subPath)) == subPath);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
} // end namespaces
|
||||
|
@ -90,7 +90,7 @@ pair<shared_ptr<Block>, shared_ptr<yul::AsmAnalysisInfo>> yul::test::parse(
|
||||
if (!parserResult->code || errorReporter.hasErrors())
|
||||
return {};
|
||||
shared_ptr<AsmAnalysisInfo> analysisInfo = make_shared<AsmAnalysisInfo>();
|
||||
AsmAnalyzer analyzer(*analysisInfo, errorReporter, _dialect, {}, parserResult->dataNames());
|
||||
AsmAnalyzer analyzer(*analysisInfo, errorReporter, _dialect, {}, parserResult->qualifiedDataNames());
|
||||
// TODO this should be done recursively.
|
||||
if (!analyzer.analyze(*parserResult->code) || errorReporter.hasErrors())
|
||||
return {};
|
||||
|
104
test/libyul/objectCompiler/subObjectAccess.yul
Normal file
104
test/libyul/objectCompiler/subObjectAccess.yul
Normal file
@ -0,0 +1,104 @@
|
||||
object "A" {
|
||||
code {
|
||||
pop(dataoffset("A"))
|
||||
pop(datasize("A"))
|
||||
pop(dataoffset("B"))
|
||||
pop(datasize("B"))
|
||||
pop(dataoffset("B.C"))
|
||||
pop(datasize("B.C"))
|
||||
pop(dataoffset("B.E"))
|
||||
pop(datasize("B.E"))
|
||||
pop(dataoffset("B.C.D"))
|
||||
pop(datasize("B.C.D"))
|
||||
}
|
||||
|
||||
data "data1" "Hello, World!"
|
||||
|
||||
object "B" {
|
||||
code {
|
||||
pop(dataoffset("C"))
|
||||
pop(datasize("C"))
|
||||
pop(dataoffset("E"))
|
||||
pop(datasize("E"))
|
||||
pop(dataoffset("C.D"))
|
||||
pop(datasize("C.D"))
|
||||
}
|
||||
object "C" {
|
||||
code {
|
||||
pop(dataoffset("D"))
|
||||
pop(datasize("D"))
|
||||
}
|
||||
object "D" {
|
||||
code {
|
||||
invalid()
|
||||
}
|
||||
}
|
||||
}
|
||||
object "E" {
|
||||
code {
|
||||
invalid()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Assembly:
|
||||
// /* "source":26:46 */
|
||||
// pop(0x00)
|
||||
// /* "source":51:69 */
|
||||
// pop(bytecodeSize)
|
||||
// /* "source":74:94 */
|
||||
// pop(dataOffset(sub_0))
|
||||
// /* "source":99:117 */
|
||||
// pop(dataSize(sub_0))
|
||||
// /* "source":122:144 */
|
||||
// pop(dataOffset(sub_0.sub_0))
|
||||
// /* "source":149:169 */
|
||||
// pop(dataSize(sub_0.sub_0))
|
||||
// /* "source":174:196 */
|
||||
// pop(dataOffset(sub_0.sub_1))
|
||||
// /* "source":201:221 */
|
||||
// pop(dataSize(sub_0.sub_1))
|
||||
// /* "source":226:250 */
|
||||
// pop(dataOffset(sub_0.sub_0.sub_0))
|
||||
// /* "source":255:277 */
|
||||
// pop(dataSize(sub_0.sub_0.sub_0))
|
||||
// stop
|
||||
// data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421
|
||||
//
|
||||
// sub_0: assembly {
|
||||
// /* "source":347:367 */
|
||||
// pop(dataOffset(sub_0))
|
||||
// /* "source":374:392 */
|
||||
// pop(dataSize(sub_0))
|
||||
// /* "source":399:419 */
|
||||
// pop(dataOffset(sub_1))
|
||||
// /* "source":426:444 */
|
||||
// pop(dataSize(sub_1))
|
||||
// /* "source":451:473 */
|
||||
// pop(dataOffset(sub_0.sub_0))
|
||||
// /* "source":480:500 */
|
||||
// pop(dataSize(sub_0.sub_0))
|
||||
// stop
|
||||
//
|
||||
// sub_0: assembly {
|
||||
// /* "source":545:565 */
|
||||
// pop(dataOffset(sub_0))
|
||||
// /* "source":574:592 */
|
||||
// pop(dataSize(sub_0))
|
||||
// stop
|
||||
//
|
||||
// sub_0: assembly {
|
||||
// /* "source":645:654 */
|
||||
// invalid
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// sub_1: assembly {
|
||||
// /* "source":717:726 */
|
||||
// invalid
|
||||
// }
|
||||
// }
|
||||
// Bytecode: 600050604650601f50601d50603e50600850603d50600150603c50600150fe601350600850601b50600150601c50600150fe600750600150fefefefefefe600750600150fefe
|
||||
// Opcodes: PUSH1 0x0 POP PUSH1 0x46 POP PUSH1 0x1F POP PUSH1 0x1D POP PUSH1 0x3E POP PUSH1 0x8 POP PUSH1 0x3D POP PUSH1 0x1 POP PUSH1 0x3C POP PUSH1 0x1 POP INVALID PUSH1 0x13 POP PUSH1 0x8 POP PUSH1 0x1B POP PUSH1 0x1 POP PUSH1 0x1C POP PUSH1 0x1 POP INVALID PUSH1 0x7 POP PUSH1 0x1 POP INVALID INVALID INVALID INVALID INVALID INVALID PUSH1 0x7 POP PUSH1 0x1 POP INVALID INVALID
|
||||
// SourceMappings: 26:20:0:-:0;;51:18;;74:20;;99:18;;122:22;;149:20;;174:22;;201:20;;226:24;;255:22;
|
Loading…
Reference in New Issue
Block a user