diff options
-rw-r--r-- | libsolidity/analysis/ReferencesResolver.cpp | 20 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 25 | ||||
-rw-r--r-- | libsolidity/ast/ASTAnnotations.h | 2 | ||||
-rw-r--r-- | libsolidity/codegen/ContractCompiler.cpp | 45 | ||||
-rw-r--r-- | test/libsolidity/SolidityEndToEndTest.cpp | 36 |
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) |