Merge pull request #2275 from ethereum/returndata_lll

Add RETURNDATACOPY and RETURNDATASIZE to assembly (and LLL)
This commit is contained in:
chriseth 2017-06-13 17:41:52 +02:00 committed by GitHub
commit 6b052249da
13 changed files with 108 additions and 6 deletions

View File

@ -1,14 +1,15 @@
### 0.4.12 (unreleased)
Features:
* Assembler: renamed ``SHA3`` to `KECCAK256``.
* AST: export all attributes to Json format
* Assembly: renamed ``SHA3`` to `KECCAK256``.
* Assembly: Add ``RETURNDATASIZE`` and ``RETURNDATACOPY`` (EIP211) instructions.
* AST: export all attributes to JSON format.
* Inline Assembly: Present proper error message when not supplying enough arguments to a functional
instruction.
* Inline Assembly: introduce ``keccak256`` as an opcode. ``sha3`` is still a valid alias.
Bugfixes:
* Unused variable warnings no longer issued for variables used inside inline assembly
* Unused variable warnings no longer issued for variables used inside inline assembly.
### 0.4.11 (2017-05-03)

View File

@ -234,6 +234,10 @@ In the grammar, opcodes are represented as pre-defined identifiers.
+-------------------------+------+-----------------------------------------------------------------+
| extcodecopy(a, t, f, s) | `-` | like codecopy(t, f, s) but take code at address a |
+-------------------------+------+-----------------------------------------------------------------+
| returndatasize | | size of the last returndata |
+-------------------------+------+-----------------------------------------------------------------+
| returndatacopy(t, f, s) | `*` | copy s bytes from returndata at position f to mem at position t |
+-------------------------+------+-----------------------------------------------------------------+
| create(v, p, s) | | create new contract with code mem[p..(p+s)) and send v wei |
| | | and return the new address |
+-------------------------+------+-----------------------------------------------------------------+

View File

@ -103,6 +103,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
break;
case Instruction::CALLDATACOPY:
case Instruction::CODECOPY:
case Instruction::RETURNDATACOPY:
gas += memoryGas(0, -2);
gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-2));
break;

View File

@ -67,6 +67,8 @@ const std::map<std::string, Instruction> dev::solidity::c_instructions =
{ "GASPRICE", Instruction::GASPRICE },
{ "EXTCODESIZE", Instruction::EXTCODESIZE },
{ "EXTCODECOPY", Instruction::EXTCODECOPY },
{ "RETURNDATASIZE", Instruction::RETURNDATASIZE },
{ "RETURNDATACOPY", Instruction::RETURNDATACOPY },
{ "BLOCKHASH", Instruction::BLOCKHASH },
{ "COINBASE", Instruction::COINBASE },
{ "TIMESTAMP", Instruction::TIMESTAMP },
@ -203,6 +205,8 @@ static const std::map<Instruction, InstructionInfo> c_instructionInfo =
{ Instruction::GASPRICE, { "GASPRICE", 0, 0, 1, false, Tier::Base } },
{ Instruction::EXTCODESIZE, { "EXTCODESIZE", 0, 1, 1, false, Tier::ExtCode } },
{ Instruction::EXTCODECOPY, { "EXTCODECOPY", 0, 4, 0, true, Tier::ExtCode } },
{ Instruction::RETURNDATASIZE, {"RETURNDATASIZE", 0, 0, 1, false, Tier::Base } },
{ Instruction::RETURNDATACOPY, {"RETURNDATACOPY", 0, 3, 0, true, Tier::VeryLow } },
{ Instruction::BLOCKHASH, { "BLOCKHASH", 0, 1, 1, false, Tier::Ext } },
{ Instruction::COINBASE, { "COINBASE", 0, 0, 1, false, Tier::Base } },
{ Instruction::TIMESTAMP, { "TIMESTAMP", 0, 0, 1, false, Tier::Base } },

View File

@ -77,6 +77,8 @@ enum class Instruction: uint8_t
GASPRICE, ///< get price of gas in current environment
EXTCODESIZE, ///< get external code size (from another contract)
EXTCODECOPY, ///< copy external code (from another contract)
RETURNDATASIZE, ///< get size of the last return data
RETURNDATACOPY, ///< copy last return data to memory
BLOCKHASH = 0x40, ///< get hash of most recent complete block
COINBASE, ///< get the block's coinbase address

View File

@ -143,6 +143,8 @@ bool SemanticInformation::isDeterministic(AssemblyItem const& _item)
case Instruction::MSIZE: // depends on previous writes and reads, not only on content
case Instruction::BALANCE: // depends on previous calls
case Instruction::EXTCODESIZE:
case Instruction::RETURNDATACOPY: // depends on previous calls
case Instruction::RETURNDATASIZE:
return false;
default:
return true;
@ -156,6 +158,7 @@ bool SemanticInformation::invalidatesMemory(Instruction _instruction)
case Instruction::CALLDATACOPY:
case Instruction::CODECOPY:
case Instruction::EXTCODECOPY:
case Instruction::RETURNDATACOPY:
case Instruction::MSTORE:
case Instruction::MSTORE8:
case Instruction::CALL:

View File

@ -26,6 +26,8 @@
#include <libsolidity/analysis/TypeChecker.h>
#include <libsolidity/interface/ErrorReporter.h>
#include <boost/algorithm/string.hpp>
using namespace std;
namespace dev
@ -232,6 +234,26 @@ vector<Declaration const*> NameAndTypeResolver::cleanedDeclarations(
return uniqueFunctions;
}
void NameAndTypeResolver::warnVariablesNamedLikeInstructions()
{
for (auto const& instruction: c_instructions)
{
string const instructionName{boost::algorithm::to_lower_copy(instruction.first)};
auto declarations = nameFromCurrentScope(instructionName);
for (Declaration const* const declaration: declarations)
{
solAssert(!!declaration, "");
if (dynamic_cast<MagicVariableDeclaration const* const>(declaration))
// Don't warn the user for what the user did not.
continue;
m_errorReporter.warning(
declaration->location(),
"Variable is shadowed in inline assembly by an instruction of the same name"
);
}
}
}
bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode)
{
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(&_node))

