mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Storage access from inline assembly.
This commit is contained in:
parent
5f4b68e211
commit
478f2997ea
@ -28,6 +28,8 @@
|
||||
#include <libsolidity/inlineasm/AsmAnalysis.h>
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::solidity;
|
||||
@ -166,10 +168,26 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
|
||||
assembly::ExternalIdentifierAccess::Resolver resolver =
|
||||
[&](assembly::Identifier const& _identifier, assembly::IdentifierContext) {
|
||||
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name);
|
||||
bool isSlot = boost::algorithm::ends_with(_identifier.name, "_slot");
|
||||
bool isOffset = boost::algorithm::ends_with(_identifier.name, "_offset");
|
||||
if (isSlot || isOffset)
|
||||
{
|
||||
// special mode to access storage variables
|
||||
if (!declarations.empty())
|
||||
// the special identifier exists itself, we should not allow that.
|
||||
return size_t(-1);
|
||||
string realName = _identifier.name.substr(0,
|
||||
_identifier.name.size() - isSlot ?
|
||||
string("_slot").size() :
|
||||
string("_offset").size()
|
||||
);
|
||||
declarations = m_resolver.nameFromCurrentScope(realName);
|
||||
}
|
||||
if (declarations.size() != 1)
|
||||
return size_t(-1);
|
||||
_inlineAssembly.annotation().externalReferences[&_identifier].isSlot = isSlot;
|
||||
_inlineAssembly.annotation().externalReferences[&_identifier].isOffset = isOffset;
|
||||
_inlineAssembly.annotation().externalReferences[&_identifier].declaration = declarations.front();
|
||||
// At this stage we do not yet know the stack size of the identifier, so we just return 1.
|
||||
return size_t(1);
|
||||
};
|
||||
assembly::AsmAnalyzer::Scopes scopes;
|
||||
|
@ -642,22 +642,35 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
||||
solAssert(!!declaration, "");
|
||||
if (auto var = dynamic_cast<VariableDeclaration const*>(declaration))
|
||||
{
|
||||
if (!var->isLocalVariable())
|
||||
if (ref->second.isSlot || ref->second.isOffset)
|
||||
{
|
||||
typeError(_identifier.location, "Only local variables are supported.");
|
||||
if (!var->isStateVariable() && !var->type()->dataStoredIn(DataLocation::Storage))
|
||||
{
|
||||
typeError(_identifier.location, "The suffixes _offset and _slot can only be used on storage variables.");
|
||||
return size_t(-1);
|
||||
}
|
||||
else if (_context != assembly::IdentifierContext::RValue)
|
||||
{
|
||||
typeError(_identifier.location, "Storage variables cannot be assigned to.");
|
||||
return size_t(-1);
|
||||
}
|
||||
}
|
||||
else if (!var->isLocalVariable())
|
||||
{
|
||||
typeError(_identifier.location, "Only local variables are supported. To access storage variables, use the _slot and _offset suffixes.");
|
||||
return size_t(-1);
|
||||
}
|
||||
if (var->type()->dataStoredIn(DataLocation::Storage))
|
||||
else if (var->type()->dataStoredIn(DataLocation::Storage))
|
||||
{
|
||||
typeError(_identifier.location, "Storage reference variables are not supported.");
|
||||
typeError(_identifier.location, "You have to use the _slot or _offset prefix to access storage reference variables.");
|
||||
return size_t(-1);
|
||||
}
|
||||
if (var->type()->sizeOnStack() != 1)
|
||||
else if (var->type()->sizeOnStack() != 1)
|
||||
{
|
||||
typeError(_identifier.location, "Only types that use one stack slot are supported.");
|
||||
return size_t(-1);
|
||||
}
|
||||
if (var->isConstant())
|
||||
else if (var->isConstant())
|
||||
{
|
||||
typeError(_identifier.location, "Constant variables not supported by inline assembly.");
|
||||
return size_t(-1);
|
||||
|
@ -122,6 +122,8 @@ struct InlineAssemblyAnnotation: StatementAnnotation
|
||||
struct ExternalIdentifierInfo
|
||||
{
|
||||
Declaration const* declaration = nullptr;
|
||||
bool isSlot = false; ///< Whether the storage slot of a variable is queried.
|
||||
bool isOffset = false; ///< Whether the intra-slot offset of a storage variable is queried.
|
||||
size_t valueSize = size_t(-1);
|
||||
};
|
||||
|
||||
|
@ -542,6 +542,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
|
||||
solAssert(!!decl->type(), "Type of declaration required but not yet determined.");
|
||||
if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(decl))
|
||||
{
|
||||
solAssert(!ref->second.isOffset && !ref->second.isSlot, "");
|
||||
functionDef = &m_context.resolveVirtualFunction(*functionDef);
|
||||
_assembly.append(m_context.functionEntryLabel(*functionDef).pushTag());
|
||||
// If there is a runtime context, we have to merge both labels into the same
|
||||
@ -557,19 +558,42 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
|
||||
else if (auto variable = dynamic_cast<VariableDeclaration const*>(decl))
|
||||
{
|
||||
solAssert(!variable->isConstant(), "");
|
||||
solAssert(m_context.isLocalVariable(variable), "");
|
||||
int stackDiff = _assembly.deposit() - m_context.baseStackOffsetOfVariable(*variable);
|
||||
if (stackDiff < 1 || stackDiff > 16)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
CompilerError() <<
|
||||
errinfo_sourceLocation(_inlineAssembly.location()) <<
|
||||
errinfo_comment("Stack too deep, try removing local variables.")
|
||||
);
|
||||
solAssert(variable->type()->sizeOnStack() == 1, "");
|
||||
_assembly.append(dupInstruction(stackDiff));
|
||||
if (m_context.isStateVariable(decl))
|
||||
{
|
||||
auto const& location = m_context.storageLocationOfVariable(*decl);
|
||||
if (ref->second.isSlot)
|
||||
m_context << location.first;
|
||||
else if (ref->second.isOffset)
|
||||
m_context << u256(location.second);
|
||||
else
|
||||
solAssert(false, "");
|
||||
}
|
||||
else if (m_context.isLocalVariable(decl))
|
||||
{
|
||||
int stackDiff = _assembly.deposit() - m_context.baseStackOffsetOfVariable(*variable);
|
||||
if (ref->second.isSlot || ref->second.isOffset)
|
||||
{
|
||||
solAssert(variable->type()->sizeOnStack() == 2, "");
|
||||
if (ref->second.isOffset)
|
||||
stackDiff--;
|
||||
}
|
||||
else
|
||||
solAssert(variable->type()->sizeOnStack() == 1, "");
|
||||
if (stackDiff < 1 || stackDiff > 16)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
CompilerError() <<
|
||||
errinfo_sourceLocation(_inlineAssembly.location()) <<
|
||||
errinfo_comment("Stack too deep, try removing local variables.")
|
||||
);
|
||||
solAssert(variable->type()->sizeOnStack() == 1, "");
|
||||
_assembly.append(dupInstruction(stackDiff));
|
||||
}
|
||||
else
|
||||
solAssert(false, "");
|
||||
}
|
||||
else if (auto contract = dynamic_cast<ContractDefinition const*>(decl))
|
||||
{
|
||||
solAssert(!ref->second.isOffset && !ref->second.isSlot, "");
|
||||
solAssert(contract->isLibrary(), "");
|
||||
_assembly.appendLibraryAddress(contract->fullyQualifiedName());
|
||||
}
|
||||
@ -580,6 +604,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
|
||||
else
|
||||
{
|
||||
// lvalue context
|
||||
solAssert(!ref->second.isOffset && !ref->second.isSlot, "");
|
||||
auto variable = dynamic_cast<VariableDeclaration const*>(decl);
|
||||
solAssert(
|
||||
!!variable && m_context.isLocalVariable(variable),
|
||||
|
@ -7427,10 +7427,38 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage_access)
|
||||
uint16 public y;
|
||||
uint public z;
|
||||
function f() returns (bool) {
|
||||
uint off1;
|
||||
uint off2;
|
||||
assembly {
|
||||
sstore(z_slot, 7)
|
||||
off1 := z_offset
|
||||
off2 := y_offset
|
||||
}
|
||||
assert(off1 == 0);
|
||||
assert(off2 == 2);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode, 0, "C");
|
||||
BOOST_CHECK(callContractFunction("f()") == encodeArgs(true));
|
||||
BOOST_CHECK(callContractFunction("z()") == encodeArgs(u256(7)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(inline_assembly_storage_access_via_pointer)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract C {
|
||||
struct Data { uint contents; }
|
||||
uint public separator;
|
||||
Data public a;
|
||||
uint public separator2;
|
||||
function f() returns (bool) {
|
||||
Data x = a;
|
||||
uint off;
|
||||
assembly {
|
||||
sstore(z$slot, 7)
|
||||
off := z$offset
|
||||
sstore(x_slot, 7)
|
||||
off := x_offset
|
||||
}
|
||||
assert(off == 0);
|
||||
return true;
|
||||
@ -7439,7 +7467,9 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage_access)
|
||||
)";
|
||||
compileAndRun(sourceCode, 0, "C");
|
||||
BOOST_CHECK(callContractFunction("f()") == encodeArgs(true));
|
||||
BOOST_CHECK(callContractFunction("z()") == encodeArgs(u256(7)));
|
||||
BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(7)));
|
||||
BOOST_CHECK(callContractFunction("separator()") == encodeArgs(u256(0)));
|
||||
BOOST_CHECK(callContractFunction("separator2()") == encodeArgs(u256(0)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(inline_assembly_jumps)
|
||||
|
Loading…
Reference in New Issue
Block a user