mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #2275 from ethereum/returndata_lll
Add RETURNDATACOPY and RETURNDATASIZE to assembly (and LLL)
This commit is contained in:
commit
6b052249da
@ -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)
|
||||
|
||||
|
@ -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 |
|
||||
+-------------------------+------+-----------------------------------------------------------------+
|
||||
|
@ -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;
|
||||
|
@ -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 } },
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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))
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user