View File

@ -90,6 +90,9 @@ public:
std::vector<Declaration const*> const& _declarations
);
/// Generate and store warnings about variables that are named like instructions.
void warnVariablesNamedLikeInstructions();
private:
/// Internal version of @a resolveNamesAndTypes (called from there) throws exceptions on fatal errors.
bool resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode = true);

View File

@ -162,6 +162,8 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName)
bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
{
m_resolver.warnVariablesNamedLikeInstructions();
// Errors created in this stage are completely ignored because we do not yet know
// the type and size of external identifiers, which would result in false errors.
// The only purpose of this step is to fill the inline assembly annotation with

View File

@ -65,6 +65,7 @@ bool AsmAnalyzer::operator()(assembly::Instruction const& _instruction)
auto const& info = instructionInfo(_instruction.instruction);
m_stackHeight += info.ret - info.args;
m_info.stackHeightInfo[&_instruction] = m_stackHeight;
warnOnFutureInstruction(_instruction.instruction, _instruction.location);
return true;
}
@ -149,6 +150,7 @@ bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr)
if (!(*this)(_instr.instruction))
success = false;
m_info.stackHeightInfo[&_instr] = m_stackHeight;
warnOnFutureInstruction(_instr.instruction.instruction, _instr.location);
return success;
}
@ -431,7 +433,6 @@ Scope& AsmAnalyzer::scope(Block const* _block)
solAssert(scopePtr, "Scope requested but not present.");
return *scopePtr;
}
void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _location)
{
if (!m_julia)
@ -443,3 +444,20 @@ void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _loc
"\"" + type + "\" is not a valid type (user defined types are not yet supported)."
);
}
void AsmAnalyzer::warnOnFutureInstruction(solidity::Instruction _instr, SourceLocation const& _location)
{
switch (_instr)
{
case solidity::Instruction::RETURNDATASIZE:
case solidity::Instruction::RETURNDATACOPY:
m_errorReporter.warning(
_location,
"The RETURNDATASIZE/RETURNDATACOPY instructions are only available after "
"the Metropolis hard fork. Before that they act as an invalid instruction."
);
break;
default:
break;
}
}

View File

@ -97,6 +97,7 @@ private:
Scope& scope(assembly::Block const* _block);
void expectValidType(std::string const& type, SourceLocation const& _location);
void warnOnFutureInstruction(solidity::Instruction _instr, SourceLocation const& _location);
int m_stackHeight = 0;
julia::ExternalIdentifierAccess::Resolver m_resolver;

View File

@ -542,6 +542,26 @@ BOOST_AUTO_TEST_CASE(keccak256)
BOOST_CHECK(successAssemble("{ pop(sha3(0, 0)) }"));
}
BOOST_AUTO_TEST_CASE(returndatasize)
{
BOOST_CHECK(successAssemble("{ let r := returndatasize }"));
}
BOOST_AUTO_TEST_CASE(returndatasize_functional)
{
BOOST_CHECK(successAssemble("{ let r := returndatasize() }"));
}
BOOST_AUTO_TEST_CASE(returndatacopy)
{
BOOST_CHECK(successAssemble("{ 64 32 0 returndatacopy }"));
}
BOOST_AUTO_TEST_CASE(returndatacopy_functional)
{
BOOST_CHECK(successAssemble("{ returndatacopy(0, 32, 64) }"));
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()

View File

@ -193,11 +193,16 @@ CHECK_ERROR_OR_WARNING(text, type, substring, false, false)
#define CHECK_ERROR_ALLOW_MULTI(text, type, substring) \
CHECK_ERROR_OR_WARNING(text, type, substring, false, true)
// [checkWarning(text, type, substring)] asserts that the compilation down to typechecking
// emits a warning of type [type] and with a message containing [substring].
// [checkWarning(text, substring)] asserts that the compilation down to typechecking
// emits a warning and with a message containing [substring].
#define CHECK_WARNING(text, substring) \
CHECK_ERROR_OR_WARNING(text, Warning, substring, true, false)
// [checkWarningAllowMulti(text, substring)] aserts that the compilation down to typechecking
// emits a warning and with a message containing [substring].
#define CHECK_WARNING_ALLOW_MULTI(text, substring) \
CHECK_ERROR_OR_WARNING(text, Warning, substring, true, true)
// [checkSuccess(text)] asserts that the compilation down to typechecking succeeds.
#define CHECK_SUCCESS(text) do { BOOST_CHECK(success((text))); } while(0)
@ -5780,6 +5785,22 @@ BOOST_AUTO_TEST_CASE(no_unused_inline_asm)
CHECK_SUCCESS_NO_WARNINGS(text);
}
BOOST_AUTO_TEST_CASE(returndatacopy_as_variable)
{
char const* text = R"(
contract c { function f() { uint returndatasize; assembly { returndatasize }}}
)";
CHECK_WARNING_ALLOW_MULTI(text, "Variable is shadowed in inline assembly by an instruction of the same name");
}
BOOST_AUTO_TEST_CASE(shadowing_warning_can_be_removed)
{
char const* text = R"(
contract C {function f() {assembly {}}}
)";
CHECK_SUCCESS_NO_WARNINGS(text);
}
BOOST_AUTO_TEST_SUITE_END()