aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libsolidity/analysis/ReferencesResolver.cpp20
-rw-r--r--libsolidity/analysis/TypeChecker.cpp25
-rw-r--r--libsolidity/ast/ASTAnnotations.h2
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp45
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp36
5 files changed, 108 insertions, 20 deletions
diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp
index a50fc6a0..2b40b4fd 100644
--- a/libsolidity/analysis/ReferencesResolver.cpp
+++ b/libsolidity/analysis/ReferencesResolver.cpp
@@ -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;
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 29011676..b07dbde1 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -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);
diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h
index 48524be3..0b6c8991 100644
--- a/libsolidity/ast/ASTAnnotations.h
+++ b/libsolidity/ast/ASTAnnotations.h
@@ -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);
};
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp
index 5257b1f7..7e0be4cb 100644
--- a/libsolidity/codegen/ContractCompiler.cpp
+++ b/libsolidity/codegen/ContractCompiler.cpp
@@ -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),
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index 019b0e4e..f2f4b8b0 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -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)