diff options
-rw-r--r-- | Changelog.md | 7 | ||||
-rw-r--r-- | docs/assembly.rst | 4 | ||||
-rw-r--r-- | docs/contracts.rst | 3 | ||||
-rw-r--r-- | libevmasm/GasMeter.cpp | 1 | ||||
-rw-r--r-- | libevmasm/Instruction.cpp | 4 | ||||
-rw-r--r-- | libevmasm/Instruction.h | 2 | ||||
-rw-r--r-- | libevmasm/SemanticInformation.cpp | 3 | ||||
-rw-r--r-- | libsolidity/analysis/NameAndTypeResolver.cpp | 22 | ||||
-rw-r--r-- | libsolidity/analysis/NameAndTypeResolver.h | 3 | ||||
-rw-r--r-- | libsolidity/analysis/ReferencesResolver.cpp | 2 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmAnalysis.cpp | 20 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmAnalysis.h | 1 | ||||
-rw-r--r-- | test/libsolidity/InlineAssembly.cpp | 20 | ||||
-rw-r--r-- | test/libsolidity/SolidityNameAndTypeResolution.cpp | 25 |
14 files changed, 110 insertions, 7 deletions
diff --git a/Changelog.md b/Changelog.md index b398e014..8f1fe045 100644 --- a/Changelog.md +++ b/Changelog.md @@ -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) diff --git a/docs/assembly.rst b/docs/assembly.rst index 90e70031..cd3ff4c0 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -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 | +-------------------------+------+-----------------------------------------------------------------+ diff --git a/docs/contracts.rst b/docs/contracts.rst index a1192d4e..01913654 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -509,7 +509,8 @@ In particular, the following operations will consume more gas than the stipend p Please ensure you test your fallback function thoroughly to ensure the execution cost is less than 2300 gas before deploying a contract. .. warning:: - Contracts that receive Ether but do not define a fallback function + Contracts that receive Ether directly (without a function call, i.e. using ``send`` or ``transfer``) + but do not define a fallback function throw an exception, sending back the Ether (this was different before Solidity v0.4.0). So if you want your contract to receive Ether, you have to implement a fallback function. diff --git a/libevmasm/GasMeter.cpp b/libevmasm/GasMeter.cpp index 260b7439..31a7d13e 100644 --- a/libevmasm/GasMeter.cpp +++ b/libevmasm/GasMeter.cpp @@ -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; diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index 25eab60b..af7e9ff9 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -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 } }, diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index 09d1e58b..a8c3bf4a 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -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 diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index 61586e7b..86feb1d2 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -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: diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 2742dcf2..aac90311 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -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)) diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index 0441867d..84628778 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -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); diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index edf2fc02..2a5f27df 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -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 diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 13852880..36ac0e75 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -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; + } +} diff --git a/libsolidity/inlineasm/AsmAnalysis.h b/libsolidity/inlineasm/AsmAnalysis.h index e7748bcf..55b409ba 100644 --- a/libsolidity/inlineasm/AsmAnalysis.h +++ b/libsolidity/inlineasm/AsmAnalysis.h @@ -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; diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index d2d320db..435c3dad 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -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() diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index db5b9bf8..ba2ade66 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -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() |