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)
|
### 0.4.12 (unreleased)
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
* Assembler: renamed ``SHA3`` to `KECCAK256``.
|
* Assembly: renamed ``SHA3`` to `KECCAK256``.
|
||||||
* AST: export all attributes to Json format
|
* 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
|
* Inline Assembly: Present proper error message when not supplying enough arguments to a functional
|
||||||
instruction.
|
instruction.
|
||||||
* Inline Assembly: introduce ``keccak256`` as an opcode. ``sha3`` is still a valid alias.
|
* Inline Assembly: introduce ``keccak256`` as an opcode. ``sha3`` is still a valid alias.
|
||||||
|
|
||||||
Bugfixes:
|
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)
|
### 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 |
|
| 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 |
|
| create(v, p, s) | | create new contract with code mem[p..(p+s)) and send v wei |
|
||||||
| | | and return the new address |
|
| | | and return the new address |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+------+-----------------------------------------------------------------+
|
||||||
|
@ -103,6 +103,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
|
|||||||
break;
|
break;
|
||||||
case Instruction::CALLDATACOPY:
|
case Instruction::CALLDATACOPY:
|
||||||
case Instruction::CODECOPY:
|
case Instruction::CODECOPY:
|
||||||
|
case Instruction::RETURNDATACOPY:
|
||||||
gas += memoryGas(0, -2);
|
gas += memoryGas(0, -2);
|
||||||
gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-2));
|
gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-2));
|
||||||
break;
|
break;
|
||||||
|
@ -67,6 +67,8 @@ const std::map<std::string, Instruction> dev::solidity::c_instructions =
|
|||||||
{ "GASPRICE", Instruction::GASPRICE },
|
{ "GASPRICE", Instruction::GASPRICE },
|
||||||
{ "EXTCODESIZE", Instruction::EXTCODESIZE },
|
{ "EXTCODESIZE", Instruction::EXTCODESIZE },
|
||||||
{ "EXTCODECOPY", Instruction::EXTCODECOPY },
|
{ "EXTCODECOPY", Instruction::EXTCODECOPY },
|
||||||
|
{ "RETURNDATASIZE", Instruction::RETURNDATASIZE },
|
||||||
|
{ "RETURNDATACOPY", Instruction::RETURNDATACOPY },
|
||||||
{ "BLOCKHASH", Instruction::BLOCKHASH },
|
{ "BLOCKHASH", Instruction::BLOCKHASH },
|
||||||
{ "COINBASE", Instruction::COINBASE },
|
{ "COINBASE", Instruction::COINBASE },
|
||||||
{ "TIMESTAMP", Instruction::TIMESTAMP },
|
{ "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::GASPRICE, { "GASPRICE", 0, 0, 1, false, Tier::Base } },
|
||||||
{ Instruction::EXTCODESIZE, { "EXTCODESIZE", 0, 1, 1, false, Tier::ExtCode } },
|
{ Instruction::EXTCODESIZE, { "EXTCODESIZE", 0, 1, 1, false, Tier::ExtCode } },
|
||||||
{ Instruction::EXTCODECOPY, { "EXTCODECOPY", 0, 4, 0, true, 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::BLOCKHASH, { "BLOCKHASH", 0, 1, 1, false, Tier::Ext } },
|
||||||
{ Instruction::COINBASE, { "COINBASE", 0, 0, 1, false, Tier::Base } },
|
{ Instruction::COINBASE, { "COINBASE", 0, 0, 1, false, Tier::Base } },
|
||||||
{ Instruction::TIMESTAMP, { "TIMESTAMP", 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
|
GASPRICE, ///< get price of gas in current environment
|
||||||
EXTCODESIZE, ///< get external code size (from another contract)
|
EXTCODESIZE, ///< get external code size (from another contract)
|
||||||
EXTCODECOPY, ///< copy external code (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
|
BLOCKHASH = 0x40, ///< get hash of most recent complete block
|
||||||
COINBASE, ///< get the block's coinbase address
|
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::MSIZE: // depends on previous writes and reads, not only on content
|
||||||
case Instruction::BALANCE: // depends on previous calls
|
case Instruction::BALANCE: // depends on previous calls
|
||||||
case Instruction::EXTCODESIZE:
|
case Instruction::EXTCODESIZE:
|
||||||
|
case Instruction::RETURNDATACOPY: // depends on previous calls
|
||||||
|
case Instruction::RETURNDATASIZE:
|
||||||
return false;
|
return false;
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
@ -156,6 +158,7 @@ bool SemanticInformation::invalidatesMemory(Instruction _instruction)
|
|||||||
case Instruction::CALLDATACOPY:
|
case Instruction::CALLDATACOPY:
|
||||||
case Instruction::CODECOPY:
|
case Instruction::CODECOPY:
|
||||||
case Instruction::EXTCODECOPY:
|
case Instruction::EXTCODECOPY:
|
||||||
|
case Instruction::RETURNDATACOPY:
|
||||||
case Instruction::MSTORE:
|
case Instruction::MSTORE:
|
||||||
case Instruction::MSTORE8:
|
case Instruction::MSTORE8:
|
||||||
case Instruction::CALL:
|
case Instruction::CALL:
|
||||||
|
@ -26,6 +26,8 @@
|
|||||||
#include <libsolidity/analysis/TypeChecker.h>
|
#include <libsolidity/analysis/TypeChecker.h>
|
||||||
#include <libsolidity/interface/ErrorReporter.h>
|
#include <libsolidity/interface/ErrorReporter.h>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string.hpp>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
@ -232,6 +234,26 @@ vector<Declaration const*> NameAndTypeResolver::cleanedDeclarations(
|
|||||||
return uniqueFunctions;
|
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)
|
bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode)
|
||||||
{
|
{
|
||||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(&_node))
|
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(&_node))
|
||||||
|
@ -90,6 +90,9 @@ public:
|
|||||||
std::vector<Declaration const*> const& _declarations
|
std::vector<Declaration const*> const& _declarations
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// Generate and store warnings about variables that are named like instructions.
|
||||||
|
void warnVariablesNamedLikeInstructions();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Internal version of @a resolveNamesAndTypes (called from there) throws exceptions on fatal errors.
|
/// Internal version of @a resolveNamesAndTypes (called from there) throws exceptions on fatal errors.
|
||||||
bool resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode = true);
|
bool resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode = true);
|
||||||
|
@ -162,6 +162,8 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName)
|
|||||||
|
|
||||||
bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
|
bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
|
||||||
{
|
{
|
||||||
|
m_resolver.warnVariablesNamedLikeInstructions();
|
||||||
|
|
||||||
// Errors created in this stage are completely ignored because we do not yet know
|
// 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 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
|
// 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);
|
auto const& info = instructionInfo(_instruction.instruction);
|
||||||
m_stackHeight += info.ret - info.args;
|
m_stackHeight += info.ret - info.args;
|
||||||
m_info.stackHeightInfo[&_instruction] = m_stackHeight;
|
m_info.stackHeightInfo[&_instruction] = m_stackHeight;
|
||||||
|
warnOnFutureInstruction(_instruction.instruction, _instruction.location);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,6 +150,7 @@ bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr)
|
|||||||
if (!(*this)(_instr.instruction))
|
if (!(*this)(_instr.instruction))
|
||||||
success = false;
|
success = false;
|
||||||
m_info.stackHeightInfo[&_instr] = m_stackHeight;
|
m_info.stackHeightInfo[&_instr] = m_stackHeight;
|
||||||
|
warnOnFutureInstruction(_instr.instruction.instruction, _instr.location);
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -431,7 +433,6 @@ Scope& AsmAnalyzer::scope(Block const* _block)
|
|||||||
solAssert(scopePtr, "Scope requested but not present.");
|
solAssert(scopePtr, "Scope requested but not present.");
|
||||||
return *scopePtr;
|
return *scopePtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _location)
|
void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _location)
|
||||||
{
|
{
|
||||||
if (!m_julia)
|
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)."
|
"\"" + 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);
|
Scope& scope(assembly::Block const* _block);
|
||||||
void expectValidType(std::string const& type, SourceLocation const& _location);
|
void expectValidType(std::string const& type, SourceLocation const& _location);
|
||||||
|
void warnOnFutureInstruction(solidity::Instruction _instr, SourceLocation const& _location);
|
||||||
|
|
||||||
int m_stackHeight = 0;
|
int m_stackHeight = 0;
|
||||||
julia::ExternalIdentifierAccess::Resolver m_resolver;
|
julia::ExternalIdentifierAccess::Resolver m_resolver;
|
||||||
|
@ -542,6 +542,26 @@ BOOST_AUTO_TEST_CASE(keccak256)
|
|||||||
BOOST_CHECK(successAssemble("{ pop(sha3(0, 0)) }"));
|
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()
|
||||||
|
|
||||||
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) \
|
#define CHECK_ERROR_ALLOW_MULTI(text, type, substring) \
|
||||||
CHECK_ERROR_OR_WARNING(text, type, substring, false, true)
|
CHECK_ERROR_OR_WARNING(text, type, substring, false, true)
|
||||||
|
|
||||||
// [checkWarning(text, type, substring)] asserts that the compilation down to typechecking
|
// [checkWarning(text, substring)] asserts that the compilation down to typechecking
|
||||||
// emits a warning of type [type] and with a message containing [substring].
|
// emits a warning and with a message containing [substring].
|
||||||
#define CHECK_WARNING(text, substring) \
|
#define CHECK_WARNING(text, substring) \
|
||||||
CHECK_ERROR_OR_WARNING(text, Warning, substring, true, false)
|
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.
|
// [checkSuccess(text)] asserts that the compilation down to typechecking succeeds.
|
||||||
#define CHECK_SUCCESS(text) do { BOOST_CHECK(success((text))); } while(0)
|
#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);
|
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()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
Loading…
Reference in New Issue
Block a user