From 4713dd625df08fe6a5fe9cd29febcc225951c36d Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 23 Jan 2017 15:46:03 +0100 Subject: [PATCH 001/118] Fix error tag usage in lll. --- libevmasm/Assembly.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 845abfd4e..0247593bf 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -40,7 +40,7 @@ void Assembly::append(Assembly const& _a) auto newDeposit = m_deposit + _a.deposit(); for (AssemblyItem i: _a.m_items) { - if (i.type() == Tag || i.type() == PushTag) + if (i.type() == Tag || (i.type() == PushTag && i != errorTag())) i.setData(i.data() + m_usedTags); else if (i.type() == PushSub || i.type() == PushSubSize) i.setData(i.data() + m_subs.size()); From 0cb95ac35e4ae2ce1b9f929c00a2aed282bd4a70 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 12 Jan 2017 14:52:01 +0000 Subject: [PATCH 002/118] LLL: add test for (panic) in a sequence --- test/liblll/EndToEndTest.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/liblll/EndToEndTest.cpp b/test/liblll/EndToEndTest.cpp index 77c1f7409..c7c1fd3b4 100644 --- a/test/liblll/EndToEndTest.cpp +++ b/test/liblll/EndToEndTest.cpp @@ -50,6 +50,13 @@ BOOST_AUTO_TEST_CASE(bare_panic) BOOST_REQUIRE(m_output.empty()); } +BOOST_AUTO_TEST_CASE(panic) +{ + char const* sourceCode = "{ (panic) }"; + compileAndRunWithoutCheck(sourceCode); + BOOST_REQUIRE(m_output.empty()); +} + BOOST_AUTO_TEST_CASE(exp_operator_const) { char const* sourceCode = R"( From 20c62a132da4e32efbde7df3bb1dbdc4f448243f Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Fri, 13 Jan 2017 13:19:14 +0100 Subject: [PATCH 003/118] Make enum Tier into an enum class --- libevmasm/GasMeter.cpp | 16 +-- libevmasm/Instruction.cpp | 262 +++++++++++++++++++------------------- libevmasm/Instruction.h | 4 +- 3 files changed, 141 insertions(+), 141 deletions(-) diff --git a/libevmasm/GasMeter.cpp b/libevmasm/GasMeter.cpp index 21db35655..bc7d4e55e 100644 --- a/libevmasm/GasMeter.cpp +++ b/libevmasm/GasMeter.cpp @@ -223,14 +223,14 @@ unsigned GasMeter::runGas(Instruction _instruction) switch (instructionInfo(_instruction).gasPriceTier) { - case 0: return GasCosts::tier0Gas; - case 1: return GasCosts::tier1Gas; - case 2: return GasCosts::tier2Gas; - case 3: return GasCosts::tier3Gas; - case 4: return GasCosts::tier4Gas; - case 5: return GasCosts::tier5Gas; - case 6: return GasCosts::tier6Gas; - case 7: return GasCosts::tier7Gas; + case Tier::ZeroTier: return GasCosts::tier0Gas; + case Tier::BaseTier: return GasCosts::tier1Gas; + case Tier::VeryLowTier: return GasCosts::tier2Gas; + case Tier::LowTier: return GasCosts::tier3Gas; + case Tier::MidTier: return GasCosts::tier4Gas; + case Tier::HighTier: return GasCosts::tier5Gas; + case Tier::ExtTier: return GasCosts::tier6Gas; + case Tier::SpecialTier: return GasCosts::tier7Gas; default: break; } assertThrow(false, OptimizerException, "Invalid gas tier."); diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index 5244a91f7..43cbbd380 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -164,136 +164,136 @@ const std::map dev::solidity::c_instructions = static const std::map c_instructionInfo = { // Add, Args, Ret, SideEffects, GasPriceTier - { Instruction::STOP, { "STOP", 0, 0, 0, true, ZeroTier } }, - { Instruction::ADD, { "ADD", 0, 2, 1, false, VeryLowTier } }, - { Instruction::SUB, { "SUB", 0, 2, 1, false, VeryLowTier } }, - { Instruction::MUL, { "MUL", 0, 2, 1, false, LowTier } }, - { Instruction::DIV, { "DIV", 0, 2, 1, false, LowTier } }, - { Instruction::SDIV, { "SDIV", 0, 2, 1, false, LowTier } }, - { Instruction::MOD, { "MOD", 0, 2, 1, false, LowTier } }, - { Instruction::SMOD, { "SMOD", 0, 2, 1, false, LowTier } }, - { Instruction::EXP, { "EXP", 0, 2, 1, false, SpecialTier } }, - { Instruction::NOT, { "NOT", 0, 1, 1, false, VeryLowTier } }, - { Instruction::LT, { "LT", 0, 2, 1, false, VeryLowTier } }, - { Instruction::GT, { "GT", 0, 2, 1, false, VeryLowTier } }, - { Instruction::SLT, { "SLT", 0, 2, 1, false, VeryLowTier } }, - { Instruction::SGT, { "SGT", 0, 2, 1, false, VeryLowTier } }, - { Instruction::EQ, { "EQ", 0, 2, 1, false, VeryLowTier } }, - { Instruction::ISZERO, { "ISZERO", 0, 1, 1, false, VeryLowTier } }, - { Instruction::AND, { "AND", 0, 2, 1, false, VeryLowTier } }, - { Instruction::OR, { "OR", 0, 2, 1, false, VeryLowTier } }, - { Instruction::XOR, { "XOR", 0, 2, 1, false, VeryLowTier } }, - { Instruction::BYTE, { "BYTE", 0, 2, 1, false, VeryLowTier } }, - { Instruction::ADDMOD, { "ADDMOD", 0, 3, 1, false, MidTier } }, - { Instruction::MULMOD, { "MULMOD", 0, 3, 1, false, MidTier } }, - { Instruction::SIGNEXTEND, { "SIGNEXTEND", 0, 2, 1, false, LowTier } }, - { Instruction::SHA3, { "SHA3", 0, 2, 1, false, SpecialTier } }, - { Instruction::ADDRESS, { "ADDRESS", 0, 0, 1, false, BaseTier } }, - { Instruction::BALANCE, { "BALANCE", 0, 1, 1, false, ExtTier } }, - { Instruction::ORIGIN, { "ORIGIN", 0, 0, 1, false, BaseTier } }, - { Instruction::CALLER, { "CALLER", 0, 0, 1, false, BaseTier } }, - { Instruction::CALLVALUE, { "CALLVALUE", 0, 0, 1, false, BaseTier } }, - { Instruction::CALLDATALOAD,{ "CALLDATALOAD", 0, 1, 1, false, VeryLowTier } }, - { Instruction::CALLDATASIZE,{ "CALLDATASIZE", 0, 0, 1, false, BaseTier } }, - { Instruction::CALLDATACOPY,{ "CALLDATACOPY", 0, 3, 0, true, VeryLowTier } }, - { Instruction::CODESIZE, { "CODESIZE", 0, 0, 1, false, BaseTier } }, - { Instruction::CODECOPY, { "CODECOPY", 0, 3, 0, true, VeryLowTier } }, - { Instruction::GASPRICE, { "GASPRICE", 0, 0, 1, false, BaseTier } }, - { Instruction::EXTCODESIZE, { "EXTCODESIZE", 0, 1, 1, false, ExtTier } }, - { Instruction::EXTCODECOPY, { "EXTCODECOPY", 0, 4, 0, true, ExtTier } }, - { Instruction::BLOCKHASH, { "BLOCKHASH", 0, 1, 1, false, ExtTier } }, - { Instruction::COINBASE, { "COINBASE", 0, 0, 1, false, BaseTier } }, - { Instruction::TIMESTAMP, { "TIMESTAMP", 0, 0, 1, false, BaseTier } }, - { Instruction::NUMBER, { "NUMBER", 0, 0, 1, false, BaseTier } }, - { Instruction::DIFFICULTY, { "DIFFICULTY", 0, 0, 1, false, BaseTier } }, - { Instruction::GASLIMIT, { "GASLIMIT", 0, 0, 1, false, BaseTier } }, - { Instruction::POP, { "POP", 0, 1, 0, false, BaseTier } }, - { Instruction::MLOAD, { "MLOAD", 0, 1, 1, false, VeryLowTier } }, - { Instruction::MSTORE, { "MSTORE", 0, 2, 0, true, VeryLowTier } }, - { Instruction::MSTORE8, { "MSTORE8", 0, 2, 0, true, VeryLowTier } }, - { Instruction::SLOAD, { "SLOAD", 0, 1, 1, false, SpecialTier } }, - { Instruction::SSTORE, { "SSTORE", 0, 2, 0, true, SpecialTier } }, - { Instruction::JUMP, { "JUMP", 0, 1, 0, true, MidTier } }, - { Instruction::JUMPI, { "JUMPI", 0, 2, 0, true, HighTier } }, - { Instruction::PC, { "PC", 0, 0, 1, false, BaseTier } }, - { Instruction::MSIZE, { "MSIZE", 0, 0, 1, false, BaseTier } }, - { Instruction::GAS, { "GAS", 0, 0, 1, false, BaseTier } }, - { Instruction::JUMPDEST, { "JUMPDEST", 0, 0, 0, true, SpecialTier } }, - { Instruction::PUSH1, { "PUSH1", 1, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH2, { "PUSH2", 2, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH3, { "PUSH3", 3, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH4, { "PUSH4", 4, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH5, { "PUSH5", 5, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH6, { "PUSH6", 6, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH7, { "PUSH7", 7, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH8, { "PUSH8", 8, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH9, { "PUSH9", 9, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH10, { "PUSH10", 10, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH11, { "PUSH11", 11, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH12, { "PUSH12", 12, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH13, { "PUSH13", 13, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH14, { "PUSH14", 14, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH15, { "PUSH15", 15, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH16, { "PUSH16", 16, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH17, { "PUSH17", 17, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH18, { "PUSH18", 18, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH19, { "PUSH19", 19, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH20, { "PUSH20", 20, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH21, { "PUSH21", 21, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH22, { "PUSH22", 22, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH23, { "PUSH23", 23, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH24, { "PUSH24", 24, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH25, { "PUSH25", 25, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH26, { "PUSH26", 26, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH27, { "PUSH27", 27, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH28, { "PUSH28", 28, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH29, { "PUSH29", 29, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH30, { "PUSH30", 30, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH31, { "PUSH31", 31, 0, 1, false, VeryLowTier } }, - { Instruction::PUSH32, { "PUSH32", 32, 0, 1, false, VeryLowTier } }, - { Instruction::DUP1, { "DUP1", 0, 1, 2, false, VeryLowTier } }, - { Instruction::DUP2, { "DUP2", 0, 2, 3, false, VeryLowTier } }, - { Instruction::DUP3, { "DUP3", 0, 3, 4, false, VeryLowTier } }, - { Instruction::DUP4, { "DUP4", 0, 4, 5, false, VeryLowTier } }, - { Instruction::DUP5, { "DUP5", 0, 5, 6, false, VeryLowTier } }, - { Instruction::DUP6, { "DUP6", 0, 6, 7, false, VeryLowTier } }, - { Instruction::DUP7, { "DUP7", 0, 7, 8, false, VeryLowTier } }, - { Instruction::DUP8, { "DUP8", 0, 8, 9, false, VeryLowTier } }, - { Instruction::DUP9, { "DUP9", 0, 9, 10, false, VeryLowTier } }, - { Instruction::DUP10, { "DUP10", 0, 10, 11, false, VeryLowTier } }, - { Instruction::DUP11, { "DUP11", 0, 11, 12, false, VeryLowTier } }, - { Instruction::DUP12, { "DUP12", 0, 12, 13, false, VeryLowTier } }, - { Instruction::DUP13, { "DUP13", 0, 13, 14, false, VeryLowTier } }, - { Instruction::DUP14, { "DUP14", 0, 14, 15, false, VeryLowTier } }, - { Instruction::DUP15, { "DUP15", 0, 15, 16, false, VeryLowTier } }, - { Instruction::DUP16, { "DUP16", 0, 16, 17, false, VeryLowTier } }, - { Instruction::SWAP1, { "SWAP1", 0, 2, 2, false, VeryLowTier } }, - { Instruction::SWAP2, { "SWAP2", 0, 3, 3, false, VeryLowTier } }, - { Instruction::SWAP3, { "SWAP3", 0, 4, 4, false, VeryLowTier } }, - { Instruction::SWAP4, { "SWAP4", 0, 5, 5, false, VeryLowTier } }, - { Instruction::SWAP5, { "SWAP5", 0, 6, 6, false, VeryLowTier } }, - { Instruction::SWAP6, { "SWAP6", 0, 7, 7, false, VeryLowTier } }, - { Instruction::SWAP7, { "SWAP7", 0, 8, 8, false, VeryLowTier } }, - { Instruction::SWAP8, { "SWAP8", 0, 9, 9, false, VeryLowTier } }, - { Instruction::SWAP9, { "SWAP9", 0, 10, 10, false, VeryLowTier } }, - { Instruction::SWAP10, { "SWAP10", 0, 11, 11, false, VeryLowTier } }, - { Instruction::SWAP11, { "SWAP11", 0, 12, 12, false, VeryLowTier } }, - { Instruction::SWAP12, { "SWAP12", 0, 13, 13, false, VeryLowTier } }, - { Instruction::SWAP13, { "SWAP13", 0, 14, 14, false, VeryLowTier } }, - { Instruction::SWAP14, { "SWAP14", 0, 15, 15, false, VeryLowTier } }, - { Instruction::SWAP15, { "SWAP15", 0, 16, 16, false, VeryLowTier } }, - { Instruction::SWAP16, { "SWAP16", 0, 17, 17, false, VeryLowTier } }, - { Instruction::LOG0, { "LOG0", 0, 2, 0, true, SpecialTier } }, - { Instruction::LOG1, { "LOG1", 0, 3, 0, true, SpecialTier } }, - { Instruction::LOG2, { "LOG2", 0, 4, 0, true, SpecialTier } }, - { Instruction::LOG3, { "LOG3", 0, 5, 0, true, SpecialTier } }, - { Instruction::LOG4, { "LOG4", 0, 6, 0, true, SpecialTier } }, - { Instruction::CREATE, { "CREATE", 0, 3, 1, true, SpecialTier } }, - { Instruction::CALL, { "CALL", 0, 7, 1, true, SpecialTier } }, - { Instruction::CALLCODE, { "CALLCODE", 0, 7, 1, true, SpecialTier } }, - { Instruction::RETURN, { "RETURN", 0, 2, 0, true, ZeroTier } }, - { Instruction::DELEGATECALL,{ "DELEGATECALL", 0, 6, 1, true, SpecialTier } }, - { Instruction::SUICIDE, { "SUICIDE", 0, 1, 0, true, ZeroTier } } + { Instruction::STOP, { "STOP", 0, 0, 0, true, Tier::ZeroTier } }, + { Instruction::ADD, { "ADD", 0, 2, 1, false, Tier::VeryLowTier } }, + { Instruction::SUB, { "SUB", 0, 2, 1, false, Tier::VeryLowTier } }, + { Instruction::MUL, { "MUL", 0, 2, 1, false, Tier::LowTier } }, + { Instruction::DIV, { "DIV", 0, 2, 1, false, Tier::LowTier } }, + { Instruction::SDIV, { "SDIV", 0, 2, 1, false, Tier::LowTier } }, + { Instruction::MOD, { "MOD", 0, 2, 1, false, Tier::LowTier } }, + { Instruction::SMOD, { "SMOD", 0, 2, 1, false, Tier::LowTier } }, + { Instruction::EXP, { "EXP", 0, 2, 1, false, Tier::SpecialTier } }, + { Instruction::NOT, { "NOT", 0, 1, 1, false, Tier::VeryLowTier } }, + { Instruction::LT, { "LT", 0, 2, 1, false, Tier::VeryLowTier } }, + { Instruction::GT, { "GT", 0, 2, 1, false, Tier::VeryLowTier } }, + { Instruction::SLT, { "SLT", 0, 2, 1, false, Tier::VeryLowTier } }, + { Instruction::SGT, { "SGT", 0, 2, 1, false, Tier::VeryLowTier } }, + { Instruction::EQ, { "EQ", 0, 2, 1, false, Tier::VeryLowTier } }, + { Instruction::ISZERO, { "ISZERO", 0, 1, 1, false, Tier::VeryLowTier } }, + { Instruction::AND, { "AND", 0, 2, 1, false, Tier::VeryLowTier } }, + { Instruction::OR, { "OR", 0, 2, 1, false, Tier::VeryLowTier } }, + { Instruction::XOR, { "XOR", 0, 2, 1, false, Tier::VeryLowTier } }, + { Instruction::BYTE, { "BYTE", 0, 2, 1, false, Tier::VeryLowTier } }, + { Instruction::ADDMOD, { "ADDMOD", 0, 3, 1, false, Tier::MidTier } }, + { Instruction::MULMOD, { "MULMOD", 0, 3, 1, false, Tier::MidTier } }, + { Instruction::SIGNEXTEND, { "SIGNEXTEND", 0, 2, 1, false, Tier::LowTier } }, + { Instruction::SHA3, { "SHA3", 0, 2, 1, false, Tier::SpecialTier } }, + { Instruction::ADDRESS, { "ADDRESS", 0, 0, 1, false, Tier::BaseTier } }, + { Instruction::BALANCE, { "BALANCE", 0, 1, 1, false, Tier::ExtTier } }, + { Instruction::ORIGIN, { "ORIGIN", 0, 0, 1, false, Tier::BaseTier } }, + { Instruction::CALLER, { "CALLER", 0, 0, 1, false, Tier::BaseTier } }, + { Instruction::CALLVALUE, { "CALLVALUE", 0, 0, 1, false, Tier::BaseTier } }, + { Instruction::CALLDATALOAD,{ "CALLDATALOAD", 0, 1, 1, false, Tier::VeryLowTier } }, + { Instruction::CALLDATASIZE,{ "CALLDATASIZE", 0, 0, 1, false, Tier::BaseTier } }, + { Instruction::CALLDATACOPY,{ "CALLDATACOPY", 0, 3, 0, true, Tier::VeryLowTier } }, + { Instruction::CODESIZE, { "CODESIZE", 0, 0, 1, false, Tier::BaseTier } }, + { Instruction::CODECOPY, { "CODECOPY", 0, 3, 0, true, Tier::VeryLowTier } }, + { Instruction::GASPRICE, { "GASPRICE", 0, 0, 1, false, Tier::BaseTier } }, + { Instruction::EXTCODESIZE, { "EXTCODESIZE", 0, 1, 1, false, Tier::ExtTier } }, + { Instruction::EXTCODECOPY, { "EXTCODECOPY", 0, 4, 0, true, Tier::ExtTier } }, + { Instruction::BLOCKHASH, { "BLOCKHASH", 0, 1, 1, false, Tier::ExtTier } }, + { Instruction::COINBASE, { "COINBASE", 0, 0, 1, false, Tier::BaseTier } }, + { Instruction::TIMESTAMP, { "TIMESTAMP", 0, 0, 1, false, Tier::BaseTier } }, + { Instruction::NUMBER, { "NUMBER", 0, 0, 1, false, Tier::BaseTier } }, + { Instruction::DIFFICULTY, { "DIFFICULTY", 0, 0, 1, false, Tier::BaseTier } }, + { Instruction::GASLIMIT, { "GASLIMIT", 0, 0, 1, false, Tier::BaseTier } }, + { Instruction::POP, { "POP", 0, 1, 0, false, Tier::BaseTier } }, + { Instruction::MLOAD, { "MLOAD", 0, 1, 1, false, Tier::VeryLowTier } }, + { Instruction::MSTORE, { "MSTORE", 0, 2, 0, true, Tier::VeryLowTier } }, + { Instruction::MSTORE8, { "MSTORE8", 0, 2, 0, true, Tier::VeryLowTier } }, + { Instruction::SLOAD, { "SLOAD", 0, 1, 1, false, Tier::SpecialTier } }, + { Instruction::SSTORE, { "SSTORE", 0, 2, 0, true, Tier::SpecialTier } }, + { Instruction::JUMP, { "JUMP", 0, 1, 0, true, Tier::MidTier } }, + { Instruction::JUMPI, { "JUMPI", 0, 2, 0, true, Tier::HighTier } }, + { Instruction::PC, { "PC", 0, 0, 1, false, Tier::BaseTier } }, + { Instruction::MSIZE, { "MSIZE", 0, 0, 1, false, Tier::BaseTier } }, + { Instruction::GAS, { "GAS", 0, 0, 1, false, Tier::BaseTier } }, + { Instruction::JUMPDEST, { "JUMPDEST", 0, 0, 0, true, Tier::SpecialTier } }, + { Instruction::PUSH1, { "PUSH1", 1, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH2, { "PUSH2", 2, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH3, { "PUSH3", 3, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH4, { "PUSH4", 4, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH5, { "PUSH5", 5, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH6, { "PUSH6", 6, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH7, { "PUSH7", 7, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH8, { "PUSH8", 8, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH9, { "PUSH9", 9, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH10, { "PUSH10", 10, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH11, { "PUSH11", 11, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH12, { "PUSH12", 12, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH13, { "PUSH13", 13, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH14, { "PUSH14", 14, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH15, { "PUSH15", 15, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH16, { "PUSH16", 16, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH17, { "PUSH17", 17, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH18, { "PUSH18", 18, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH19, { "PUSH19", 19, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH20, { "PUSH20", 20, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH21, { "PUSH21", 21, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH22, { "PUSH22", 22, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH23, { "PUSH23", 23, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH24, { "PUSH24", 24, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH25, { "PUSH25", 25, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH26, { "PUSH26", 26, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH27, { "PUSH27", 27, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH28, { "PUSH28", 28, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH29, { "PUSH29", 29, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH30, { "PUSH30", 30, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH31, { "PUSH31", 31, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::PUSH32, { "PUSH32", 32, 0, 1, false, Tier::VeryLowTier } }, + { Instruction::DUP1, { "DUP1", 0, 1, 2, false, Tier::VeryLowTier } }, + { Instruction::DUP2, { "DUP2", 0, 2, 3, false, Tier::VeryLowTier } }, + { Instruction::DUP3, { "DUP3", 0, 3, 4, false, Tier::VeryLowTier } }, + { Instruction::DUP4, { "DUP4", 0, 4, 5, false, Tier::VeryLowTier } }, + { Instruction::DUP5, { "DUP5", 0, 5, 6, false, Tier::VeryLowTier } }, + { Instruction::DUP6, { "DUP6", 0, 6, 7, false, Tier::VeryLowTier } }, + { Instruction::DUP7, { "DUP7", 0, 7, 8, false, Tier::VeryLowTier } }, + { Instruction::DUP8, { "DUP8", 0, 8, 9, false, Tier::VeryLowTier } }, + { Instruction::DUP9, { "DUP9", 0, 9, 10, false, Tier::VeryLowTier } }, + { Instruction::DUP10, { "DUP10", 0, 10, 11, false, Tier::VeryLowTier } }, + { Instruction::DUP11, { "DUP11", 0, 11, 12, false, Tier::VeryLowTier } }, + { Instruction::DUP12, { "DUP12", 0, 12, 13, false, Tier::VeryLowTier } }, + { Instruction::DUP13, { "DUP13", 0, 13, 14, false, Tier::VeryLowTier } }, + { Instruction::DUP14, { "DUP14", 0, 14, 15, false, Tier::VeryLowTier } }, + { Instruction::DUP15, { "DUP15", 0, 15, 16, false, Tier::VeryLowTier } }, + { Instruction::DUP16, { "DUP16", 0, 16, 17, false, Tier::VeryLowTier } }, + { Instruction::SWAP1, { "SWAP1", 0, 2, 2, false, Tier::VeryLowTier } }, + { Instruction::SWAP2, { "SWAP2", 0, 3, 3, false, Tier::VeryLowTier } }, + { Instruction::SWAP3, { "SWAP3", 0, 4, 4, false, Tier::VeryLowTier } }, + { Instruction::SWAP4, { "SWAP4", 0, 5, 5, false, Tier::VeryLowTier } }, + { Instruction::SWAP5, { "SWAP5", 0, 6, 6, false, Tier::VeryLowTier } }, + { Instruction::SWAP6, { "SWAP6", 0, 7, 7, false, Tier::VeryLowTier } }, + { Instruction::SWAP7, { "SWAP7", 0, 8, 8, false, Tier::VeryLowTier } }, + { Instruction::SWAP8, { "SWAP8", 0, 9, 9, false, Tier::VeryLowTier } }, + { Instruction::SWAP9, { "SWAP9", 0, 10, 10, false, Tier::VeryLowTier } }, + { Instruction::SWAP10, { "SWAP10", 0, 11, 11, false, Tier::VeryLowTier } }, + { Instruction::SWAP11, { "SWAP11", 0, 12, 12, false, Tier::VeryLowTier } }, + { Instruction::SWAP12, { "SWAP12", 0, 13, 13, false, Tier::VeryLowTier } }, + { Instruction::SWAP13, { "SWAP13", 0, 14, 14, false, Tier::VeryLowTier } }, + { Instruction::SWAP14, { "SWAP14", 0, 15, 15, false, Tier::VeryLowTier } }, + { Instruction::SWAP15, { "SWAP15", 0, 16, 16, false, Tier::VeryLowTier } }, + { Instruction::SWAP16, { "SWAP16", 0, 17, 17, false, Tier::VeryLowTier } }, + { Instruction::LOG0, { "LOG0", 0, 2, 0, true, Tier::SpecialTier } }, + { Instruction::LOG1, { "LOG1", 0, 3, 0, true, Tier::SpecialTier } }, + { Instruction::LOG2, { "LOG2", 0, 4, 0, true, Tier::SpecialTier } }, + { Instruction::LOG3, { "LOG3", 0, 5, 0, true, Tier::SpecialTier } }, + { Instruction::LOG4, { "LOG4", 0, 6, 0, true, Tier::SpecialTier } }, + { Instruction::CREATE, { "CREATE", 0, 3, 1, true, Tier::SpecialTier } }, + { Instruction::CALL, { "CALL", 0, 7, 1, true, Tier::SpecialTier } }, + { Instruction::CALLCODE, { "CALLCODE", 0, 7, 1, true, Tier::SpecialTier } }, + { Instruction::RETURN, { "RETURN", 0, 2, 0, true, Tier::ZeroTier } }, + { Instruction::DELEGATECALL,{ "DELEGATECALL", 0, 6, 1, true, Tier::SpecialTier } }, + { Instruction::SUICIDE, { "SUICIDE", 0, 1, 0, true, Tier::ZeroTier } } }; void dev::solidity::eachInstruction( @@ -343,7 +343,7 @@ InstructionInfo dev::solidity::instructionInfo(Instruction _inst) } catch (...) { - return InstructionInfo({"", 0, 0, 0, false, InvalidTier}); + return InstructionInfo({"", 0, 0, 0, false, Tier::InvalidTier}); } } diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index c7fad1c5a..4ebaeb987 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -225,7 +225,7 @@ inline Instruction logInstruction(unsigned _number) return Instruction(unsigned(Instruction::LOG0) + _number); } -enum Tier +enum class Tier : int { ZeroTier = 0, // 0, Zero BaseTier, // 2, Quick @@ -246,7 +246,7 @@ struct InstructionInfo int args; ///< Number of items required on the stack for this instruction (and, for the purposes of ret, the number taken from the stack). int ret; ///< Number of items placed (back) on the stack by this instruction, assuming args items were removed. bool sideEffects; ///< false if the only effect on the execution environment (apart from gas usage) is a change to a topmost segment of the stack - int gasPriceTier; ///< Tier for gas pricing. + Tier gasPriceTier; ///< Tier for gas pricing. }; /// Information on all the instructions. From 10c2df8b33115eb1806cd3796c20c295e8e6f38d Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 13 Jan 2017 13:56:55 +0100 Subject: [PATCH 004/118] Update version to 0.4.9. --- CMakeLists.txt | 2 +- Changelog.md | 2 ++ docs/conf.py | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ecd857adf..68bb71f4a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.4.8") +set(PROJECT_VERSION "0.4.9") project(solidity VERSION ${PROJECT_VERSION}) # Let's find our dependencies diff --git a/Changelog.md b/Changelog.md index b8effc19f..a5c4b922f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,5 @@ +### 0.4.9 (unreleased) + ### 0.4.8 (2017-01-13) Features: diff --git a/docs/conf.py b/docs/conf.py index ecabbb861..85fc36776 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -56,9 +56,9 @@ copyright = '2016, Ethereum' # built documents. # # The short X.Y version. -version = '0.4.8' +version = '0.4.9' # The full version, including alpha/beta/rc tags. -release = '0.4.8-develop' +release = '0.4.9-develop' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. From fb5583857a85b91614c761c6d91eafc2627177ba Mon Sep 17 00:00:00 2001 From: Nicola Date: Fri, 13 Jan 2017 15:40:46 +0100 Subject: [PATCH 005/118] Update SolidityLexer.py 'payable' added as a keyword --- docs/utils/SolidityLexer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/utils/SolidityLexer.py b/docs/utils/SolidityLexer.py index 779147f48..b536d1a49 100644 --- a/docs/utils/SolidityLexer.py +++ b/docs/utils/SolidityLexer.py @@ -57,7 +57,7 @@ class SolidityLexer(RegexLexer): (r'(for|in|while|do|break|return|continue|switch|case|default|if|else|' r'throw|try|catch|finally|new|delete|typeof|instanceof|void|' r'this|import|mapping|returns|private|public|external|internal|' - r'constant|memory|storage)\b', Keyword, 'slashstartsregex'), + r'constant|memory|storage|payable)\b', Keyword, 'slashstartsregex'), (r'(var|let|with|function|event|modifier|struct|enum|contract|library)\b', Keyword.Declaration, 'slashstartsregex'), (r'(bytes|string|address|uint|int|bool|byte|' + '|'.join( From 79e5772b8a18bc4a7a1fd1019604cec6f741b743 Mon Sep 17 00:00:00 2001 From: Nicola Date: Mon, 16 Jan 2017 10:26:09 +0100 Subject: [PATCH 006/118] Update SolidityLexer.py (#1567) Added number unit keywords --- docs/utils/SolidityLexer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/utils/SolidityLexer.py b/docs/utils/SolidityLexer.py index b536d1a49..a7da59aaf 100644 --- a/docs/utils/SolidityLexer.py +++ b/docs/utils/SolidityLexer.py @@ -67,6 +67,7 @@ class SolidityLexer(RegexLexer): ['ufixed%dx%d' % ((i), (j + 8)) for i in range(0, 256, 8) for j in range(0, 256 - i, 8)] + ['fixed%dx%d' % ((i), (j + 8)) for i in range(0, 256, 8) for j in range(0, 256 - i, 8)] ) + r')\b', Keyword.Type, 'slashstartsregex'), + (r'(wei|szabo|finney|ether|seconds|minutes|hours|days|weeks|years)\b', Keyword.Type, 'slashstartsregex'), (r'(abstract|boolean|byte|char|class|const|debugger|double|enum|export|' r'extends|final|float|goto|implements|int|interface|long|native|' r'package|private|protected|public|short|static|super|synchronized|throws|' From f3a84eab91a6f05cfe58e260f206f9af51811313 Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Fri, 18 Nov 2016 07:37:15 -0800 Subject: [PATCH 007/118] Error out when contracts collide on name The previous behaviour, courtesy of the [] operator in std::map, would uncritically store a new ContractDefinition in m_contracts even when a ContractDefinition already existed. This "resolved" collissions on contract names by clobbering the original one with the new one, and could lead to scenarios where the clobber would only be discovered when the original ContractDefinition could not be found or referred to, which was an unhelpful InternalCompilerError. This change checks the m_contracts map for a collision first and will not let the ContractDefinition be changed to a new one once it's set, throwing a CompilerError with information about the conflict. --- libsolidity/interface/CompilerStack.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index a31df5846..5d4ccd92b 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -180,6 +180,15 @@ bool CompilerStack::parse() if (!resolver.updateDeclaration(*m_globalContext->currentThis())) return false; if (!resolver.updateDeclaration(*m_globalContext->currentSuper())) return false; if (!resolver.resolveNamesAndTypes(*contract)) return false; + if (m_contracts.find(contract->name()) != m_contracts.end()) + { + const ContractDefinition* existingContract = m_contracts.find(contract->name())->second.contract; + if (contract != existingContract) + BOOST_THROW_EXCEPTION(CompilerError() << + errinfo_sourceLocation(contract->location()) << + errinfo_comment(contract->name() + " conflicts with contract at " + + *(existingContract->location().sourceName))); + } m_contracts[contract->name()].contract = contract; } @@ -201,6 +210,16 @@ bool CompilerStack::parse() else noErrors = false; + if (m_contracts.find(contract->name()) != m_contracts.end()) + { + const ContractDefinition* existingContract = m_contracts.find(contract->name())->second.contract; + if (contract != existingContract) + BOOST_THROW_EXCEPTION(CompilerError() << + errinfo_sourceLocation(contract->location()) << + errinfo_comment(contract->name() + " conflicts with!!! contract at " + + *(existingContract->location().sourceName))); + } + m_contracts[contract->name()].contract = contract; } From b24ca4fa236ccc89600d30206172baf3eee386a7 Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Fri, 18 Nov 2016 19:29:08 -0800 Subject: [PATCH 008/118] Fix tab, drop stupid '!!!', change error message. --- libsolidity/interface/CompilerStack.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 5d4ccd92b..dded81f5f 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -183,10 +183,10 @@ bool CompilerStack::parse() if (m_contracts.find(contract->name()) != m_contracts.end()) { const ContractDefinition* existingContract = m_contracts.find(contract->name())->second.contract; - if (contract != existingContract) + if (contract != existingContract) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(contract->location()) << - errinfo_comment(contract->name() + " conflicts with contract at " + errinfo_comment(contract->name() + " is already defined at " + *(existingContract->location().sourceName))); } m_contracts[contract->name()].contract = contract; @@ -216,7 +216,7 @@ bool CompilerStack::parse() if (contract != existingContract) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(contract->location()) << - errinfo_comment(contract->name() + " conflicts with!!! contract at " + errinfo_comment(contract->name() + " is already defined at " + *(existingContract->location().sourceName))); } From ce3082dec2178273d4913a6f9df909c0aca8ef5a Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Sun, 27 Nov 2016 18:46:44 -0800 Subject: [PATCH 009/118] Tidy up the error message --- libsolidity/interface/CompilerStack.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index dded81f5f..dd5188607 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -184,10 +184,10 @@ bool CompilerStack::parse() { const ContractDefinition* existingContract = m_contracts.find(contract->name())->second.contract; if (contract != existingContract) - BOOST_THROW_EXCEPTION(CompilerError() << + BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(contract->location()) << - errinfo_comment(contract->name() + " is already defined at " - + *(existingContract->location().sourceName))); + errinfo_comment(contract->name() + " is already defined.") << + errinfo_secondarySourceLocation(SecondarySourceLocation().append(existingContract->location()), "Previous definition is here:")); } m_contracts[contract->name()].contract = contract; } From 9e88f1eebe27c780c80be06d702eac30e2fc5fa3 Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Sun, 27 Nov 2016 18:50:42 -0800 Subject: [PATCH 010/118] Tab whitespace cleanup (again) --- libsolidity/interface/CompilerStack.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index dd5188607..9c4618e78 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -187,7 +187,8 @@ bool CompilerStack::parse() BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(contract->location()) << errinfo_comment(contract->name() + " is already defined.") << - errinfo_secondarySourceLocation(SecondarySourceLocation().append(existingContract->location()), "Previous definition is here:")); + errinfo_secondarySourceLocation( + SecondarySourceLocation().append(existingContract->location()), "Previous definition is here:")); } m_contracts[contract->name()].contract = contract; } From 071b936b371cd5287717d0ac27b80b837836809a Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Sun, 11 Dec 2016 14:17:38 -0800 Subject: [PATCH 011/118] Only avoid collision if it's the same file @chriseth had suggested that it would be better if contracts were referenced in a file:contract notation, and that we output .bin files that prepend original path names if necessary to avoid a collision. This commit is mostly a draft; it still needs to be run through the test suite. --- libsolidity/ast/AST.cpp | 6 ++++ libsolidity/ast/AST.h | 2 ++ libsolidity/interface/CompilerStack.cpp | 48 ++++++++++++++++++------- libsolidity/interface/CompilerStack.h | 4 +++ solc/CommandLineInterface.cpp | 6 ++-- 5 files changed, 52 insertions(+), 14 deletions(-) diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 6f7a64dcb..480fce443 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -190,6 +190,12 @@ void ContractDefinition::setUserDocumentation(Json::Value const& _userDocumentat } +std::string ContractDefinition::fullyQualifiedName() const +{ + std::string qualifiedName = *(location().sourceName) + ":" + name(); + return qualifiedName; +} + vector const& ContractDefinition::inheritableMembers() const { if (!m_inheritableMembers) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 2d0924089..060cf9737 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -358,6 +358,8 @@ public: Json::Value const& devDocumentation() const; void setDevDocumentation(Json::Value const& _devDocumentation); + std::string fullyQualifiedName() const; + virtual TypePointer type() const override; virtual ContractDefinitionAnnotation& annotation() const override; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 9c4618e78..13cf7d6ce 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -21,6 +21,7 @@ * Full-stack compiler that converts a source code string to bytecode. */ + #include #include @@ -180,17 +181,18 @@ bool CompilerStack::parse() if (!resolver.updateDeclaration(*m_globalContext->currentThis())) return false; if (!resolver.updateDeclaration(*m_globalContext->currentSuper())) return false; if (!resolver.resolveNamesAndTypes(*contract)) return false; - if (m_contracts.find(contract->name()) != m_contracts.end()) + + if (m_contracts.find(contract->fullyQualifiedName()) != m_contracts.end()) { - const ContractDefinition* existingContract = m_contracts.find(contract->name())->second.contract; + const ContractDefinition* existingContract = m_contracts.find(contract->fullyQualifiedName())->second.contract; if (contract != existingContract) - BOOST_THROW_EXCEPTION(DeclarationError() << + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(contract->location()) << errinfo_comment(contract->name() + " is already defined.") << errinfo_secondarySourceLocation( - SecondarySourceLocation().append(existingContract->location()), "Previous definition is here:")); + SecondarySourceLocation().append("Previous definition is here:", existingContract->location()))); } - m_contracts[contract->name()].contract = contract; + m_contracts[contract->fullyQualifiedName()].contract = contract; } if (!checkLibraryNameClashes()) @@ -211,9 +213,9 @@ bool CompilerStack::parse() else noErrors = false; - if (m_contracts.find(contract->name()) != m_contracts.end()) + if (m_contracts.find(contract->fullyQualifiedName()) != m_contracts.end()) { - const ContractDefinition* existingContract = m_contracts.find(contract->name())->second.contract; + const ContractDefinition* existingContract = m_contracts.find(contract->fullyQualifiedName())->second.contract; if (contract != existingContract) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(contract->location()) << @@ -221,7 +223,7 @@ bool CompilerStack::parse() + *(existingContract->location().sourceName))); } - m_contracts[contract->name()].contract = contract; + m_contracts[contract->fullyQualifiedName()].contract = contract; } if (noErrors) @@ -335,6 +337,28 @@ string const* CompilerStack::runtimeSourceMapping(string const& _contractName) c return c.runtimeSourceMapping.get(); } +std::string const CompilerStack::filesystemFriendlyName(string const& _contractName) const +{ + // Look up the contract (by its fully-qualified name) + Contract const& matchContract = m_contracts.at(_contractName); + // Check to see if it could collide on name + for (auto const& contract: m_contracts) + { + if (contract.second.contract->name() == matchContract.contract->name() && + contract.second.contract != matchContract.contract) + { + // If it does, then return its fully-qualified name, made fs-friendly + std::string friendlyName = boost::algorithm::replace_all_copy(_contractName, "/", "_"); + boost::algorithm::replace_all(friendlyName, ":", "_"); + boost::algorithm::replace_all(friendlyName, ".", "_"); + return friendlyName; + } + } + // If no collision, return the contract's name + // String is copied to ensure that the contract's name can't be messed with + return std::string(matchContract.contract->name()); +} + eth::LinkerObject const& CompilerStack::object(string const& _contractName) const { return contract(_contractName).object; @@ -598,7 +622,7 @@ bool CompilerStack::checkLibraryNameClashes() if (ContractDefinition* contract = dynamic_cast(node.get())) if (contract->isLibrary()) { - if (libraries.count(contract->name())) + if (libraries.count(contract->fullyQualifiedName())) { auto err = make_shared(Error::Type::DeclarationError); *err << @@ -615,7 +639,7 @@ bool CompilerStack::checkLibraryNameClashes() clashFound = true; } else - libraries[contract->name()] = contract->location(); + libraries[contract->fullyQualifiedName()] = contract->location(); } return !clashFound; } @@ -648,7 +672,7 @@ void CompilerStack::compileContract( compileContract(*dependency, _compiledContracts); shared_ptr compiler = make_shared(m_optimize, m_optimizeRuns); - Contract& compiledContract = m_contracts.at(_contract.name()); + Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName()); string onChainMetadata = createOnChainMetadata(compiledContract); bytes cborEncodedMetadata = // CBOR-encoding of {"bzzr0": dev::swarmHash(onChainMetadata)} @@ -694,7 +718,7 @@ CompilerStack::Contract const& CompilerStack::contract(string const& _contractNa for (auto const& it: m_sources) for (ASTPointer const& node: it.second.ast->nodes()) if (auto contract = dynamic_cast(node.get())) - contractName = contract->name(); + contractName = contract->fullyQualifiedName(); auto it = m_contracts.find(contractName); if (it == m_contracts.end()) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Contract " + _contractName + " not found.")); diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index d49a8df14..9436bd834 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -148,6 +148,10 @@ public: /// @returns the string that provides a mapping between runtime bytecode and sourcecode. /// if the contract does not (yet) have bytecode. std::string const* runtimeSourceMapping(std::string const& _contractName = "") const; + + /// @returns either the contract's name or a mixture of its name and source file, sanitized for filesystem use + std::string const filesystemFriendlyName(std::string const& _contractName) const; + /// @returns hash of the runtime bytecode for the contract, i.e. the code that is /// returned by the constructor or the zero-h256 if the contract still needs to be linked or /// does not have runtime code. diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index e322455b8..cebc9a07e 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -185,8 +185,10 @@ void CommandLineInterface::handleBinary(string const& _contract) { if (m_args.count(g_argBinary)) { - if (m_args.count(g_argOutputDir)) - createFile(_contract + ".bin", m_compiler->object(_contract).toHex()); + if (m_args.count("output-dir")) + { + createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin", m_compiler->object(_contract).toHex()); + } else { cout << "Binary: " << endl; From 8f25bd54e35dba3fb632c2d209a86acaba63d33d Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Sun, 11 Dec 2016 23:06:57 -0800 Subject: [PATCH 012/118] Drop ':' if the source file name is empty A large number of tests compile contracts while passing in an empty string for the source name. This leads to it being keyed by the name ":", while the tests try to look it up under the name "". This change resolves that issue by dropping the ':' in cases where there is, effectively, no source file to prepend anyway. --- libsolidity/ast/AST.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 480fce443..562ac8285 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -192,7 +192,8 @@ void ContractDefinition::setUserDocumentation(Json::Value const& _userDocumentat std::string ContractDefinition::fullyQualifiedName() const { - std::string qualifiedName = *(location().sourceName) + ":" + name(); + std::string sourceString = *(location().sourceName); + std::string qualifiedName = (sourceString.empty() ? ("") : (sourceString + ":")) + name(); return qualifiedName; } From e3b0827721f114a371df57b7079205034683ed25 Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Sun, 11 Dec 2016 23:58:01 -0800 Subject: [PATCH 013/118] Push the error instead of throwing it Throwing a CompilerError on multiple contract definition violates the expectations of the test suite, which thinks that compile() will return false if the code can't compile. This brings contract collision reporting in line with most of the other errors. --- libsolidity/interface/CompilerStack.cpp | 48 ++++++++++++++++++------- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 13cf7d6ce..7626406c0 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -186,13 +186,24 @@ bool CompilerStack::parse() { const ContractDefinition* existingContract = m_contracts.find(contract->fullyQualifiedName())->second.contract; if (contract != existingContract) - BOOST_THROW_EXCEPTION(CompilerError() << + { + auto err = make_shared(Error::Type::DeclarationError); + *err << errinfo_sourceLocation(contract->location()) << - errinfo_comment(contract->name() + " is already defined.") << - errinfo_secondarySourceLocation( - SecondarySourceLocation().append("Previous definition is here:", existingContract->location()))); + errinfo_comment( + "Contract/Library \"" + contract->name() + "\" declared twice " + ) << + errinfo_secondarySourceLocation(SecondarySourceLocation().append( + "The other declaration is here:", existingContract->location())); + + m_errors.push_back(err); + noErrors = false; + } + } + else + { + m_contracts[contract->fullyQualifiedName()].contract = contract; } - m_contracts[contract->fullyQualifiedName()].contract = contract; } if (!checkLibraryNameClashes()) @@ -216,14 +227,27 @@ bool CompilerStack::parse() if (m_contracts.find(contract->fullyQualifiedName()) != m_contracts.end()) { const ContractDefinition* existingContract = m_contracts.find(contract->fullyQualifiedName())->second.contract; - if (contract != existingContract) - BOOST_THROW_EXCEPTION(CompilerError() << - errinfo_sourceLocation(contract->location()) << - errinfo_comment(contract->name() + " is already defined at " - + *(existingContract->location().sourceName))); - } - m_contracts[contract->fullyQualifiedName()].contract = contract; + if (contract != existingContract) + { + auto err = make_shared(Error::Type::DeclarationError); + *err << + errinfo_sourceLocation(contract->location()) << + errinfo_comment( + "Contract/Library \"" + contract->name() + "\" declared twice " + ) << + errinfo_secondarySourceLocation(SecondarySourceLocation().append( + "The other declaration is here:", existingContract->location())); + + m_errors.push_back(err); + noErrors = false; + } + + } + else + { + m_contracts[contract->fullyQualifiedName()].contract = contract; + } } if (noErrors) From 220ccfb492511eab5a45936e477e6882f52be96a Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Mon, 12 Dec 2016 00:00:50 -0800 Subject: [PATCH 014/118] Move clashing libraries to common source in test Since contracts and libraries only collide if they share a common source file now, this test only works if both libraries are in the same source. --- test/libsolidity/Imports.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/libsolidity/Imports.cpp b/test/libsolidity/Imports.cpp index 56895fdc7..e01d2e905 100644 --- a/test/libsolidity/Imports.cpp +++ b/test/libsolidity/Imports.cpp @@ -104,8 +104,7 @@ BOOST_AUTO_TEST_CASE(simple_alias) BOOST_AUTO_TEST_CASE(library_name_clash) { CompilerStack c; - c.addSource("a", "library A {} pragma solidity >=0.0;"); - c.addSource("b", "library A {} pragma solidity >=0.0;"); + c.addSource("a", "library A {} library A {} pragma solidity >=0.0;"); BOOST_CHECK(!c.compile()); } From 0c98e4b2da6b5de885114a744339a1f0f96c79be Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Fri, 16 Dec 2016 04:22:42 -0800 Subject: [PATCH 015/118] Stylistic corrections --- libsolidity/ast/AST.cpp | 2 +- libsolidity/interface/CompilerStack.cpp | 12 ++++-------- solc/CommandLineInterface.cpp | 2 -- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 562ac8285..45ff69c58 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -190,7 +190,7 @@ void ContractDefinition::setUserDocumentation(Json::Value const& _userDocumentat } -std::string ContractDefinition::fullyQualifiedName() const +string ContractDefinition::fullyQualifiedName() const { std::string sourceString = *(location().sourceName); std::string qualifiedName = (sourceString.empty() ? ("") : (sourceString + ":")) + name(); diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 7626406c0..fbaf1bcc6 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -184,7 +184,7 @@ bool CompilerStack::parse() if (m_contracts.find(contract->fullyQualifiedName()) != m_contracts.end()) { - const ContractDefinition* existingContract = m_contracts.find(contract->fullyQualifiedName())->second.contract; + ContractDefinition const* existingContract = m_contracts[contract->fullyQualifiedName()].contract; if (contract != existingContract) { auto err = make_shared(Error::Type::DeclarationError); @@ -201,9 +201,7 @@ bool CompilerStack::parse() } } else - { m_contracts[contract->fullyQualifiedName()].contract = contract; - } } if (!checkLibraryNameClashes()) @@ -224,9 +222,10 @@ bool CompilerStack::parse() else noErrors = false; + // Note that find() must be used here to prevent an automatic insert into the map if (m_contracts.find(contract->fullyQualifiedName()) != m_contracts.end()) { - const ContractDefinition* existingContract = m_contracts.find(contract->fullyQualifiedName())->second.contract; + ContractDefinition const* existingContract = m_contracts[contract->fullyQualifiedName()].contract; if (contract != existingContract) { @@ -245,9 +244,7 @@ bool CompilerStack::parse() } else - { m_contracts[contract->fullyQualifiedName()].contract = contract; - } } if (noErrors) @@ -379,8 +376,7 @@ std::string const CompilerStack::filesystemFriendlyName(string const& _contractN } } // If no collision, return the contract's name - // String is copied to ensure that the contract's name can't be messed with - return std::string(matchContract.contract->name()); + return matchContract.contract->name(); } eth::LinkerObject const& CompilerStack::object(string const& _contractName) const diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index cebc9a07e..4a4d3571b 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -186,9 +186,7 @@ void CommandLineInterface::handleBinary(string const& _contract) if (m_args.count(g_argBinary)) { if (m_args.count("output-dir")) - { createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin", m_compiler->object(_contract).toHex()); - } else { cout << "Binary: " << endl; From f10bf36ae3681169be7c75dac32567f1746c529d Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Fri, 16 Dec 2016 04:52:19 -0800 Subject: [PATCH 016/118] Move fullyQualified() name to Declaration --- libsolidity/ast/AST.cpp | 8 -------- libsolidity/ast/AST.h | 3 +-- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 45ff69c58..dd582e544 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -189,14 +189,6 @@ void ContractDefinition::setUserDocumentation(Json::Value const& _userDocumentat m_userDocumentation = _userDocumentation; } - -string ContractDefinition::fullyQualifiedName() const -{ - std::string sourceString = *(location().sourceName); - std::string qualifiedName = (sourceString.empty() ? ("") : (sourceString + ":")) + name(); - return qualifiedName; -} - vector const& ContractDefinition::inheritableMembers() const { if (!m_inheritableMembers) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 060cf9737..f72b272c2 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -161,6 +161,7 @@ public: /// @returns the source name this declaration is present in. /// Can be combined with annotation().canonicalName to form a globally unique name. std::string sourceUnitName() const; + std::string fullyQualifiedName() const { return sourceUnitName() + ":" + name(); } virtual bool isLValue() const { return false; } virtual bool isPartOfExternalInterface() const { return false; } @@ -358,8 +359,6 @@ public: Json::Value const& devDocumentation() const; void setDevDocumentation(Json::Value const& _devDocumentation); - std::string fullyQualifiedName() const; - virtual TypePointer type() const override; virtual ContractDefinitionAnnotation& annotation() const override; From f8914c6b281f398d6084f10708233bf79d633ef0 Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Fri, 16 Dec 2016 07:54:18 -0800 Subject: [PATCH 017/118] Fix contract lookup in tests The fully-qualified name of a contract with no source unit is : instead of just , so the test system needed to be adjusted accordingly. --- test/contracts/AuctionRegistrar.cpp | 2 +- test/contracts/FixedFeeRegistrar.cpp | 2 +- test/contracts/Wallet.cpp | 2 +- test/libsolidity/SolidityABIJSON.cpp | 4 ++-- test/libsolidity/SolidityExecutionFramework.h | 6 +++++- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/test/contracts/AuctionRegistrar.cpp b/test/contracts/AuctionRegistrar.cpp index fb8c1c68c..0169648e3 100644 --- a/test/contracts/AuctionRegistrar.cpp +++ b/test/contracts/AuctionRegistrar.cpp @@ -224,7 +224,7 @@ protected: m_compiler.reset(false); m_compiler.addSource("", registrarCode); ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed"); - s_compiledRegistrar.reset(new bytes(m_compiler.object("GlobalRegistrar").bytecode)); + s_compiledRegistrar.reset(new bytes(m_compiler.object(":GlobalRegistrar").bytecode)); } sendMessage(*s_compiledRegistrar, true); BOOST_REQUIRE(!m_output.empty()); diff --git a/test/contracts/FixedFeeRegistrar.cpp b/test/contracts/FixedFeeRegistrar.cpp index 39c32eb72..6ef1a8fe1 100644 --- a/test/contracts/FixedFeeRegistrar.cpp +++ b/test/contracts/FixedFeeRegistrar.cpp @@ -136,7 +136,7 @@ protected: m_compiler.reset(false); m_compiler.addSource("", registrarCode); ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed"); - s_compiledRegistrar.reset(new bytes(m_compiler.object("FixedFeeRegistrar").bytecode)); + s_compiledRegistrar.reset(new bytes(m_compiler.object(":FixedFeeRegistrar").bytecode)); } sendMessage(*s_compiledRegistrar, true); BOOST_REQUIRE(!m_output.empty()); diff --git a/test/contracts/Wallet.cpp b/test/contracts/Wallet.cpp index 80f06613e..24ecebe78 100644 --- a/test/contracts/Wallet.cpp +++ b/test/contracts/Wallet.cpp @@ -451,7 +451,7 @@ protected: m_compiler.reset(false); m_compiler.addSource("", walletCode); ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed"); - s_compiledWallet.reset(new bytes(m_compiler.object("Wallet").bytecode)); + s_compiledWallet.reset(new bytes(m_compiler.object(":Wallet").bytecode)); } bytes args = encodeArgs(u256(0x60), _required, _dailyLimit, u256(_owners.size()), _owners); sendMessage(*s_compiledWallet + args, true, _value); diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index 043d74ed4..a54e9b7ea 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -762,8 +762,8 @@ BOOST_AUTO_TEST_CASE(metadata_stamp) } )"; BOOST_REQUIRE(m_compilerStack.compile(std::string(sourceCode))); - bytes const& bytecode = m_compilerStack.runtimeObject("test").bytecode; - bytes hash = dev::swarmHash(m_compilerStack.onChainMetadata("test")).asBytes(); + bytes const& bytecode = m_compilerStack.runtimeObject(":test").bytecode; + bytes hash = dev::swarmHash(m_compilerStack.onChainMetadata(":test")).asBytes(); BOOST_REQUIRE(hash.size() == 32); BOOST_REQUIRE(bytecode.size() >= 2); size_t metadataCBORSize = (size_t(bytecode.end()[-2]) << 8) + size_t(bytecode.end()[-1]); diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h index 03e3a881c..00e8330ce 100644 --- a/test/libsolidity/SolidityExecutionFramework.h +++ b/test/libsolidity/SolidityExecutionFramework.h @@ -67,7 +67,11 @@ public: ); BOOST_ERROR("Compiling contract failed"); } - eth::LinkerObject obj = m_compiler.object(_contractName); + eth::LinkerObject obj; + if (_contractName.empty()) + obj = m_compiler.object(_contractName); + else + obj = m_compiler.object(":" + _contractName); BOOST_REQUIRE(obj.linkReferences.empty()); sendMessage(obj.bytecode + _arguments, true, _value); return m_output; From 85c55c796adf4d93c3d11ccb8d048a51dc46cf8d Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Tue, 20 Dec 2016 04:57:46 -0800 Subject: [PATCH 018/118] Remove unique error for contract collision Because contracts are uniquely identified by their source unit, there is no need for a unique error for this; it's actually covered by the checker for double-declaration of identifiers. --- libsolidity/interface/CompilerStack.cpp | 49 +++++-------------------- 1 file changed, 10 insertions(+), 39 deletions(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index fbaf1bcc6..d4675a235 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -182,25 +182,12 @@ bool CompilerStack::parse() if (!resolver.updateDeclaration(*m_globalContext->currentSuper())) return false; if (!resolver.resolveNamesAndTypes(*contract)) return false; - if (m_contracts.find(contract->fullyQualifiedName()) != m_contracts.end()) - { - ContractDefinition const* existingContract = m_contracts[contract->fullyQualifiedName()].contract; - if (contract != existingContract) - { - auto err = make_shared(Error::Type::DeclarationError); - *err << - errinfo_sourceLocation(contract->location()) << - errinfo_comment( - "Contract/Library \"" + contract->name() + "\" declared twice " - ) << - errinfo_secondarySourceLocation(SecondarySourceLocation().append( - "The other declaration is here:", existingContract->location())); + // Note that we now reference contracts by their fully qualified names, and + // thus contracts can only conflict if declared in the same source file. This + // already causes a double-declaration error elsewhere, so we do not report + // an error here and instead silently drop any additional contracts we find. - m_errors.push_back(err); - noErrors = false; - } - } - else + if (m_contracts.find(contract->fullyQualifiedName()) == m_contracts.end()) m_contracts[contract->fullyQualifiedName()].contract = contract; } @@ -222,28 +209,12 @@ bool CompilerStack::parse() else noErrors = false; - // Note that find() must be used here to prevent an automatic insert into the map - if (m_contracts.find(contract->fullyQualifiedName()) != m_contracts.end()) - { - ContractDefinition const* existingContract = m_contracts[contract->fullyQualifiedName()].contract; + // Note that we now reference contracts by their fully qualified names, and + // thus contracts can only conflict if declared in the same source file. This + // already causes a double-declaration error elsewhere, so we do not report + // an error here and instead silently drop any additional contracts we find. - if (contract != existingContract) - { - auto err = make_shared(Error::Type::DeclarationError); - *err << - errinfo_sourceLocation(contract->location()) << - errinfo_comment( - "Contract/Library \"" + contract->name() + "\" declared twice " - ) << - errinfo_secondarySourceLocation(SecondarySourceLocation().append( - "The other declaration is here:", existingContract->location())); - - m_errors.push_back(err); - noErrors = false; - } - - } - else + if (m_contracts.find(contract->fullyQualifiedName()) == m_contracts.end()) m_contracts[contract->fullyQualifiedName()].contract = contract; } From 1f30982ab532fdf719f3924e24e80057fe85e031 Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Wed, 21 Dec 2016 11:26:22 -0800 Subject: [PATCH 019/118] Use fully-qualified names for linking, too Using libraries leaves behind a library link reference in the binary which the linker must later resolve. These link references were still being generated by name and not by fully-qualified name. This would lead to a link-time collision between two libraries having the same name but in different source units. This change changes linker symbols over to fully-qualified names, which resolves that issue. This does potentially introduce a new problem, which is that linker symbols appear to be limited to 36 characters and are truncated. Storing paths extends the average symbol size, and it would be great if truncation was from the tail rather than the head. --- libsolidity/codegen/ContractCompiler.cpp | 2 +- libsolidity/codegen/ExpressionCompiler.cpp | 4 ++-- test/libsolidity/SolidityEndToEndTest.cpp | 24 +++++++++++----------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index a0f196bc3..fa77c1aad 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -575,7 +575,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) else if (auto contract = dynamic_cast(decl)) { solAssert(contract->isLibrary(), ""); - _assembly.appendLibraryAddress(contract->name()); + _assembly.appendLibraryAddress(contract->fullyQualifiedName()); } else solAssert(false, "Invalid declaration type."); diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 3922da881..37bd14583 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -892,7 +892,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) solAssert(funType->location() == FunctionType::Location::DelegateCall, ""); auto contract = dynamic_cast(funType->declaration().scope()); solAssert(contract && contract->isLibrary(), ""); - m_context.appendLibraryAddress(contract->name()); + m_context.appendLibraryAddress(contract->fullyQualifiedName()); m_context << funType->externalIdentifier(); utils().moveIntoStack(funType->selfType()->sizeOnStack(), 2); } @@ -1270,7 +1270,7 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) else if (auto contract = dynamic_cast(declaration)) { if (contract->isLibrary()) - m_context.appendLibraryAddress(contract->name()); + m_context.appendLibraryAddress(contract->fullyQualifiedName()); } else if (dynamic_cast(declaration)) { diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 191618317..03aa26999 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -3192,7 +3192,7 @@ BOOST_AUTO_TEST_CASE(library_call_in_homestead) } )"; compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{":Lib", m_contractAddress}}); BOOST_CHECK(callContractFunction("f()") == encodeArgs()); BOOST_CHECK(callContractFunction("sender()") == encodeArgs(u160(m_sender))); } @@ -6191,7 +6191,7 @@ BOOST_AUTO_TEST_CASE(library_call) } )"; compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{":Lib", m_contractAddress}}); BOOST_CHECK(callContractFunction("f(uint256)", u256(33)) == encodeArgs(u256(33) * 9)); } @@ -6208,7 +6208,7 @@ BOOST_AUTO_TEST_CASE(library_stray_values) } )"; compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{":Lib", m_contractAddress}}); BOOST_CHECK(callContractFunction("f(uint256)", u256(33)) == encodeArgs(u256(42))); } @@ -6341,7 +6341,7 @@ BOOST_AUTO_TEST_CASE(internal_types_in_library) } )"; compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{":Lib", m_contractAddress}}); BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(4), u256(17))); } @@ -6368,7 +6368,7 @@ BOOST_AUTO_TEST_CASE(using_library_structs) } )"; compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{":Lib", m_contractAddress}}); BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(7), u256(8))); } @@ -6902,7 +6902,7 @@ BOOST_AUTO_TEST_CASE(using_for_function_on_int) } )"; compileAndRun(sourceCode, 0, "D"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); + compileAndRun(sourceCode, 0, "C", bytes(), map{{":D", m_contractAddress}}); BOOST_CHECK(callContractFunction("f(uint256)", u256(9)) == encodeArgs(u256(2 * 9))); } @@ -6920,7 +6920,7 @@ BOOST_AUTO_TEST_CASE(using_for_function_on_struct) } )"; compileAndRun(sourceCode, 0, "D"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); + compileAndRun(sourceCode, 0, "C", bytes(), map{{":D", m_contractAddress}}); BOOST_CHECK(callContractFunction("f(uint256)", u256(7)) == encodeArgs(u256(3 * 7))); BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(3 * 7))); } @@ -6943,7 +6943,7 @@ BOOST_AUTO_TEST_CASE(using_for_overload) } )"; compileAndRun(sourceCode, 0, "D"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); + compileAndRun(sourceCode, 0, "C", bytes(), map{{":D", m_contractAddress}}); BOOST_CHECK(callContractFunction("f(uint256)", u256(7)) == encodeArgs(u256(6 * 7))); BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(6 * 7))); } @@ -6962,7 +6962,7 @@ BOOST_AUTO_TEST_CASE(using_for_by_name) } )"; compileAndRun(sourceCode, 0, "D"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); + compileAndRun(sourceCode, 0, "C", bytes(), map{{":D", m_contractAddress}}); BOOST_CHECK(callContractFunction("f(uint256)", u256(7)) == encodeArgs(u256(6 * 7))); BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(6 * 7))); } @@ -6982,7 +6982,7 @@ BOOST_AUTO_TEST_CASE(bound_function_in_var) } )"; compileAndRun(sourceCode, 0, "D"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); + compileAndRun(sourceCode, 0, "C", bytes(), map{{":D", m_contractAddress}}); BOOST_CHECK(callContractFunction("f(uint256)", u256(7)) == encodeArgs(u256(6 * 7))); BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(6 * 7))); } @@ -7005,7 +7005,7 @@ BOOST_AUTO_TEST_CASE(bound_function_to_string) } )"; compileAndRun(sourceCode, 0, "D"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); + compileAndRun(sourceCode, 0, "C", bytes(), map{{":D", m_contractAddress}}); BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(3))); BOOST_CHECK(callContractFunction("g()") == encodeArgs(u256(3))); } @@ -7751,7 +7751,7 @@ BOOST_AUTO_TEST_CASE(payable_function_calls_library) } )"; compileAndRun(sourceCode, 0, "L"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"L", m_contractAddress}}); + compileAndRun(sourceCode, 0, "C", bytes(), map{{":L", m_contractAddress}}); BOOST_CHECK(callContractFunctionWithValue("f()", 27) == encodeArgs(u256(7))); } From 5a2331a9f6615e58f4d10d266870daaaf05deb6b Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Wed, 21 Dec 2016 11:40:13 -0800 Subject: [PATCH 020/118] Remove checkLibraryNameClashes() The library name clash checker throws errors when two libraries of the same name are spotted. In a previous commit, this function was rewritten to use fully-qualified names instead, which makes it redundant to the checker for multiply-declared identifiers. Since it no longer serves a clear purpose, the function is being dropped. --- libsolidity/interface/CompilerStack.cpp | 34 ------------------------- libsolidity/interface/CompilerStack.h | 3 --- 2 files changed, 37 deletions(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index d4675a235..d8bb20d7c 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -191,9 +191,6 @@ bool CompilerStack::parse() m_contracts[contract->fullyQualifiedName()].contract = contract; } - if (!checkLibraryNameClashes()) - noErrors = false; - for (Source const* source: m_sourceOrder) for (ASTPointer const& node: source->ast->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) @@ -604,37 +601,6 @@ void CompilerStack::resolveImports() swap(m_sourceOrder, sourceOrder); } -bool CompilerStack::checkLibraryNameClashes() -{ - bool clashFound = false; - map libraries; - for (Source const* source: m_sourceOrder) - for (ASTPointer const& node: source->ast->nodes()) - if (ContractDefinition* contract = dynamic_cast(node.get())) - if (contract->isLibrary()) - { - if (libraries.count(contract->fullyQualifiedName())) - { - auto err = make_shared(Error::Type::DeclarationError); - *err << - errinfo_sourceLocation(contract->location()) << - errinfo_comment( - "Library \"" + contract->name() + "\" declared twice " - "(will create ambiguities during linking)." - ) << - errinfo_secondarySourceLocation(SecondarySourceLocation().append( - "The other declaration is here:", libraries[contract->name()] - )); - - m_errors.push_back(err); - clashFound = true; - } - else - libraries[contract->fullyQualifiedName()] = contract->location(); - } - return !clashFound; -} - string CompilerStack::absolutePath(string const& _path, string const& _reference) const { using path = boost::filesystem::path; diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 9436bd834..61edc284a 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -234,9 +234,6 @@ private: StringMap loadMissingSources(SourceUnit const& _ast, std::string const& _path); std::string applyRemapping(std::string const& _path, std::string const& _context); void resolveImports(); - /// Checks whether there are libraries with the same name, reports that as an error and - /// @returns false in this case. - bool checkLibraryNameClashes(); /// @returns the absolute path corresponding to @a _path relative to @a _reference. std::string absolutePath(std::string const& _path, std::string const& _reference) const; /// Helper function to return path converted strings. From a7f8a1986a9b3997c75cfc84235e567d2fa9aab4 Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Wed, 21 Dec 2016 11:45:34 -0800 Subject: [PATCH 021/118] Revert "Move clashing libraries to common source in test" This reverts commit c4a9ca5cfe7a8b4ba9d2d84392c57d5eefacd1f7. --- test/libsolidity/Imports.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/libsolidity/Imports.cpp b/test/libsolidity/Imports.cpp index e01d2e905..56895fdc7 100644 --- a/test/libsolidity/Imports.cpp +++ b/test/libsolidity/Imports.cpp @@ -104,7 +104,8 @@ BOOST_AUTO_TEST_CASE(simple_alias) BOOST_AUTO_TEST_CASE(library_name_clash) { CompilerStack c; - c.addSource("a", "library A {} library A {} pragma solidity >=0.0;"); + c.addSource("a", "library A {} pragma solidity >=0.0;"); + c.addSource("b", "library A {} pragma solidity >=0.0;"); BOOST_CHECK(!c.compile()); } From fda39afdba9591131aac6d647cb46247e9495606 Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Thu, 22 Dec 2016 08:30:53 -0800 Subject: [PATCH 022/118] Modify library collision test Since libaraies no longer collide on name but on fully-qualified name, you can only induce collision by colliding them as idenfitiers. --- test/libsolidity/Imports.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/libsolidity/Imports.cpp b/test/libsolidity/Imports.cpp index 56895fdc7..6aa96fb87 100644 --- a/test/libsolidity/Imports.cpp +++ b/test/libsolidity/Imports.cpp @@ -106,6 +106,7 @@ BOOST_AUTO_TEST_CASE(library_name_clash) CompilerStack c; c.addSource("a", "library A {} pragma solidity >=0.0;"); c.addSource("b", "library A {} pragma solidity >=0.0;"); + c.addSource("c", "import {A} from \"./a\"; import {A} from \"./b\";"); BOOST_CHECK(!c.compile()); } From 610156fb92cbf9feadf14b6d0827f0f840b4c765 Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Thu, 5 Jan 2017 20:08:34 -0500 Subject: [PATCH 023/118] Fix bad merge artifact(?) Looks like merging up munged line 188 in CommandLineInterface.cpp, so that a string literal was being used where a global variable should be. --- solc/CommandLineInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 4a4d3571b..e49e8517d 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -185,7 +185,7 @@ void CommandLineInterface::handleBinary(string const& _contract) { if (m_args.count(g_argBinary)) { - if (m_args.count("output-dir")) + if (m_args.count(g_argOutputDir)) createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin", m_compiler->object(_contract).toHex()); else { From 43dbf01e73b915dbeddfd7eb9d62112fdf2f99f1 Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Sun, 8 Jan 2017 21:49:39 -0500 Subject: [PATCH 024/118] Update changelog --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index a5c4b922f..2c5c37fd7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,6 +12,9 @@ Bugfixes: * Imports: ``import ".dir/a"`` is not a relative path. Relative paths begin with directory ``.`` or ``..``. * Type checker, disallow inheritances of different kinds (e.g. a function and a modifier) of members of the same name +Features: + * Contracts and libraries are now unique to their source files, rather than globally. + ### 0.4.7 (2016-12-15) Features: From 88a2ac25e5e10aa7cea8627b60aa663f7ed6066d Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Mon, 16 Jan 2017 18:07:48 -0500 Subject: [PATCH 025/118] Revert "Fix contract lookup in tests" This reverts commit f8914c6b281f398d6084f10708233bf79d633ef0. --- test/contracts/AuctionRegistrar.cpp | 2 +- test/contracts/FixedFeeRegistrar.cpp | 2 +- test/contracts/Wallet.cpp | 2 +- test/libsolidity/SolidityABIJSON.cpp | 4 ++-- test/libsolidity/SolidityExecutionFramework.h | 6 +----- 5 files changed, 6 insertions(+), 10 deletions(-) diff --git a/test/contracts/AuctionRegistrar.cpp b/test/contracts/AuctionRegistrar.cpp index 0169648e3..fb8c1c68c 100644 --- a/test/contracts/AuctionRegistrar.cpp +++ b/test/contracts/AuctionRegistrar.cpp @@ -224,7 +224,7 @@ protected: m_compiler.reset(false); m_compiler.addSource("", registrarCode); ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed"); - s_compiledRegistrar.reset(new bytes(m_compiler.object(":GlobalRegistrar").bytecode)); + s_compiledRegistrar.reset(new bytes(m_compiler.object("GlobalRegistrar").bytecode)); } sendMessage(*s_compiledRegistrar, true); BOOST_REQUIRE(!m_output.empty()); diff --git a/test/contracts/FixedFeeRegistrar.cpp b/test/contracts/FixedFeeRegistrar.cpp index 6ef1a8fe1..39c32eb72 100644 --- a/test/contracts/FixedFeeRegistrar.cpp +++ b/test/contracts/FixedFeeRegistrar.cpp @@ -136,7 +136,7 @@ protected: m_compiler.reset(false); m_compiler.addSource("", registrarCode); ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed"); - s_compiledRegistrar.reset(new bytes(m_compiler.object(":FixedFeeRegistrar").bytecode)); + s_compiledRegistrar.reset(new bytes(m_compiler.object("FixedFeeRegistrar").bytecode)); } sendMessage(*s_compiledRegistrar, true); BOOST_REQUIRE(!m_output.empty()); diff --git a/test/contracts/Wallet.cpp b/test/contracts/Wallet.cpp index 24ecebe78..80f06613e 100644 --- a/test/contracts/Wallet.cpp +++ b/test/contracts/Wallet.cpp @@ -451,7 +451,7 @@ protected: m_compiler.reset(false); m_compiler.addSource("", walletCode); ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed"); - s_compiledWallet.reset(new bytes(m_compiler.object(":Wallet").bytecode)); + s_compiledWallet.reset(new bytes(m_compiler.object("Wallet").bytecode)); } bytes args = encodeArgs(u256(0x60), _required, _dailyLimit, u256(_owners.size()), _owners); sendMessage(*s_compiledWallet + args, true, _value); diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index a54e9b7ea..043d74ed4 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -762,8 +762,8 @@ BOOST_AUTO_TEST_CASE(metadata_stamp) } )"; BOOST_REQUIRE(m_compilerStack.compile(std::string(sourceCode))); - bytes const& bytecode = m_compilerStack.runtimeObject(":test").bytecode; - bytes hash = dev::swarmHash(m_compilerStack.onChainMetadata(":test")).asBytes(); + bytes const& bytecode = m_compilerStack.runtimeObject("test").bytecode; + bytes hash = dev::swarmHash(m_compilerStack.onChainMetadata("test")).asBytes(); BOOST_REQUIRE(hash.size() == 32); BOOST_REQUIRE(bytecode.size() >= 2); size_t metadataCBORSize = (size_t(bytecode.end()[-2]) << 8) + size_t(bytecode.end()[-1]); diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h index 00e8330ce..03e3a881c 100644 --- a/test/libsolidity/SolidityExecutionFramework.h +++ b/test/libsolidity/SolidityExecutionFramework.h @@ -67,11 +67,7 @@ public: ); BOOST_ERROR("Compiling contract failed"); } - eth::LinkerObject obj; - if (_contractName.empty()) - obj = m_compiler.object(_contractName); - else - obj = m_compiler.object(":" + _contractName); + eth::LinkerObject obj = m_compiler.object(_contractName); BOOST_REQUIRE(obj.linkReferences.empty()); sendMessage(obj.bytecode + _arguments, true, _value); return m_output; From 94b092d87c051e8846f5d61eaa1a4581b6588c71 Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Mon, 16 Jan 2017 23:47:04 -0500 Subject: [PATCH 026/118] Provide fall-back method for contract lookup Properly, contracts are now looked up via : identifiers called "fully qualified names." As a modicum of backward-compatibility, failure on a lookup is now backed up by seeing if the ":" exists at all, and if it doesn't, then the known contracts are scanned for any matching contract name. --- libsolidity/interface/CompilerStack.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index d8bb20d7c..262b91ff1 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -677,8 +677,25 @@ CompilerStack::Contract const& CompilerStack::contract(string const& _contractNa if (auto contract = dynamic_cast(node.get())) contractName = contract->fullyQualifiedName(); auto it = m_contracts.find(contractName); - if (it == m_contracts.end()) + // To provide a measure of backward-compatibility, if a contract is not located by its + // fully-qualified name, a lookup will be attempted purely on the contract's name to see + // if anything will satisfy. + if (it == m_contracts.end() && contractName.find(":") == string::npos) + { + for (auto const& contractEntry: m_contracts) + { + stringstream ss; + ss.str(contractEntry.first); + // All entries are : + string source; + string foundName; + getline(ss, source, ':'); + getline(ss, foundName, ':'); + if (foundName == contractName) return contractEntry.second; + } + // If we get here, both lookup methods failed. BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Contract " + _contractName + " not found.")); + } return it->second; } From 6ecb4aa36f8c30ca91f650d17f1214c2f2bc4319 Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Tue, 17 Jan 2017 02:14:53 -0500 Subject: [PATCH 027/118] Add git submodule init to build instructions (#1569) --- docs/installing-solidity.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index ef38705cb..d73747179 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -119,6 +119,11 @@ you should fork Solidity and add your personal fork as a second remote: cd solidity git remote add personal git@github.com:[username]/solidity.git +Solidity has git submodules. Ensure they are properly loaded: + +.. code:: bash + + git submodule update --init --recursive Prerequisites - macOS --------------------- From 99eaadd2cd4ece9c3ceba16fa5559c45c2e66b08 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 17 Jan 2017 10:31:09 +0100 Subject: [PATCH 028/118] Deterministic AST node identifiers. --- Changelog.md | 3 +++ libsolidity/ast/AST.cpp | 2 ++ libsolidity/ast/AST.h | 4 ++++ libsolidity/ast/ASTJsonConverter.cpp | 4 ++-- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index a5c4b922f..1a80bac04 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,8 @@ ### 0.4.9 (unreleased) +Features: + * AST: Use deterministic node identifiers. + ### 0.4.8 (2017-01-13) Features: diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 6f7a64dcb..3db4627a2 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -37,6 +37,8 @@ using namespace dev::solidity; ASTNode::ASTNode(SourceLocation const& _location): m_location(_location) { + static size_t id = 0; + m_id = ++id; } ASTNode::~ASTNode() diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 2d0924089..e9df2e7d5 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -57,6 +57,9 @@ public: explicit ASTNode(SourceLocation const& _location); virtual ~ASTNode(); + /// @returns an identifier of this AST node that is unique for a single compilation run. + size_t id() const { return m_id; } + virtual void accept(ASTVisitor& _visitor) = 0; virtual void accept(ASTConstVisitor& _visitor) const = 0; template @@ -94,6 +97,7 @@ public: ///@} protected: + size_t m_id = 0; /// Annotation - is specialised in derived classes, is created upon request (because of polymorphism). mutable ASTAnnotation* m_annotation = nullptr; diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index abaad0fdf..de8fde92f 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -42,7 +42,7 @@ void ASTJsonConverter::addJsonNode( { Json::Value node; - node["id"] = reinterpret_cast(&_node); + node["id"] = _node.id(); node["src"] = sourceLocationToString(_node.location()); node["name"] = _nodeName; if (_attributes.size() != 0) @@ -124,7 +124,7 @@ bool ASTJsonConverter::visit(ContractDefinition const& _node) { Json::Value linearizedBaseContracts(Json::arrayValue); for (auto const& baseContract: _node.annotation().linearizedBaseContracts) - linearizedBaseContracts.append(reinterpret_cast(baseContract)); + linearizedBaseContracts.append(baseContract->id()); addJsonNode(_node, "ContractDefinition", { make_pair("name", _node.name()), make_pair("isLibrary", _node.isLibrary()), From 9f9b7fb6f0953558bb671c5385dec9c875eb15f0 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 17 Jan 2017 12:32:07 +0000 Subject: [PATCH 029/118] Explain how version numbers are built --- docs/installing-solidity.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index d73747179..2a1f50124 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -216,6 +216,21 @@ Alternatively, you can build for Windows on the command-line, like so: cmake --build . --config RelWithDebInfo +The version string in detail +============================ + +The Solidity version string contains four parts: +- the version number +- pre-release tag, usually set to ``develop.YYYY.MM.DD`` or ``nightly.YYYY.MM.DD`` +- commit in the format of ``commit.GITHASH`` +- platform has arbitrary number of items, containing details about the platform and compiler + +If there are local modifications, the commit will be postfixed with ``.mod``. + +A relase example: ``0.4.8+commit.60cc1668.Emscripten.clang``. + +A pre-release example: ``0.4.9-nightly.2017.1.17+commit.6ecb4aa3.Emscripten.clang`` + Important information about versioning ====================================== From 350c7e7e2c1bafbe8d543fc052aedad3e69ba5e1 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 17 Jan 2017 12:09:13 +0000 Subject: [PATCH 030/118] Store strict version number in metadata (exclude the platform) --- Changelog.md | 1 + libsolidity/interface/CompilerStack.cpp | 2 +- libsolidity/interface/Version.cpp | 3 +++ libsolidity/interface/Version.h | 1 + 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 1a80bac04..a4063640c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ Features: * AST: Use deterministic node identifiers. + * Metadata: Do not include platform in the version number. ### 0.4.8 (2017-01-13) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index a31df5846..08b217155 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -695,7 +695,7 @@ string CompilerStack::createOnChainMetadata(Contract const& _contract) const Json::Value meta; meta["version"] = 1; meta["language"] = "Solidity"; - meta["compiler"]["version"] = VersionString; + meta["compiler"]["version"] = VersionStringStrict; meta["sources"] = Json::objectValue; for (auto const& s: m_sources) diff --git a/libsolidity/interface/Version.cpp b/libsolidity/interface/Version.cpp index ff66f0398..30923fc25 100644 --- a/libsolidity/interface/Version.cpp +++ b/libsolidity/interface/Version.cpp @@ -38,6 +38,9 @@ string const dev::solidity::VersionString = (string(SOL_VERSION_PRERELEASE).empty() ? "" : "-" + string(SOL_VERSION_PRERELEASE)) + (string(SOL_VERSION_BUILDINFO).empty() ? "" : "+" + string(SOL_VERSION_BUILDINFO)); +string const dev::solidity::VersionStringStrict = + string(dev::solidity::VersionNumber) + + (string(SOL_VERSION_PRERELEASE).empty() ? "" : "-" + string(SOL_VERSION_PRERELEASE)); bytes dev::solidity::binaryVersion() { diff --git a/libsolidity/interface/Version.h b/libsolidity/interface/Version.h index 5b07b3f42..24c3555d5 100644 --- a/libsolidity/interface/Version.h +++ b/libsolidity/interface/Version.h @@ -32,6 +32,7 @@ namespace solidity extern char const* VersionNumber; extern std::string const VersionString; +extern std::string const VersionStringStrict; /// @returns a binary form of the version string, where A.B.C-HASH is encoded such that /// the first byte is zero, the following three bytes encode A B and C (interpreted as decimals) From 467559917008525d6b606de264e4a86a1c5e0620 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 17 Jan 2017 12:35:13 +0000 Subject: [PATCH 031/118] Include SOL_VERSION_COMMIT/SOL_VERSION_PLATFORM in buildinfo.h --- cmake/scripts/buildinfo.cmake | 2 ++ cmake/templates/BuildInfo.h.in | 2 ++ libsolidity/interface/Version.cpp | 3 ++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/cmake/scripts/buildinfo.cmake b/cmake/scripts/buildinfo.cmake index 8e1615f6d..efbfb8fb0 100644 --- a/cmake/scripts/buildinfo.cmake +++ b/cmake/scripts/buildinfo.cmake @@ -60,6 +60,8 @@ if (SOL_COMMIT_HASH AND SOL_LOCAL_CHANGES) set(SOL_COMMIT_HASH "${SOL_COMMIT_HASH}.mod") endif() +set(SOL_VERSION_COMMIT "commit.${SOL_COMMIT_HASH}") +set(SOl_VERSION_PLATFORM ETH_BUILD_PLATFORM) set(SOL_VERSION_BUILDINFO "commit.${SOL_COMMIT_HASH}.${ETH_BUILD_PLATFORM}") set(TMPFILE "${ETH_DST_DIR}/BuildInfo.h.tmp") diff --git a/cmake/templates/BuildInfo.h.in b/cmake/templates/BuildInfo.h.in index 6c16e4ac4..4b35df981 100644 --- a/cmake/templates/BuildInfo.h.in +++ b/cmake/templates/BuildInfo.h.in @@ -8,3 +8,5 @@ #define ETH_BUILD_PLATFORM "@ETH_BUILD_PLATFORM@" #define SOL_VERSION_PRERELEASE "@SOL_VERSION_PRERELEASE@" #define SOL_VERSION_BUILDINFO "@SOL_VERSION_BUILDINFO@" +#define SOL_VERSION_COMMIT "@SOL_VERSION_COMMIT@" +#define SOL_VERSION_PLATFORM "@SOL_VERSION_PLATFORM@" diff --git a/libsolidity/interface/Version.cpp b/libsolidity/interface/Version.cpp index 30923fc25..0d23f9c3d 100644 --- a/libsolidity/interface/Version.cpp +++ b/libsolidity/interface/Version.cpp @@ -40,7 +40,8 @@ string const dev::solidity::VersionString = string const dev::solidity::VersionStringStrict = string(dev::solidity::VersionNumber) + - (string(SOL_VERSION_PRERELEASE).empty() ? "" : "-" + string(SOL_VERSION_PRERELEASE)); + (string(SOL_VERSION_PRERELEASE).empty() ? "" : "-" + string(SOL_VERSION_PRERELEASE)) + + (string(SOL_VERSION_COMMIT).empty() ? "" : "+" + string(SOL_VERSION_COMMIT)); bytes dev::solidity::binaryVersion() { From 592c692bceb4cbf14446ad7eff949a475ab63251 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 17 Jan 2017 13:01:11 +0000 Subject: [PATCH 032/118] Mention how our version maps to Semver --- docs/installing-solidity.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 2a1f50124..345780d71 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -227,6 +227,9 @@ The Solidity version string contains four parts: If there are local modifications, the commit will be postfixed with ``.mod``. +These parts are combined as required by Semver, where the Solidity pre-release tag equals to the Semver pre-release +and the Solidity commit and platform combined make up the Semver build metadata. + A relase example: ``0.4.8+commit.60cc1668.Emscripten.clang``. A pre-release example: ``0.4.9-nightly.2017.1.17+commit.6ecb4aa3.Emscripten.clang`` From 2bf89776ae999297d373b7e3fe5e16c0b5d85965 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 17 Jan 2017 13:08:22 +0000 Subject: [PATCH 033/118] Update copyright date in docs --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 85fc36776..e97eff3a6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -49,7 +49,7 @@ master_doc = 'index' # General information about the project. project = 'Solidity' -copyright = '2016, Ethereum' +copyright = '2016-2017, Ethereum' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the From c16e141ffbca70174c0efa119dbe7c7ef14b5286 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 18 Jan 2017 14:09:40 +0100 Subject: [PATCH 034/118] Fix JSON output on macos. --- libsolidity/ast/ASTJsonConverter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index de8fde92f..69c10c8d3 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -42,7 +42,7 @@ void ASTJsonConverter::addJsonNode( { Json::Value node; - node["id"] = _node.id(); + node["id"] = Json::UInt64(_node.id()); node["src"] = sourceLocationToString(_node.location()); node["name"] = _nodeName; if (_attributes.size() != 0) @@ -124,7 +124,7 @@ bool ASTJsonConverter::visit(ContractDefinition const& _node) { Json::Value linearizedBaseContracts(Json::arrayValue); for (auto const& baseContract: _node.annotation().linearizedBaseContracts) - linearizedBaseContracts.append(baseContract->id()); + linearizedBaseContracts.append(Json::UInt64(baseContract->id())); addJsonNode(_node, "ContractDefinition", { make_pair("name", _node.name()), make_pair("isLibrary", _node.isLibrary()), From d40ae663ecbdbccc2b71eff175bb6765291ba078 Mon Sep 17 00:00:00 2001 From: Federico Bond Date: Wed, 18 Jan 2017 12:43:23 -0300 Subject: [PATCH 035/118] Fix typo in comment --- libsolidity/ast/AST.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index d11a246c2..4af649634 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -606,7 +606,7 @@ private: /** * Declaration of a variable. This can be used in various places, e.g. in function parameter - * lists, struct definitions and even function bodys. + * lists, struct definitions and even function bodies. */ class VariableDeclaration: public Declaration { From 821997a1d38718d7808416a69a3ba276729d2c08 Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Wed, 18 Jan 2017 17:24:39 +0100 Subject: [PATCH 036/118] libevmasm: remove duplicate `Tier` from `Tier::xTier`s Synchronize with cpp-ethereum --- libevmasm/GasMeter.cpp | 16 +-- libevmasm/Instruction.cpp | 262 +++++++++++++++++++------------------- libevmasm/Instruction.h | 20 +-- 3 files changed, 149 insertions(+), 149 deletions(-) diff --git a/libevmasm/GasMeter.cpp b/libevmasm/GasMeter.cpp index bc7d4e55e..462c09dd0 100644 --- a/libevmasm/GasMeter.cpp +++ b/libevmasm/GasMeter.cpp @@ -223,14 +223,14 @@ unsigned GasMeter::runGas(Instruction _instruction) switch (instructionInfo(_instruction).gasPriceTier) { - case Tier::ZeroTier: return GasCosts::tier0Gas; - case Tier::BaseTier: return GasCosts::tier1Gas; - case Tier::VeryLowTier: return GasCosts::tier2Gas; - case Tier::LowTier: return GasCosts::tier3Gas; - case Tier::MidTier: return GasCosts::tier4Gas; - case Tier::HighTier: return GasCosts::tier5Gas; - case Tier::ExtTier: return GasCosts::tier6Gas; - case Tier::SpecialTier: return GasCosts::tier7Gas; + case Tier::Zero: return GasCosts::tier0Gas; + case Tier::Base: return GasCosts::tier1Gas; + case Tier::VeryLow: return GasCosts::tier2Gas; + case Tier::Low: return GasCosts::tier3Gas; + case Tier::Mid: return GasCosts::tier4Gas; + case Tier::High: return GasCosts::tier5Gas; + case Tier::Ext: return GasCosts::tier6Gas; + case Tier::Special: return GasCosts::tier7Gas; default: break; } assertThrow(false, OptimizerException, "Invalid gas tier."); diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index 43cbbd380..17445c599 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -164,136 +164,136 @@ const std::map dev::solidity::c_instructions = static const std::map c_instructionInfo = { // Add, Args, Ret, SideEffects, GasPriceTier - { Instruction::STOP, { "STOP", 0, 0, 0, true, Tier::ZeroTier } }, - { Instruction::ADD, { "ADD", 0, 2, 1, false, Tier::VeryLowTier } }, - { Instruction::SUB, { "SUB", 0, 2, 1, false, Tier::VeryLowTier } }, - { Instruction::MUL, { "MUL", 0, 2, 1, false, Tier::LowTier } }, - { Instruction::DIV, { "DIV", 0, 2, 1, false, Tier::LowTier } }, - { Instruction::SDIV, { "SDIV", 0, 2, 1, false, Tier::LowTier } }, - { Instruction::MOD, { "MOD", 0, 2, 1, false, Tier::LowTier } }, - { Instruction::SMOD, { "SMOD", 0, 2, 1, false, Tier::LowTier } }, - { Instruction::EXP, { "EXP", 0, 2, 1, false, Tier::SpecialTier } }, - { Instruction::NOT, { "NOT", 0, 1, 1, false, Tier::VeryLowTier } }, - { Instruction::LT, { "LT", 0, 2, 1, false, Tier::VeryLowTier } }, - { Instruction::GT, { "GT", 0, 2, 1, false, Tier::VeryLowTier } }, - { Instruction::SLT, { "SLT", 0, 2, 1, false, Tier::VeryLowTier } }, - { Instruction::SGT, { "SGT", 0, 2, 1, false, Tier::VeryLowTier } }, - { Instruction::EQ, { "EQ", 0, 2, 1, false, Tier::VeryLowTier } }, - { Instruction::ISZERO, { "ISZERO", 0, 1, 1, false, Tier::VeryLowTier } }, - { Instruction::AND, { "AND", 0, 2, 1, false, Tier::VeryLowTier } }, - { Instruction::OR, { "OR", 0, 2, 1, false, Tier::VeryLowTier } }, - { Instruction::XOR, { "XOR", 0, 2, 1, false, Tier::VeryLowTier } }, - { Instruction::BYTE, { "BYTE", 0, 2, 1, false, Tier::VeryLowTier } }, - { Instruction::ADDMOD, { "ADDMOD", 0, 3, 1, false, Tier::MidTier } }, - { Instruction::MULMOD, { "MULMOD", 0, 3, 1, false, Tier::MidTier } }, - { Instruction::SIGNEXTEND, { "SIGNEXTEND", 0, 2, 1, false, Tier::LowTier } }, - { Instruction::SHA3, { "SHA3", 0, 2, 1, false, Tier::SpecialTier } }, - { Instruction::ADDRESS, { "ADDRESS", 0, 0, 1, false, Tier::BaseTier } }, - { Instruction::BALANCE, { "BALANCE", 0, 1, 1, false, Tier::ExtTier } }, - { Instruction::ORIGIN, { "ORIGIN", 0, 0, 1, false, Tier::BaseTier } }, - { Instruction::CALLER, { "CALLER", 0, 0, 1, false, Tier::BaseTier } }, - { Instruction::CALLVALUE, { "CALLVALUE", 0, 0, 1, false, Tier::BaseTier } }, - { Instruction::CALLDATALOAD,{ "CALLDATALOAD", 0, 1, 1, false, Tier::VeryLowTier } }, - { Instruction::CALLDATASIZE,{ "CALLDATASIZE", 0, 0, 1, false, Tier::BaseTier } }, - { Instruction::CALLDATACOPY,{ "CALLDATACOPY", 0, 3, 0, true, Tier::VeryLowTier } }, - { Instruction::CODESIZE, { "CODESIZE", 0, 0, 1, false, Tier::BaseTier } }, - { Instruction::CODECOPY, { "CODECOPY", 0, 3, 0, true, Tier::VeryLowTier } }, - { Instruction::GASPRICE, { "GASPRICE", 0, 0, 1, false, Tier::BaseTier } }, - { Instruction::EXTCODESIZE, { "EXTCODESIZE", 0, 1, 1, false, Tier::ExtTier } }, - { Instruction::EXTCODECOPY, { "EXTCODECOPY", 0, 4, 0, true, Tier::ExtTier } }, - { Instruction::BLOCKHASH, { "BLOCKHASH", 0, 1, 1, false, Tier::ExtTier } }, - { Instruction::COINBASE, { "COINBASE", 0, 0, 1, false, Tier::BaseTier } }, - { Instruction::TIMESTAMP, { "TIMESTAMP", 0, 0, 1, false, Tier::BaseTier } }, - { Instruction::NUMBER, { "NUMBER", 0, 0, 1, false, Tier::BaseTier } }, - { Instruction::DIFFICULTY, { "DIFFICULTY", 0, 0, 1, false, Tier::BaseTier } }, - { Instruction::GASLIMIT, { "GASLIMIT", 0, 0, 1, false, Tier::BaseTier } }, - { Instruction::POP, { "POP", 0, 1, 0, false, Tier::BaseTier } }, - { Instruction::MLOAD, { "MLOAD", 0, 1, 1, false, Tier::VeryLowTier } }, - { Instruction::MSTORE, { "MSTORE", 0, 2, 0, true, Tier::VeryLowTier } }, - { Instruction::MSTORE8, { "MSTORE8", 0, 2, 0, true, Tier::VeryLowTier } }, - { Instruction::SLOAD, { "SLOAD", 0, 1, 1, false, Tier::SpecialTier } }, - { Instruction::SSTORE, { "SSTORE", 0, 2, 0, true, Tier::SpecialTier } }, - { Instruction::JUMP, { "JUMP", 0, 1, 0, true, Tier::MidTier } }, - { Instruction::JUMPI, { "JUMPI", 0, 2, 0, true, Tier::HighTier } }, - { Instruction::PC, { "PC", 0, 0, 1, false, Tier::BaseTier } }, - { Instruction::MSIZE, { "MSIZE", 0, 0, 1, false, Tier::BaseTier } }, - { Instruction::GAS, { "GAS", 0, 0, 1, false, Tier::BaseTier } }, - { Instruction::JUMPDEST, { "JUMPDEST", 0, 0, 0, true, Tier::SpecialTier } }, - { Instruction::PUSH1, { "PUSH1", 1, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH2, { "PUSH2", 2, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH3, { "PUSH3", 3, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH4, { "PUSH4", 4, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH5, { "PUSH5", 5, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH6, { "PUSH6", 6, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH7, { "PUSH7", 7, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH8, { "PUSH8", 8, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH9, { "PUSH9", 9, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH10, { "PUSH10", 10, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH11, { "PUSH11", 11, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH12, { "PUSH12", 12, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH13, { "PUSH13", 13, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH14, { "PUSH14", 14, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH15, { "PUSH15", 15, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH16, { "PUSH16", 16, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH17, { "PUSH17", 17, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH18, { "PUSH18", 18, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH19, { "PUSH19", 19, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH20, { "PUSH20", 20, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH21, { "PUSH21", 21, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH22, { "PUSH22", 22, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH23, { "PUSH23", 23, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH24, { "PUSH24", 24, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH25, { "PUSH25", 25, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH26, { "PUSH26", 26, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH27, { "PUSH27", 27, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH28, { "PUSH28", 28, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH29, { "PUSH29", 29, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH30, { "PUSH30", 30, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH31, { "PUSH31", 31, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::PUSH32, { "PUSH32", 32, 0, 1, false, Tier::VeryLowTier } }, - { Instruction::DUP1, { "DUP1", 0, 1, 2, false, Tier::VeryLowTier } }, - { Instruction::DUP2, { "DUP2", 0, 2, 3, false, Tier::VeryLowTier } }, - { Instruction::DUP3, { "DUP3", 0, 3, 4, false, Tier::VeryLowTier } }, - { Instruction::DUP4, { "DUP4", 0, 4, 5, false, Tier::VeryLowTier } }, - { Instruction::DUP5, { "DUP5", 0, 5, 6, false, Tier::VeryLowTier } }, - { Instruction::DUP6, { "DUP6", 0, 6, 7, false, Tier::VeryLowTier } }, - { Instruction::DUP7, { "DUP7", 0, 7, 8, false, Tier::VeryLowTier } }, - { Instruction::DUP8, { "DUP8", 0, 8, 9, false, Tier::VeryLowTier } }, - { Instruction::DUP9, { "DUP9", 0, 9, 10, false, Tier::VeryLowTier } }, - { Instruction::DUP10, { "DUP10", 0, 10, 11, false, Tier::VeryLowTier } }, - { Instruction::DUP11, { "DUP11", 0, 11, 12, false, Tier::VeryLowTier } }, - { Instruction::DUP12, { "DUP12", 0, 12, 13, false, Tier::VeryLowTier } }, - { Instruction::DUP13, { "DUP13", 0, 13, 14, false, Tier::VeryLowTier } }, - { Instruction::DUP14, { "DUP14", 0, 14, 15, false, Tier::VeryLowTier } }, - { Instruction::DUP15, { "DUP15", 0, 15, 16, false, Tier::VeryLowTier } }, - { Instruction::DUP16, { "DUP16", 0, 16, 17, false, Tier::VeryLowTier } }, - { Instruction::SWAP1, { "SWAP1", 0, 2, 2, false, Tier::VeryLowTier } }, - { Instruction::SWAP2, { "SWAP2", 0, 3, 3, false, Tier::VeryLowTier } }, - { Instruction::SWAP3, { "SWAP3", 0, 4, 4, false, Tier::VeryLowTier } }, - { Instruction::SWAP4, { "SWAP4", 0, 5, 5, false, Tier::VeryLowTier } }, - { Instruction::SWAP5, { "SWAP5", 0, 6, 6, false, Tier::VeryLowTier } }, - { Instruction::SWAP6, { "SWAP6", 0, 7, 7, false, Tier::VeryLowTier } }, - { Instruction::SWAP7, { "SWAP7", 0, 8, 8, false, Tier::VeryLowTier } }, - { Instruction::SWAP8, { "SWAP8", 0, 9, 9, false, Tier::VeryLowTier } }, - { Instruction::SWAP9, { "SWAP9", 0, 10, 10, false, Tier::VeryLowTier } }, - { Instruction::SWAP10, { "SWAP10", 0, 11, 11, false, Tier::VeryLowTier } }, - { Instruction::SWAP11, { "SWAP11", 0, 12, 12, false, Tier::VeryLowTier } }, - { Instruction::SWAP12, { "SWAP12", 0, 13, 13, false, Tier::VeryLowTier } }, - { Instruction::SWAP13, { "SWAP13", 0, 14, 14, false, Tier::VeryLowTier } }, - { Instruction::SWAP14, { "SWAP14", 0, 15, 15, false, Tier::VeryLowTier } }, - { Instruction::SWAP15, { "SWAP15", 0, 16, 16, false, Tier::VeryLowTier } }, - { Instruction::SWAP16, { "SWAP16", 0, 17, 17, false, Tier::VeryLowTier } }, - { Instruction::LOG0, { "LOG0", 0, 2, 0, true, Tier::SpecialTier } }, - { Instruction::LOG1, { "LOG1", 0, 3, 0, true, Tier::SpecialTier } }, - { Instruction::LOG2, { "LOG2", 0, 4, 0, true, Tier::SpecialTier } }, - { Instruction::LOG3, { "LOG3", 0, 5, 0, true, Tier::SpecialTier } }, - { Instruction::LOG4, { "LOG4", 0, 6, 0, true, Tier::SpecialTier } }, - { Instruction::CREATE, { "CREATE", 0, 3, 1, true, Tier::SpecialTier } }, - { Instruction::CALL, { "CALL", 0, 7, 1, true, Tier::SpecialTier } }, - { Instruction::CALLCODE, { "CALLCODE", 0, 7, 1, true, Tier::SpecialTier } }, - { Instruction::RETURN, { "RETURN", 0, 2, 0, true, Tier::ZeroTier } }, - { Instruction::DELEGATECALL,{ "DELEGATECALL", 0, 6, 1, true, Tier::SpecialTier } }, - { Instruction::SUICIDE, { "SUICIDE", 0, 1, 0, true, Tier::ZeroTier } } + { Instruction::STOP, { "STOP", 0, 0, 0, true, Tier::Zero } }, + { Instruction::ADD, { "ADD", 0, 2, 1, false, Tier::VeryLow } }, + { Instruction::SUB, { "SUB", 0, 2, 1, false, Tier::VeryLow } }, + { Instruction::MUL, { "MUL", 0, 2, 1, false, Tier::Low } }, + { Instruction::DIV, { "DIV", 0, 2, 1, false, Tier::Low } }, + { Instruction::SDIV, { "SDIV", 0, 2, 1, false, Tier::Low } }, + { Instruction::MOD, { "MOD", 0, 2, 1, false, Tier::Low } }, + { Instruction::SMOD, { "SMOD", 0, 2, 1, false, Tier::Low } }, + { Instruction::EXP, { "EXP", 0, 2, 1, false, Tier::Special } }, + { Instruction::NOT, { "NOT", 0, 1, 1, false, Tier::VeryLow } }, + { Instruction::LT, { "LT", 0, 2, 1, false, Tier::VeryLow } }, + { Instruction::GT, { "GT", 0, 2, 1, false, Tier::VeryLow } }, + { Instruction::SLT, { "SLT", 0, 2, 1, false, Tier::VeryLow } }, + { Instruction::SGT, { "SGT", 0, 2, 1, false, Tier::VeryLow } }, + { Instruction::EQ, { "EQ", 0, 2, 1, false, Tier::VeryLow } }, + { Instruction::ISZERO, { "ISZERO", 0, 1, 1, false, Tier::VeryLow } }, + { Instruction::AND, { "AND", 0, 2, 1, false, Tier::VeryLow } }, + { Instruction::OR, { "OR", 0, 2, 1, false, Tier::VeryLow } }, + { Instruction::XOR, { "XOR", 0, 2, 1, false, Tier::VeryLow } }, + { Instruction::BYTE, { "BYTE", 0, 2, 1, false, Tier::VeryLow } }, + { Instruction::ADDMOD, { "ADDMOD", 0, 3, 1, false, Tier::Mid } }, + { Instruction::MULMOD, { "MULMOD", 0, 3, 1, false, Tier::Mid } }, + { Instruction::SIGNEXTEND, { "SIGNEXTEND", 0, 2, 1, false, Tier::Low } }, + { Instruction::SHA3, { "SHA3", 0, 2, 1, false, Tier::Special } }, + { Instruction::ADDRESS, { "ADDRESS", 0, 0, 1, false, Tier::Base } }, + { Instruction::BALANCE, { "BALANCE", 0, 1, 1, false, Tier::Ext } }, + { Instruction::ORIGIN, { "ORIGIN", 0, 0, 1, false, Tier::Base } }, + { Instruction::CALLER, { "CALLER", 0, 0, 1, false, Tier::Base } }, + { Instruction::CALLVALUE, { "CALLVALUE", 0, 0, 1, false, Tier::Base } }, + { Instruction::CALLDATALOAD,{ "CALLDATALOAD", 0, 1, 1, false, Tier::VeryLow } }, + { Instruction::CALLDATASIZE,{ "CALLDATASIZE", 0, 0, 1, false, Tier::Base } }, + { Instruction::CALLDATACOPY,{ "CALLDATACOPY", 0, 3, 0, true, Tier::VeryLow } }, + { Instruction::CODESIZE, { "CODESIZE", 0, 0, 1, false, Tier::Base } }, + { Instruction::CODECOPY, { "CODECOPY", 0, 3, 0, true, Tier::VeryLow } }, + { Instruction::GASPRICE, { "GASPRICE", 0, 0, 1, false, Tier::Base } }, + { Instruction::EXTCODESIZE, { "EXTCODESIZE", 0, 1, 1, false, Tier::Ext } }, + { Instruction::EXTCODECOPY, { "EXTCODECOPY", 0, 4, 0, true, Tier::Ext } }, + { 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 } }, + { Instruction::NUMBER, { "NUMBER", 0, 0, 1, false, Tier::Base } }, + { Instruction::DIFFICULTY, { "DIFFICULTY", 0, 0, 1, false, Tier::Base } }, + { Instruction::GASLIMIT, { "GASLIMIT", 0, 0, 1, false, Tier::Base } }, + { Instruction::POP, { "POP", 0, 1, 0, false, Tier::Base } }, + { Instruction::MLOAD, { "MLOAD", 0, 1, 1, false, Tier::VeryLow } }, + { Instruction::MSTORE, { "MSTORE", 0, 2, 0, true, Tier::VeryLow } }, + { Instruction::MSTORE8, { "MSTORE8", 0, 2, 0, true, Tier::VeryLow } }, + { Instruction::SLOAD, { "SLOAD", 0, 1, 1, false, Tier::Special } }, + { Instruction::SSTORE, { "SSTORE", 0, 2, 0, true, Tier::Special } }, + { Instruction::JUMP, { "JUMP", 0, 1, 0, true, Tier::Mid } }, + { Instruction::JUMPI, { "JUMPI", 0, 2, 0, true, Tier::High } }, + { Instruction::PC, { "PC", 0, 0, 1, false, Tier::Base } }, + { Instruction::MSIZE, { "MSIZE", 0, 0, 1, false, Tier::Base } }, + { Instruction::GAS, { "GAS", 0, 0, 1, false, Tier::Base } }, + { Instruction::JUMPDEST, { "JUMPDEST", 0, 0, 0, true, Tier::Special } }, + { Instruction::PUSH1, { "PUSH1", 1, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH2, { "PUSH2", 2, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH3, { "PUSH3", 3, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH4, { "PUSH4", 4, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH5, { "PUSH5", 5, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH6, { "PUSH6", 6, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH7, { "PUSH7", 7, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH8, { "PUSH8", 8, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH9, { "PUSH9", 9, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH10, { "PUSH10", 10, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH11, { "PUSH11", 11, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH12, { "PUSH12", 12, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH13, { "PUSH13", 13, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH14, { "PUSH14", 14, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH15, { "PUSH15", 15, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH16, { "PUSH16", 16, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH17, { "PUSH17", 17, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH18, { "PUSH18", 18, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH19, { "PUSH19", 19, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH20, { "PUSH20", 20, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH21, { "PUSH21", 21, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH22, { "PUSH22", 22, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH23, { "PUSH23", 23, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH24, { "PUSH24", 24, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH25, { "PUSH25", 25, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH26, { "PUSH26", 26, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH27, { "PUSH27", 27, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH28, { "PUSH28", 28, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH29, { "PUSH29", 29, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH30, { "PUSH30", 30, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH31, { "PUSH31", 31, 0, 1, false, Tier::VeryLow } }, + { Instruction::PUSH32, { "PUSH32", 32, 0, 1, false, Tier::VeryLow } }, + { Instruction::DUP1, { "DUP1", 0, 1, 2, false, Tier::VeryLow } }, + { Instruction::DUP2, { "DUP2", 0, 2, 3, false, Tier::VeryLow } }, + { Instruction::DUP3, { "DUP3", 0, 3, 4, false, Tier::VeryLow } }, + { Instruction::DUP4, { "DUP4", 0, 4, 5, false, Tier::VeryLow } }, + { Instruction::DUP5, { "DUP5", 0, 5, 6, false, Tier::VeryLow } }, + { Instruction::DUP6, { "DUP6", 0, 6, 7, false, Tier::VeryLow } }, + { Instruction::DUP7, { "DUP7", 0, 7, 8, false, Tier::VeryLow } }, + { Instruction::DUP8, { "DUP8", 0, 8, 9, false, Tier::VeryLow } }, + { Instruction::DUP9, { "DUP9", 0, 9, 10, false, Tier::VeryLow } }, + { Instruction::DUP10, { "DUP10", 0, 10, 11, false, Tier::VeryLow } }, + { Instruction::DUP11, { "DUP11", 0, 11, 12, false, Tier::VeryLow } }, + { Instruction::DUP12, { "DUP12", 0, 12, 13, false, Tier::VeryLow } }, + { Instruction::DUP13, { "DUP13", 0, 13, 14, false, Tier::VeryLow } }, + { Instruction::DUP14, { "DUP14", 0, 14, 15, false, Tier::VeryLow } }, + { Instruction::DUP15, { "DUP15", 0, 15, 16, false, Tier::VeryLow } }, + { Instruction::DUP16, { "DUP16", 0, 16, 17, false, Tier::VeryLow } }, + { Instruction::SWAP1, { "SWAP1", 0, 2, 2, false, Tier::VeryLow } }, + { Instruction::SWAP2, { "SWAP2", 0, 3, 3, false, Tier::VeryLow } }, + { Instruction::SWAP3, { "SWAP3", 0, 4, 4, false, Tier::VeryLow } }, + { Instruction::SWAP4, { "SWAP4", 0, 5, 5, false, Tier::VeryLow } }, + { Instruction::SWAP5, { "SWAP5", 0, 6, 6, false, Tier::VeryLow } }, + { Instruction::SWAP6, { "SWAP6", 0, 7, 7, false, Tier::VeryLow } }, + { Instruction::SWAP7, { "SWAP7", 0, 8, 8, false, Tier::VeryLow } }, + { Instruction::SWAP8, { "SWAP8", 0, 9, 9, false, Tier::VeryLow } }, + { Instruction::SWAP9, { "SWAP9", 0, 10, 10, false, Tier::VeryLow } }, + { Instruction::SWAP10, { "SWAP10", 0, 11, 11, false, Tier::VeryLow } }, + { Instruction::SWAP11, { "SWAP11", 0, 12, 12, false, Tier::VeryLow } }, + { Instruction::SWAP12, { "SWAP12", 0, 13, 13, false, Tier::VeryLow } }, + { Instruction::SWAP13, { "SWAP13", 0, 14, 14, false, Tier::VeryLow } }, + { Instruction::SWAP14, { "SWAP14", 0, 15, 15, false, Tier::VeryLow } }, + { Instruction::SWAP15, { "SWAP15", 0, 16, 16, false, Tier::VeryLow } }, + { Instruction::SWAP16, { "SWAP16", 0, 17, 17, false, Tier::VeryLow } }, + { Instruction::LOG0, { "LOG0", 0, 2, 0, true, Tier::Special } }, + { Instruction::LOG1, { "LOG1", 0, 3, 0, true, Tier::Special } }, + { Instruction::LOG2, { "LOG2", 0, 4, 0, true, Tier::Special } }, + { Instruction::LOG3, { "LOG3", 0, 5, 0, true, Tier::Special } }, + { Instruction::LOG4, { "LOG4", 0, 6, 0, true, Tier::Special } }, + { Instruction::CREATE, { "CREATE", 0, 3, 1, true, Tier::Special } }, + { Instruction::CALL, { "CALL", 0, 7, 1, true, Tier::Special } }, + { Instruction::CALLCODE, { "CALLCODE", 0, 7, 1, true, Tier::Special } }, + { Instruction::RETURN, { "RETURN", 0, 2, 0, true, Tier::Zero } }, + { Instruction::DELEGATECALL,{ "DELEGATECALL", 0, 6, 1, true, Tier::Special } }, + { Instruction::SUICIDE, { "SUICIDE", 0, 1, 0, true, Tier::Zero } } }; void dev::solidity::eachInstruction( @@ -343,7 +343,7 @@ InstructionInfo dev::solidity::instructionInfo(Instruction _inst) } catch (...) { - return InstructionInfo({"", 0, 0, 0, false, Tier::InvalidTier}); + return InstructionInfo({"", 0, 0, 0, false, Tier::Invalid}); } } diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index 4ebaeb987..2dd451cdf 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -225,17 +225,17 @@ inline Instruction logInstruction(unsigned _number) return Instruction(unsigned(Instruction::LOG0) + _number); } -enum class Tier : int +enum class Tier : unsigned { - ZeroTier = 0, // 0, Zero - BaseTier, // 2, Quick - VeryLowTier, // 3, Fastest - LowTier, // 5, Fast - MidTier, // 8, Mid - HighTier, // 10, Slow - ExtTier, // 20, Ext - SpecialTier, // multiparam or otherwise special - InvalidTier // Invalid. + Zero = 0, // 0, Zero + Base, // 2, Quick + VeryLow, // 3, Fastest + Low, // 5, Fast + Mid, // 8, Mid + High, // 10, Slow + Ext, // 20, Ext + Special, // multiparam or otherwise special + Invalid // Invalid. }; /// Information structure for a particular instruction. From d3a391c13651569a3154a84dcd62fecf69fc074c Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 18 Jan 2017 17:41:36 +0100 Subject: [PATCH 037/118] Provide fallback for linking. --- Changelog.md | 4 +--- libevmasm/LinkerObject.cpp | 29 ++++++++++++++++++----- libevmasm/LinkerObject.h | 6 +++++ test/libsolidity/SolidityEndToEndTest.cpp | 24 +++++++++---------- 4 files changed, 42 insertions(+), 21 deletions(-) diff --git a/Changelog.md b/Changelog.md index 1d3c47c12..1e5dd8e1c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.4.9 (unreleased) Features: + * Compiler Interface: Contracts and libraries can be referenced with a `file:` prefix to make them unique. * AST: Use deterministic node identifiers. * Metadata: Do not include platform in the version number. @@ -16,9 +17,6 @@ Bugfixes: * Imports: ``import ".dir/a"`` is not a relative path. Relative paths begin with directory ``.`` or ``..``. * Type checker, disallow inheritances of different kinds (e.g. a function and a modifier) of members of the same name -Features: - * Contracts and libraries are now unique to their source files, rather than globally. - ### 0.4.7 (2016-12-15) Features: diff --git a/libevmasm/LinkerObject.cpp b/libevmasm/LinkerObject.cpp index 93e4067c9..06607089e 100644 --- a/libevmasm/LinkerObject.cpp +++ b/libevmasm/LinkerObject.cpp @@ -37,13 +37,10 @@ void LinkerObject::link(map const& _libraryAddresses) { std::map remainingRefs; for (auto const& linkRef: linkReferences) - { - auto it = _libraryAddresses.find(linkRef.second); - if (it == _libraryAddresses.end()) - remainingRefs.insert(linkRef); + if (h160 const* address = matchLibrary(linkRef.second, _libraryAddresses)) + address->ref().copyTo(ref(bytecode).cropped(linkRef.first, 20)); else - it->second.ref().copyTo(ref(bytecode).cropped(linkRef.first, 20)); - } + remainingRefs.insert(linkRef); linkReferences.swap(remainingRefs); } @@ -60,3 +57,23 @@ string LinkerObject::toHex() const } return hex; } + +h160 const* +LinkerObject::matchLibrary( + string const& _linkRefName, + map const& _libraryAddresses +) +{ + auto it = _libraryAddresses.find(_linkRefName); + if (it != _libraryAddresses.end()) + return &it->second; + // If the user did not supply a fully qualified library name, + // try to match only the simple libary name + size_t colon = _linkRefName.find(':'); + if (colon == string::npos) + return nullptr; + it = _libraryAddresses.find(_linkRefName.substr(colon + 1)); + if (it != _libraryAddresses.end()) + return &it->second; + return nullptr; +} diff --git a/libevmasm/LinkerObject.h b/libevmasm/LinkerObject.h index d3ec3e972..152487b44 100644 --- a/libevmasm/LinkerObject.h +++ b/libevmasm/LinkerObject.h @@ -49,6 +49,12 @@ struct LinkerObject /// @returns a hex representation of the bytecode of the given object, replacing unlinked /// addresses by placeholders. std::string toHex() const; + +private: + static h160 const* matchLibrary( + std::string const& _linkRefName, + std::map const& _libraryAddresses + ); }; } diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 03aa26999..191618317 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -3192,7 +3192,7 @@ BOOST_AUTO_TEST_CASE(library_call_in_homestead) } )"; compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{":Lib", m_contractAddress}}); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); BOOST_CHECK(callContractFunction("f()") == encodeArgs()); BOOST_CHECK(callContractFunction("sender()") == encodeArgs(u160(m_sender))); } @@ -6191,7 +6191,7 @@ BOOST_AUTO_TEST_CASE(library_call) } )"; compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{":Lib", m_contractAddress}}); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); BOOST_CHECK(callContractFunction("f(uint256)", u256(33)) == encodeArgs(u256(33) * 9)); } @@ -6208,7 +6208,7 @@ BOOST_AUTO_TEST_CASE(library_stray_values) } )"; compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{":Lib", m_contractAddress}}); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); BOOST_CHECK(callContractFunction("f(uint256)", u256(33)) == encodeArgs(u256(42))); } @@ -6341,7 +6341,7 @@ BOOST_AUTO_TEST_CASE(internal_types_in_library) } )"; compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{":Lib", m_contractAddress}}); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(4), u256(17))); } @@ -6368,7 +6368,7 @@ BOOST_AUTO_TEST_CASE(using_library_structs) } )"; compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{":Lib", m_contractAddress}}); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(7), u256(8))); } @@ -6902,7 +6902,7 @@ BOOST_AUTO_TEST_CASE(using_for_function_on_int) } )"; compileAndRun(sourceCode, 0, "D"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{":D", m_contractAddress}}); + compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); BOOST_CHECK(callContractFunction("f(uint256)", u256(9)) == encodeArgs(u256(2 * 9))); } @@ -6920,7 +6920,7 @@ BOOST_AUTO_TEST_CASE(using_for_function_on_struct) } )"; compileAndRun(sourceCode, 0, "D"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{":D", m_contractAddress}}); + compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); BOOST_CHECK(callContractFunction("f(uint256)", u256(7)) == encodeArgs(u256(3 * 7))); BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(3 * 7))); } @@ -6943,7 +6943,7 @@ BOOST_AUTO_TEST_CASE(using_for_overload) } )"; compileAndRun(sourceCode, 0, "D"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{":D", m_contractAddress}}); + compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); BOOST_CHECK(callContractFunction("f(uint256)", u256(7)) == encodeArgs(u256(6 * 7))); BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(6 * 7))); } @@ -6962,7 +6962,7 @@ BOOST_AUTO_TEST_CASE(using_for_by_name) } )"; compileAndRun(sourceCode, 0, "D"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{":D", m_contractAddress}}); + compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); BOOST_CHECK(callContractFunction("f(uint256)", u256(7)) == encodeArgs(u256(6 * 7))); BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(6 * 7))); } @@ -6982,7 +6982,7 @@ BOOST_AUTO_TEST_CASE(bound_function_in_var) } )"; compileAndRun(sourceCode, 0, "D"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{":D", m_contractAddress}}); + compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); BOOST_CHECK(callContractFunction("f(uint256)", u256(7)) == encodeArgs(u256(6 * 7))); BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(6 * 7))); } @@ -7005,7 +7005,7 @@ BOOST_AUTO_TEST_CASE(bound_function_to_string) } )"; compileAndRun(sourceCode, 0, "D"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{":D", m_contractAddress}}); + compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(3))); BOOST_CHECK(callContractFunction("g()") == encodeArgs(u256(3))); } @@ -7751,7 +7751,7 @@ BOOST_AUTO_TEST_CASE(payable_function_calls_library) } )"; compileAndRun(sourceCode, 0, "L"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{":L", m_contractAddress}}); + compileAndRun(sourceCode, 0, "C", bytes(), map{{"L", m_contractAddress}}); BOOST_CHECK(callContractFunctionWithValue("f()", 27) == encodeArgs(u256(7))); } From 23a654ade8bb20fc2768e88b77059a14dce21635 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 19 Jan 2017 11:10:09 +0100 Subject: [PATCH 038/118] Fix default function type name visibility. --- libsolidity/analysis/ReferencesResolver.cpp | 1 - libsolidity/ast/AST.h | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 66bf1d0e1..df579c3d9 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -87,7 +87,6 @@ void ReferencesResolver::endVisit(FunctionTypeName const& _typeName) { switch (_typeName.visibility()) { - case VariableDeclaration::Visibility::Default: case VariableDeclaration::Visibility::Internal: case VariableDeclaration::Visibility::External: break; diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 4af649634..6e81892b4 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -867,7 +867,10 @@ public: std::vector> const& parameterTypes() const { return m_parameterTypes->parameters(); } std::vector> const& returnParameterTypes() const { return m_returnTypes->parameters(); } - Declaration::Visibility visibility() const { return m_visibility; } + Declaration::Visibility visibility() const + { + return m_visibility == Declaration::Visibility::Default ? Declaration::Visibility::Internal : m_visibility; + } bool isDeclaredConst() const { return m_isDeclaredConst; } bool isPayable() const { return m_isPayable; } From 3fed790a56cea7bae481d01c15949aa500823968 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 17 Jan 2017 10:47:44 +0100 Subject: [PATCH 039/118] Type identifiers. --- Changelog.md | 1 + libsolidity/ast/Types.cpp | 189 ++++++++++++++++++++++++++++- libsolidity/ast/Types.h | 24 ++++ test/libsolidity/SolidityTypes.cpp | 66 ++++++++++ 4 files changed, 277 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 1e5dd8e1c..b9e4ecc03 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ Features: * Compiler Interface: Contracts and libraries can be referenced with a `file:` prefix to make them unique. * AST: Use deterministic node identifiers. + * Type system: Introduce type identifier strings. * Metadata: Do not include platform in the version number. ### 0.4.8 (2017-01-13) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 03ff84718..9e7224741 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -272,7 +272,15 @@ IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier): solAssert( m_bits > 0 && m_bits <= 256 && m_bits % 8 == 0, "Invalid bit number for integer type: " + dev::toString(_bits) - ); + ); +} + +string IntegerType::identifier() const +{ + if (isAddress()) + return "t_address"; + else + return "t_" + string(isSigned() ? "" : "u") + "int" + std::to_string(numBits()); } bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const @@ -412,7 +420,12 @@ FixedPointType::FixedPointType(int _integerBits, int _fractionalBits, FixedPoint m_fractionalBits % 8 == 0, "Invalid bit number(s) for fixed type: " + dev::toString(_integerBits) + "x" + dev::toString(_fractionalBits) - ); + ); +} + +string FixedPointType::identifier() const +{ + return "t_" + string(isSigned() ? "" : "u") + "fixed" + std::to_string(integerBits()) + "x" + std::to_string(fractionalBits()); } bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const @@ -770,6 +783,11 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ } } +string RationalNumberType::identifier() const +{ + return "t_rational_" + m_value.numerator().str() + "_by_" + m_value.denominator().str(); +} + bool RationalNumberType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -909,6 +927,13 @@ bool StringLiteralType::isImplicitlyConvertibleTo(Type const& _convertTo) const return false; } +string StringLiteralType::identifier() const +{ + // Since we have to return a valid identifier and the string itself may contain + // anything, we hash it. + return "t_stringliteral_" + toHex(keccak256(m_value).asBytes()); +} + bool StringLiteralType::operator==(const Type& _other) const { if (_other.category() != category()) @@ -1002,6 +1027,11 @@ MemberList::MemberMap FixedBytesType::nativeMembers(const ContractDefinition*) c return MemberList::MemberMap{MemberList::Member{"length", make_shared(8)}}; } +string FixedBytesType::identifier() const +{ + return "t_bytes" + std::to_string(m_bytes); +} + bool FixedBytesType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -1115,6 +1145,20 @@ string ReferenceType::stringForReferencePart() const return ""; } +string ReferenceType::identifierLocationSuffix() const +{ + string id; + if (location() == DataLocation::Storage) + id += "_storage"; + else if (location() == DataLocation::Memory) + id += "_memory"; + else + id += "_calldata"; + if (isPointer()) + id += "_ptr"; + return id; +} + bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const { if (_convertTo.category() != category()) @@ -1170,6 +1214,26 @@ bool ArrayType::isExplicitlyConvertibleTo(const Type& _convertTo) const return true; } +string ArrayType::identifier() const +{ + string id; + if (isString()) + id += "t_string"; + else if (isByteArray()) + id += "t_bytes"; + else + { + id = baseType()->identifier(); + if (isDynamicallySized()) + id += "_arraydyn"; + else + id += string("_array") + length().str(); + } + id += identifierLocationSuffix(); + + return id; +} + bool ArrayType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -1184,7 +1248,7 @@ bool ArrayType::operator==(Type const& _other) const return false; if (*other.baseType() != *baseType()) return false; - return isDynamicallySized() || length() == other.length(); + return isDynamicallySized() || length() == other.length(); } unsigned ArrayType::calldataEncodedSize(bool _padded) const @@ -1356,6 +1420,11 @@ TypePointer ArrayType::copyForLocation(DataLocation _location, bool _isPointer) return copy; } +string ContractType::identifier() const +{ + return (m_super ? "t_super_" : "t_contract_") + m_contract.name() + "_" + std::to_string(m_contract.id()); +} + bool ContractType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -1465,6 +1534,11 @@ bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const return this->m_struct == convertTo.m_struct; } +string StructType::identifier() const +{ + return "t_struct_" + m_struct.name() + "_" + std::to_string(m_struct.id()) + identifierLocationSuffix(); +} + bool StructType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -1605,6 +1679,11 @@ TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const return _operator == Token::Delete ? make_shared() : TypePointer(); } +string EnumType::identifier() const +{ + return "t_enum_" + m_enum.name() + "_" + std::to_string(m_enum.id()); +} + bool EnumType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -1686,6 +1765,18 @@ bool TupleType::isImplicitlyConvertibleTo(Type const& _other) const return false; } +string TupleType::identifier() const +{ + string id = "t_tuple" + std::to_string(components().size()) + "_"; + for (auto const& c: components()) + if (c) + id += c->identifier() + "_"; + else + id += "t_empty_"; + id += "tuple_end"; + return id; +} + bool TupleType::operator==(Type const& _other) const { if (auto tupleType = dynamic_cast(&_other)) @@ -1934,6 +2025,59 @@ TypePointers FunctionType::parameterTypes() const return TypePointers(m_parameterTypes.cbegin() + 1, m_parameterTypes.cend()); } +string FunctionType::identifier() const +{ + string id = "t_function_"; + switch (location()) + { + case Location::Internal: id += "internal"; break; + case Location::External: id += "external"; break; + case Location::CallCode: id += "callcode"; break; + case Location::DelegateCall: id += "delegatecall"; break; + case Location::Bare: id += "bare"; break; + case Location::BareCallCode: id += "barecallcode"; break; + case Location::BareDelegateCall: id += "baredelegatecall"; break; + case Location::Creation: id += "creation"; break; + case Location::Send: id += "send"; break; + case Location::SHA3: id += "sha3"; break; + case Location::Selfdestruct: id += "selfdestruct"; break; + case Location::ECRecover: id += "ecrecover"; break; + case Location::SHA256: id += "sha256"; break; + case Location::RIPEMD160: id += "ripemd160"; break; + case Location::Log0: id += "log0"; break; + case Location::Log1: id += "log1"; break; + case Location::Log2: id += "log2"; break; + case Location::Log3: id += "log3"; break; + case Location::Log4: id += "log4"; break; + case Location::Event: id += "event"; break; + case Location::SetGas: id += "setgas"; break; + case Location::SetValue: id += "setvalue"; break; + case Location::BlockHash: id += "blockhash"; break; + case Location::AddMod: id += "addmod"; break; + case Location::MulMod: id += "mulmod"; break; + case Location::ArrayPush: id += "arraypush"; break; + case Location::ByteArrayPush: id += "bytearraypush"; break; + case Location::ObjectCreation: id += "objectcreation"; break; + default: solAssert(false, "Unknown function location."); break; + } + if (isConstant()) + id += "_constant"; + id += "_param" + std::to_string(m_parameterTypes.size()) + "_"; + for (auto const& p: m_parameterTypes) + id += p->identifier() + "_"; + id += "return" + std::to_string(m_returnParameterTypes.size()) + "_"; + for (auto const& r: m_returnParameterTypes) + id += r->identifier() + "_"; + if (m_gasSet) + id += "gas_set_"; + if (m_valueSet) + id += "value_set_"; + if (bound()) + id += "bound_to" + selfType()->identifier() + "_"; + id += "function_end"; + return id; +} + bool FunctionType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -2354,6 +2498,11 @@ ASTPointer FunctionType::documentation() const return ASTPointer(); } +string MappingType::identifier() const +{ + return "t_mapping_" + m_keyType->identifier() + "_to_" + m_valueType->identifier() + "_mapping_end"; +} + bool MappingType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -2372,6 +2521,11 @@ string MappingType::canonicalName(bool) const return "mapping(" + keyType()->canonicalName(false) + " => " + valueType()->canonicalName(false) + ")"; } +string TypeType::identifier() const +{ + return "t_type_" + actualType()->identifier(); +} + bool TypeType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -2456,6 +2610,14 @@ u256 ModifierType::storageSize() const << errinfo_comment("Storage size of non-storable type type requested.")); } +string ModifierType::identifier() const +{ + string id = "t_modifier_param" + std::to_string(m_parameterTypes.size()) + "_"; + for (auto const& p: m_parameterTypes) + id += p->identifier() + "_"; + return id + "end_modifier"; +} + bool ModifierType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -2480,6 +2642,11 @@ string ModifierType::toString(bool _short) const return name + ")"; } +string ModuleType::identifier() const +{ + return "t_module_" + std::to_string(m_sourceUnit.id()); +} + bool ModuleType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -2501,6 +2668,22 @@ string ModuleType::toString(bool) const return string("module \"") + m_sourceUnit.annotation().path + string("\""); } +string MagicType::identifier() const +{ + switch (m_kind) + { + case Kind::Block: + return "t_magic_block"; + case Kind::Message: + return "t_magic_message"; + case Kind::Transaction: + return "t_magic_transaction"; + default: + solAssert(false, "Unknown kind of magic"); + } + return ""; +} + bool MagicType::operator==(Type const& _other) const { if (_other.category() != category()) diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 26e2b8f29..c1f6a476a 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -155,6 +155,10 @@ public: static TypePointer commonType(TypePointer const& _a, TypePointer const& _b); virtual Category category() const = 0; + /// @returns a valid solidity identifier such that two types should compare equal if and + /// only if they have the same identifier. + /// The identifier should start with "t_". + virtual std::string identifier() const = 0; virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; } virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const { @@ -288,6 +292,7 @@ public: explicit IntegerType(int _bits, Modifier _modifier = Modifier::Unsigned); + virtual std::string identifier() const override; virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; @@ -329,6 +334,7 @@ public: explicit FixedPointType(int _integerBits, int _fractionalBits, Modifier _modifier = Modifier::Unsigned); + virtual std::string identifier() const override; virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; @@ -378,6 +384,7 @@ public: virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } @@ -416,6 +423,7 @@ public: return TypePointer(); } + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } @@ -449,6 +457,7 @@ public: virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; @@ -476,6 +485,7 @@ class BoolType: public Type public: BoolType() {} virtual Category category() const override { return Category::Bool; } + virtual std::string identifier() const override { return "t_bool"; } virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; @@ -533,6 +543,8 @@ protected: TypePointer copyForLocationIfReference(TypePointer const& _type) const; /// @returns a human-readable description of the reference part of the type. std::string stringForReferencePart() const; + /// @returns the suffix computed from the reference part to be used by identifier(); + std::string identifierLocationSuffix() const; DataLocation m_location = DataLocation::Storage; bool m_isPointer = true; @@ -573,6 +585,7 @@ public: virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual std::string identifier() const override; virtual bool operator==(const Type& _other) const override; virtual unsigned calldataEncodedSize(bool _padded) const override; virtual bool isDynamicallySized() const override { return m_hasDynamicLength; } @@ -622,6 +635,7 @@ public: /// Contracts can be converted to themselves and to integers. virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual unsigned calldataEncodedSize(bool _padded ) const override { @@ -677,6 +691,7 @@ public: explicit StructType(StructDefinition const& _struct, DataLocation _location = DataLocation::Storage): ReferenceType(_location), m_struct(_struct) {} virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override; + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual unsigned calldataEncodedSize(bool _padded) const override; u256 memorySize() const; @@ -720,6 +735,7 @@ public: virtual Category category() const override { return Category::Enum; } explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {} virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual unsigned calldataEncodedSize(bool _padded) const override { @@ -760,6 +776,7 @@ public: virtual Category category() const override { return Category::Tuple; } explicit TupleType(std::vector const& _types = std::vector()): m_components(_types) {} virtual bool isImplicitlyConvertibleTo(Type const& _other) const override; + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } virtual std::string toString(bool) const override; @@ -897,6 +914,7 @@ public: /// @returns the "self" parameter type for a bound function TypePointer selfType() const; + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual std::string canonicalName(bool /*_addDataLocation*/) const override; @@ -995,6 +1013,7 @@ public: MappingType(TypePointer const& _keyType, TypePointer const& _valueType): m_keyType(_keyType), m_valueType(_valueType) {} + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual std::string toString(bool _short) const override; virtual std::string canonicalName(bool _addDataLocation) const override; @@ -1029,6 +1048,7 @@ public: TypePointer const& actualType() const { return m_actualType; } virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } virtual u256 storageSize() const override; @@ -1056,6 +1076,7 @@ public: virtual u256 storageSize() const override; virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned sizeOnStack() const override { return 0; } + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual std::string toString(bool _short) const override; @@ -1080,6 +1101,7 @@ public: return TypePointer(); } + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } virtual bool canLiveOutsideStorage() const override { return true; } @@ -1109,6 +1131,7 @@ public: return TypePointer(); } + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } virtual bool canLiveOutsideStorage() const override { return true; } @@ -1132,6 +1155,7 @@ class InaccessibleDynamicType: public Type public: virtual Category category() const override { return Category::InaccessibleDynamic; } + virtual std::string identifier() const override { return "t_inaccessible"; } virtual bool isImplicitlyConvertibleTo(Type const&) const override { return false; } virtual bool isExplicitlyConvertibleTo(Type const&) const override { return false; } virtual unsigned calldataEncodedSize(bool _padded) const override { (void)_padded; return 32; } diff --git a/test/libsolidity/SolidityTypes.cpp b/test/libsolidity/SolidityTypes.cpp index dc3143c87..1e7bcc7bb 100644 --- a/test/libsolidity/SolidityTypes.cpp +++ b/test/libsolidity/SolidityTypes.cpp @@ -21,6 +21,8 @@ */ #include +#include +#include #include using namespace std; @@ -86,6 +88,70 @@ BOOST_AUTO_TEST_CASE(storage_layout_arrays) BOOST_CHECK(ArrayType(DataLocation::Storage, make_shared(32), 9).storageSize() == 9); } +BOOST_AUTO_TEST_CASE(type_identifiers) +{ + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("uint128")->identifier(), "t_uint128"); + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("int128")->identifier(), "t_int128"); + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("address")->identifier(), "t_address"); + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("uint8")->identifier(), "t_uint8"); + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("ufixed8x64")->identifier(), "t_ufixed8x64"); + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("fixed128x8")->identifier(), "t_fixed128x8"); + BOOST_CHECK_EQUAL(RationalNumberType(rational(7, 1)).identifier(), "t_rational_7_by_1"); + BOOST_CHECK_EQUAL(RationalNumberType(rational(200, 77)).identifier(), "t_rational_200_by_77"); + BOOST_CHECK_EQUAL(RationalNumberType(rational(2 * 200, 2 * 77)).identifier(), "t_rational_200_by_77"); + BOOST_CHECK_EQUAL( + StringLiteralType(Literal(SourceLocation{}, Token::StringLiteral, make_shared("abc - def"))).identifier(), + "t_stringliteral_196a9142ee0d40e274a6482393c762b16dd8315713207365e1e13d8d85b74fc4" + ); + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("bytes8")->identifier(), "t_bytes8"); + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("bytes32")->identifier(), "t_bytes32"); + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("bool")->identifier(), "t_bool"); + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("bytes")->identifier(), "t_bytes_storage_ptr"); + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("string")->identifier(), "t_string_storage_ptr"); + ArrayType largeintArray(DataLocation::Memory, Type::fromElementaryTypeName("int128"), u256("2535301200456458802993406410752")); + BOOST_CHECK_EQUAL(largeintArray.identifier(), "t_int128_array2535301200456458802993406410752_memory_ptr"); + TypePointer stringArray = make_shared(DataLocation::Storage, Type::fromElementaryTypeName("string"), u256("20")); + TypePointer multiArray = make_shared(DataLocation::Storage, stringArray); + BOOST_CHECK_EQUAL(multiArray->identifier(), "t_string_storage_array20_storage_arraydyn_storage_ptr"); + + ContractDefinition c(SourceLocation{}, make_shared("MyContract"), {}, {}, {}, false); + BOOST_CHECK_EQUAL(c.type()->identifier(), "t_type_t_contract_MyContract_2"); + BOOST_CHECK_EQUAL(ContractType(c, true).identifier(), "t_super_MyContract_2"); + + StructDefinition s({}, make_shared("Struct"), {}); + BOOST_CHECK_EQUAL(s.type()->identifier(), "t_type_t_struct_Struct_3_storage_ptr"); + + EnumDefinition e({}, make_shared("Enum"), {}); + BOOST_CHECK_EQUAL(e.type()->identifier(), "t_type_t_enum_Enum_4"); + + TupleType t({e.type(), s.type(), stringArray, nullptr}); + BOOST_CHECK_EQUAL(t.identifier(), "t_tuple4_t_type_t_enum_Enum_4_t_type_t_struct_Struct_3_storage_ptr_t_string_storage_array20_storage_ptr_t_empty_tuple_end"); + + TypePointer sha3fun = make_shared(strings{}, strings{}, FunctionType::Location::SHA3); + BOOST_CHECK_EQUAL(sha3fun->identifier(), "t_function_sha3_param0_return0_function_end"); + + FunctionType metaFun(TypePointers{sha3fun}, TypePointers{s.type()}); + BOOST_CHECK_EQUAL(metaFun.identifier(), "t_function_internal_param1_t_function_sha3_param0_return0_function_end_return1_t_type_t_struct_Struct_3_storage_ptr_function_end"); + + TypePointer m = make_shared(Type::fromElementaryTypeName("bytes32"), s.type()); + MappingType m2(Type::fromElementaryTypeName("uint64"), m); + BOOST_CHECK_EQUAL(m2.identifier(), "t_mapping_t_uint64_to_t_mapping_t_bytes32_to_t_type_t_struct_Struct_3_storage_ptr_mapping_end_mapping_end"); + + // TypeType is tested with contract + + auto emptyParams = make_shared(SourceLocation(), std::vector>()); + ModifierDefinition mod(SourceLocation{}, make_shared("modif"), {}, emptyParams, {}); + BOOST_CHECK_EQUAL(ModifierType(mod).identifier(), "t_modifier_param0_end_modifier"); + + SourceUnit su({}, {}); + BOOST_CHECK_EQUAL(ModuleType(su).identifier(), "t_module_7"); + BOOST_CHECK_EQUAL(MagicType(MagicType::Kind::Block).identifier(), "t_magic_block"); + BOOST_CHECK_EQUAL(MagicType(MagicType::Kind::Message).identifier(), "t_magic_message"); + BOOST_CHECK_EQUAL(MagicType(MagicType::Kind::Transaction).identifier(), "t_magic_transaction"); + + BOOST_CHECK_EQUAL(InaccessibleDynamicType().identifier(), "t_inaccessible"); +} + BOOST_AUTO_TEST_SUITE_END() } From da178d967fb66ca508d16bbe3feecf1962dcf6ef Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 19 Jan 2017 13:18:56 +0100 Subject: [PATCH 040/118] Properly escape user strings and lists. --- libsolidity/ast/Types.cpp | 108 +++++++++++++++++++---------- libsolidity/ast/Types.h | 22 +++--- test/libsolidity/SolidityTypes.cpp | 24 +++---- 3 files changed, 98 insertions(+), 56 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 9e7224741..6e9e9d7e2 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -21,15 +21,21 @@ */ #include -#include -#include -#include + +#include +#include + #include #include #include #include -#include -#include + +#include +#include +#include +#include + +#include using namespace std; using namespace dev; @@ -117,6 +123,51 @@ u256 const& MemberList::storageSize() const return m_storageOffsets->storageSize(); } +/// Helper functions for type identifier +namespace +{ + +string parenthesizeIdentifier(string const& _internal) +{ + return "$_" + _internal + "_$"; +} + +template +string identifierList(Range const&& _list) +{ + return parenthesizeIdentifier(boost::algorithm::join(_list, "_$_")); +} + +string identifier(TypePointer const& _type) +{ + return _type ? _type->identifier() : ""; +} + +string identifierList(vector const& _list) +{ + return identifierList(_list | boost::adaptors::transformed(identifier)); +} + +string identifierList(TypePointer const& _type) +{ + return parenthesizeIdentifier(identifier(_type)); +} + +string identifierList(TypePointer const& _type1, TypePointer const& _type2) +{ + TypePointers list; + list.push_back(_type1); + list.push_back(_type2); + return identifierList(list); +} + +string parenthesizeUserIdentifier(string const& _internal) +{ + return parenthesizeIdentifier(boost::replace_all_copy(_internal, "$", "$$$")); +} + +} + TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type) { solAssert(Token::isElementaryTypeName(_type.token()), @@ -1218,16 +1269,17 @@ string ArrayType::identifier() const { string id; if (isString()) - id += "t_string"; + id = "t_string"; else if (isByteArray()) - id += "t_bytes"; + id = "t_bytes"; else { - id = baseType()->identifier(); + id = "t_array"; + id += identifierList(baseType()); if (isDynamicallySized()) - id += "_arraydyn"; + id += "dyn"; else - id += string("_array") + length().str(); + id += length().str(); } id += identifierLocationSuffix(); @@ -1422,7 +1474,7 @@ TypePointer ArrayType::copyForLocation(DataLocation _location, bool _isPointer) string ContractType::identifier() const { - return (m_super ? "t_super_" : "t_contract_") + m_contract.name() + "_" + std::to_string(m_contract.id()); + return (m_super ? "t_super" : "t_contract") + parenthesizeUserIdentifier(m_contract.name()) + std::to_string(m_contract.id()); } bool ContractType::operator==(Type const& _other) const @@ -1536,7 +1588,7 @@ bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const string StructType::identifier() const { - return "t_struct_" + m_struct.name() + "_" + std::to_string(m_struct.id()) + identifierLocationSuffix(); + return "t_struct" + parenthesizeUserIdentifier(m_struct.name()) + std::to_string(m_struct.id()) + identifierLocationSuffix(); } bool StructType::operator==(Type const& _other) const @@ -1681,7 +1733,7 @@ TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const string EnumType::identifier() const { - return "t_enum_" + m_enum.name() + "_" + std::to_string(m_enum.id()); + return "t_enum" + parenthesizeUserIdentifier(m_enum.name()) + std::to_string(m_enum.id()); } bool EnumType::operator==(Type const& _other) const @@ -1767,14 +1819,7 @@ bool TupleType::isImplicitlyConvertibleTo(Type const& _other) const string TupleType::identifier() const { - string id = "t_tuple" + std::to_string(components().size()) + "_"; - for (auto const& c: components()) - if (c) - id += c->identifier() + "_"; - else - id += "t_empty_"; - id += "tuple_end"; - return id; + return "t_tuple" + identifierList(components()); } bool TupleType::operator==(Type const& _other) const @@ -2062,19 +2107,13 @@ string FunctionType::identifier() const } if (isConstant()) id += "_constant"; - id += "_param" + std::to_string(m_parameterTypes.size()) + "_"; - for (auto const& p: m_parameterTypes) - id += p->identifier() + "_"; - id += "return" + std::to_string(m_returnParameterTypes.size()) + "_"; - for (auto const& r: m_returnParameterTypes) - id += r->identifier() + "_"; + id += identifierList(m_parameterTypes) + "returns" + identifierList(m_returnParameterTypes); if (m_gasSet) id += "gas_set_"; if (m_valueSet) id += "value_set_"; if (bound()) - id += "bound_to" + selfType()->identifier() + "_"; - id += "function_end"; + id += "bound_to" + identifierList(selfType()); return id; } @@ -2482,7 +2521,7 @@ vector const FunctionType::returnParameterTypeNames(bool _addDataLocatio return names; } -TypePointer FunctionType::selfType() const +TypePointer const& FunctionType::selfType() const { solAssert(bound(), "Function is not bound."); solAssert(m_parameterTypes.size() > 0, "Function has no self type."); @@ -2500,7 +2539,7 @@ ASTPointer FunctionType::documentation() const string MappingType::identifier() const { - return "t_mapping_" + m_keyType->identifier() + "_to_" + m_valueType->identifier() + "_mapping_end"; + return "t_mapping" + identifierList(m_keyType, m_valueType); } bool MappingType::operator==(Type const& _other) const @@ -2523,7 +2562,7 @@ string MappingType::canonicalName(bool) const string TypeType::identifier() const { - return "t_type_" + actualType()->identifier(); + return "t_type" + identifierList(actualType()); } bool TypeType::operator==(Type const& _other) const @@ -2612,10 +2651,7 @@ u256 ModifierType::storageSize() const string ModifierType::identifier() const { - string id = "t_modifier_param" + std::to_string(m_parameterTypes.size()) + "_"; - for (auto const& p: m_parameterTypes) - id += p->identifier() + "_"; - return id + "end_modifier"; + return "t_modifier" + identifierList(m_parameterTypes); } bool ModifierType::operator==(Type const& _other) const diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index c1f6a476a..1e94631e0 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -22,18 +22,21 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include #include #include #include + +#include +#include #include +#include +#include + +#include +#include +#include + namespace dev { namespace solidity @@ -158,6 +161,9 @@ public: /// @returns a valid solidity identifier such that two types should compare equal if and /// only if they have the same identifier. /// The identifier should start with "t_". + /// More complex identifier strings use "parentheses", where $_ is interpreted as as + /// "opening parenthesis", _$ as "closing parenthesis", _$_ as "comma" and any $ that + /// appears as part of a user-supplied identifier is escaped as _$$$_. virtual std::string identifier() const = 0; virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; } virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const @@ -912,7 +918,7 @@ public: std::vector const& returnParameterNames() const { return m_returnParameterNames; } std::vector const returnParameterTypeNames(bool _addDataLocation) const; /// @returns the "self" parameter type for a bound function - TypePointer selfType() const; + TypePointer const& selfType() const; virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; diff --git a/test/libsolidity/SolidityTypes.cpp b/test/libsolidity/SolidityTypes.cpp index 1e7bcc7bb..b4c068731 100644 --- a/test/libsolidity/SolidityTypes.cpp +++ b/test/libsolidity/SolidityTypes.cpp @@ -109,39 +109,39 @@ BOOST_AUTO_TEST_CASE(type_identifiers) BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("bytes")->identifier(), "t_bytes_storage_ptr"); BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("string")->identifier(), "t_string_storage_ptr"); ArrayType largeintArray(DataLocation::Memory, Type::fromElementaryTypeName("int128"), u256("2535301200456458802993406410752")); - BOOST_CHECK_EQUAL(largeintArray.identifier(), "t_int128_array2535301200456458802993406410752_memory_ptr"); + BOOST_CHECK_EQUAL(largeintArray.identifier(), "t_array$_t_int128_$2535301200456458802993406410752_memory_ptr"); TypePointer stringArray = make_shared(DataLocation::Storage, Type::fromElementaryTypeName("string"), u256("20")); TypePointer multiArray = make_shared(DataLocation::Storage, stringArray); - BOOST_CHECK_EQUAL(multiArray->identifier(), "t_string_storage_array20_storage_arraydyn_storage_ptr"); + BOOST_CHECK_EQUAL(multiArray->identifier(), "t_array$_t_array$_t_string_storage_$20_storage_$dyn_storage_ptr"); - ContractDefinition c(SourceLocation{}, make_shared("MyContract"), {}, {}, {}, false); - BOOST_CHECK_EQUAL(c.type()->identifier(), "t_type_t_contract_MyContract_2"); - BOOST_CHECK_EQUAL(ContractType(c, true).identifier(), "t_super_MyContract_2"); + ContractDefinition c(SourceLocation{}, make_shared("MyContract$"), {}, {}, {}, false); + BOOST_CHECK_EQUAL(c.type()->identifier(), "t_type$_t_contract$_MyContract$$$_$2_$"); + BOOST_CHECK_EQUAL(ContractType(c, true).identifier(), "t_super$_MyContract$$$_$2"); StructDefinition s({}, make_shared("Struct"), {}); - BOOST_CHECK_EQUAL(s.type()->identifier(), "t_type_t_struct_Struct_3_storage_ptr"); + BOOST_CHECK_EQUAL(s.type()->identifier(), "t_type$_t_struct$_Struct_$3_storage_ptr_$"); EnumDefinition e({}, make_shared("Enum"), {}); - BOOST_CHECK_EQUAL(e.type()->identifier(), "t_type_t_enum_Enum_4"); + BOOST_CHECK_EQUAL(e.type()->identifier(), "t_type$_t_enum$_Enum_$4_$"); TupleType t({e.type(), s.type(), stringArray, nullptr}); - BOOST_CHECK_EQUAL(t.identifier(), "t_tuple4_t_type_t_enum_Enum_4_t_type_t_struct_Struct_3_storage_ptr_t_string_storage_array20_storage_ptr_t_empty_tuple_end"); + BOOST_CHECK_EQUAL(t.identifier(), "t_tuple$_t_type$_t_enum$_Enum_$4_$_$_t_type$_t_struct$_Struct_$3_storage_ptr_$_$_t_array$_t_string_storage_$20_storage_ptr_$__$"); TypePointer sha3fun = make_shared(strings{}, strings{}, FunctionType::Location::SHA3); - BOOST_CHECK_EQUAL(sha3fun->identifier(), "t_function_sha3_param0_return0_function_end"); + BOOST_CHECK_EQUAL(sha3fun->identifier(), "t_function_sha3$__$returns$__$"); FunctionType metaFun(TypePointers{sha3fun}, TypePointers{s.type()}); - BOOST_CHECK_EQUAL(metaFun.identifier(), "t_function_internal_param1_t_function_sha3_param0_return0_function_end_return1_t_type_t_struct_Struct_3_storage_ptr_function_end"); + BOOST_CHECK_EQUAL(metaFun.identifier(), "t_function_internal$_t_function_sha3$__$returns$__$_$returns$_t_type$_t_struct$_Struct_$3_storage_ptr_$_$"); TypePointer m = make_shared(Type::fromElementaryTypeName("bytes32"), s.type()); MappingType m2(Type::fromElementaryTypeName("uint64"), m); - BOOST_CHECK_EQUAL(m2.identifier(), "t_mapping_t_uint64_to_t_mapping_t_bytes32_to_t_type_t_struct_Struct_3_storage_ptr_mapping_end_mapping_end"); + BOOST_CHECK_EQUAL(m2.identifier(), "t_mapping$_t_uint64_$_t_mapping$_t_bytes32_$_t_type$_t_struct$_Struct_$3_storage_ptr_$_$_$"); // TypeType is tested with contract auto emptyParams = make_shared(SourceLocation(), std::vector>()); ModifierDefinition mod(SourceLocation{}, make_shared("modif"), {}, emptyParams, {}); - BOOST_CHECK_EQUAL(ModifierType(mod).identifier(), "t_modifier_param0_end_modifier"); + BOOST_CHECK_EQUAL(ModifierType(mod).identifier(), "t_modifier$__$"); SourceUnit su({}, {}); BOOST_CHECK_EQUAL(ModuleType(su).identifier(), "t_module_7"); From 7159944f0fa5d9eb3205cd8a3e1d6ec4f133a4ad Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 20 Jan 2017 11:47:20 +0100 Subject: [PATCH 041/118] Reset AST node IDs between compilation runs. --- libsolidity/ast/AST.cpp | 22 ++++++++++++++++++++-- libsolidity/ast/AST.h | 2 ++ libsolidity/ast/Types.cpp | 7 ++++--- libsolidity/interface/CompilerStack.cpp | 1 + test/libsolidity/SolidityTypes.cpp | 1 + 5 files changed, 28 insertions(+), 5 deletions(-) diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index fcd6e38c0..8a43c3f75 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -34,11 +34,24 @@ using namespace std; using namespace dev; using namespace dev::solidity; +class IDDispenser +{ +public: + static size_t next() { return ++instance(); } + static void reset() { instance() = 0; } +private: + static size_t& instance() + { + static IDDispenser dispenser; + return dispenser.id; + } + size_t id = 0; +}; + ASTNode::ASTNode(SourceLocation const& _location): + m_id(IDDispenser::next()), m_location(_location) { - static size_t id = 0; - m_id = ++id; } ASTNode::~ASTNode() @@ -46,6 +59,11 @@ ASTNode::~ASTNode() delete m_annotation; } +void ASTNode::resetID() +{ + IDDispenser::reset(); +} + ASTAnnotation& ASTNode::annotation() const { if (!m_annotation) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 4af649634..116275c34 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -59,6 +59,8 @@ public: /// @returns an identifier of this AST node that is unique for a single compilation run. size_t id() const { return m_id; } + /// Resets the global ID counter. This invalidates all previous IDs. + static void resetID(); virtual void accept(ASTVisitor& _visitor) = 0; virtual void accept(ASTConstVisitor& _visitor) const = 0; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 6e9e9d7e2..cefd0603d 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -163,7 +164,7 @@ string identifierList(TypePointer const& _type1, TypePointer const& _type2) string parenthesizeUserIdentifier(string const& _internal) { - return parenthesizeIdentifier(boost::replace_all_copy(_internal, "$", "$$$")); + return parenthesizeIdentifier(boost::algorithm::replace_all_copy(_internal, "$", "$$$")); } } @@ -2109,9 +2110,9 @@ string FunctionType::identifier() const id += "_constant"; id += identifierList(m_parameterTypes) + "returns" + identifierList(m_returnParameterTypes); if (m_gasSet) - id += "gas_set_"; + id += "gas"; if (m_valueSet) - id += "value_set_"; + id += "value"; if (bound()) id += "bound_to" + identifierList(selfType()); return id; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 61fc77287..85ec0fb1a 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -112,6 +112,7 @@ bool CompilerStack::parse() { //reset m_errors.clear(); + ASTNode::resetID(); m_parseSuccessful = false; if (SemVerVersion{string(VersionString)}.isPrerelease()) diff --git a/test/libsolidity/SolidityTypes.cpp b/test/libsolidity/SolidityTypes.cpp index b4c068731..2dcb92265 100644 --- a/test/libsolidity/SolidityTypes.cpp +++ b/test/libsolidity/SolidityTypes.cpp @@ -90,6 +90,7 @@ BOOST_AUTO_TEST_CASE(storage_layout_arrays) BOOST_AUTO_TEST_CASE(type_identifiers) { + ASTNode::resetID(); BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("uint128")->identifier(), "t_uint128"); BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("int128")->identifier(), "t_int128"); BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("address")->identifier(), "t_address"); From 07b0a0a56028c5ea9bf3dc4fc3b7fda6e4d97ec9 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 20 Jan 2017 15:56:56 +0100 Subject: [PATCH 042/118] Make m_id const. --- libsolidity/ast/AST.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 116275c34..c7c374459 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -99,7 +99,7 @@ public: ///@} protected: - size_t m_id = 0; + size_t const m_id = 0; /// Annotation - is specialised in derived classes, is created upon request (because of polymorphism). mutable ASTAnnotation* m_annotation = nullptr; From 2536bdd6d0dfa1685967fd3106c682e0bcf17021 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 20 Jan 2017 19:01:19 +0100 Subject: [PATCH 043/118] Report source location on "stack too deep" errors. --- Changelog.md | 3 ++- libsolidity/codegen/ContractCompiler.cpp | 9 ++++++++- libsolidity/codegen/ExpressionCompiler.cpp | 7 ++++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index b9e4ecc03..5e458bba7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,7 +1,8 @@ ### 0.4.9 (unreleased) Features: - * Compiler Interface: Contracts and libraries can be referenced with a `file:` prefix to make them unique. + * Compiler interface: Contracts and libraries can be referenced with a ``file:`` prefix to make them unique. + * Compiler interface: Report source location for "stack too deep" errors. * AST: Use deterministic node identifiers. * Type system: Introduce type identifier strings. * Metadata: Do not include platform in the version number. diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index fa77c1aad..bdff0da43 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -486,7 +486,12 @@ bool ContractCompiler::visit(FunctionDefinition const& _function) stackLayout.push_back(i); stackLayout += vector(c_localVariablesSize, -1); - solAssert(stackLayout.size() <= 17, "Stack too deep, try removing local variables."); + if (stackLayout.size() > 17) + BOOST_THROW_EXCEPTION( + CompilerError() << + errinfo_sourceLocation(_function.location()) << + errinfo_comment("Stack too deep, try removing local variables.") + ); while (stackLayout.back() != int(stackLayout.size() - 1)) if (stackLayout.back() < 0) { @@ -551,6 +556,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) if (stackDiff < 1 || stackDiff > 16) BOOST_THROW_EXCEPTION( CompilerError() << + errinfo_sourceLocation(_inlineAssembly.location()) << errinfo_comment("Stack too deep, try removing local variables.") ); for (unsigned i = 0; i < variable->type()->sizeOnStack(); ++i) @@ -591,6 +597,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) if (stackDiff > 16 || stackDiff < 1) BOOST_THROW_EXCEPTION( CompilerError() << + errinfo_sourceLocation(_inlineAssembly.location()) << errinfo_comment("Stack too deep, try removing local variables.") ); for (unsigned i = 0; i < size; ++i) { diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 37bd14583..81d3409ea 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -250,7 +250,12 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) } if (lvalueSize > 0) { - solAssert(itemSize + lvalueSize <= 16, "Stack too deep, try removing local variables."); + if (itemSize + lvalueSize > 16) + BOOST_THROW_EXCEPTION( + CompilerError() << + errinfo_sourceLocation(_assignment.location()) << + errinfo_comment("Stack too deep, try removing local variables.") + ); // value [lvalue_ref] updated_value for (unsigned i = 0; i < itemSize; ++i) m_context << swapInstruction(itemSize + lvalueSize) << Instruction::POP; From df4ef74199392e9b29823d68e6a58dabd490e037 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 20 Jan 2017 19:11:22 +0100 Subject: [PATCH 044/118] Add tests for internal constructor. --- test/libsolidity/SolidityEndToEndTest.cpp | 10 +++++++ .../SolidityNameAndTypeResolution.cpp | 26 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 191618317..63a5828e6 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2505,6 +2505,16 @@ BOOST_AUTO_TEST_CASE(constructor_argument_overriding) BOOST_CHECK(callContractFunction("getA()") == encodeArgs(3)); } +BOOST_AUTO_TEST_CASE(internal_constructor) +{ + char const* sourceCode = R"( + contract C { + function C() internal {} + } + )"; + BOOST_CHECK(compileAndRunWithoutCheck(sourceCode, 0, "C").empty()); +} + BOOST_AUTO_TEST_CASE(function_modifier) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 9f6ea2b3b..edb57b0db 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -4899,6 +4899,32 @@ BOOST_AUTO_TEST_CASE(assignment_to_constant) CHECK_ERROR(text, TypeError, "Cannot assign to a constant variable."); } +BOOST_AUTO_TEST_CASE(inconstructible_internal_constructor) +{ + char const* text = R"( + contract C { + function C() internal {} + } + contract D { + function f() { var x = new C(); } + } + )"; + CHECK_ERROR(text, TypeError, "Contract with internal constructor cannot be created directly."); +} + +BOOST_AUTO_TEST_CASE(constructible_internal_constructor) +{ + char const* text = R"( + contract C { + function C() internal {} + } + contract D is C { + function D() { } + } + )"; + success(text); +} + BOOST_AUTO_TEST_SUITE_END() } From 0ef460461ac280a9f8086d62d831dc9f7d2f5319 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 20 Jan 2017 19:21:43 +0100 Subject: [PATCH 045/118] Check if constructor is public or not. --- libsolidity/analysis/TypeChecker.cpp | 7 ++++++- libsolidity/ast/ASTAnnotations.h | 2 ++ libsolidity/interface/CompilerStack.cpp | 6 +++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 67c8ac175..adb3d54f7 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -75,7 +75,10 @@ bool TypeChecker::visit(ContractDefinition const& _contract) checkContractAbstractConstructors(_contract); FunctionDefinition const* function = _contract.constructor(); - if (function) { + if (function) + { + if (!function->isPublic()) + _contract.annotation().hasPublicConstructor = false; if (!function->returnParameters().empty()) typeError(function->returnParameterList()->location(), "Non-empty \"returns\" directive for constructor."); if (function->isDeclaredConst()) @@ -1280,6 +1283,8 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) fatalTypeError(_newExpression.location(), "Identifier is not a contract."); if (!contract->annotation().isFullyImplemented) typeError(_newExpression.location(), "Trying to create an instance of an abstract contract."); + if (!contract->annotation().hasPublicConstructor) + typeError(_newExpression.location(), "Contract with internal constructor cannot be created directly."); solAssert(!!m_scope, ""); m_scope->annotation().contractDependencies.insert(contract); diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 9c4c3ae8b..61e97a55b 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -80,6 +80,8 @@ struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, DocumentedAnnota { /// Whether all functions are implemented. bool isFullyImplemented = true; + /// Whether a public constructor (even the default one) is available. + bool hasPublicConstructor = true; /// List of all (direct and indirect) base contracts in order from derived to /// base, including the contract itself. std::vector linearizedBaseContracts; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 85ec0fb1a..b26bd5011 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -624,7 +624,11 @@ void CompilerStack::compileContract( map& _compiledContracts ) { - if (_compiledContracts.count(&_contract) || !_contract.annotation().isFullyImplemented) + if ( + _compiledContracts.count(&_contract) || + !_contract.annotation().isFullyImplemented || + !_contract.annotation().hasPublicConstructor + ) return; for (auto const* dependency: _contract.annotation().contractDependencies) compileContract(*dependency, _compiledContracts); From 0b01678bc316d4c966f3cb1b72cbddca934f2609 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 20 Jan 2017 19:29:43 +0100 Subject: [PATCH 046/118] Correct identifier regex --- docs/grammar.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/grammar.txt b/docs/grammar.txt index 62b4a0212..a9f328c06 100644 --- a/docs/grammar.txt +++ b/docs/grammar.txt @@ -114,7 +114,7 @@ NumberUnit = 'wei' | 'szabo' | 'finney' | 'ether' | 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years' HexLiteral = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'') StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"' -Identifier = [a-zA-Z_] [a-zA-Z_0-9]* +Identifier = [a-zA-Z_$] [a-zA-Z_$0-9]* HexNumber = '0x' [0-9a-fA-F]+ DecimalNumber = [0-9]+ From 61a15bb92efd24b54dfa982d42a600b075ebad38 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 20 Jan 2017 19:48:53 +0100 Subject: [PATCH 047/118] Test that all constructible std contracts produce bytecode. --- scripts/tests.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/tests.sh b/scripts/tests.sh index dfbda7342..21958288b 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -45,6 +45,10 @@ do test -z "$output" -a "$failed" -eq 0 done +echo "Checking that StandardToken.sol, owned.sol and mortal.sol produce bytecode..." +output=$("$REPO_ROOT"/build/solc/solc --bin "$REPO_ROOT"/std/*.sol 2>/dev/null | grep "ffff" | wc -l) +test "$output" = "3" + # This conditional is only needed because we don't have a working Homebrew # install for `eth` at the time of writing, so we unzip the ZIP file locally # instead. This will go away soon. From c6207e27613f9664c0d79a7c7d91048ec6fc8bfc Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 20 Jan 2017 19:41:25 +0100 Subject: [PATCH 048/118] Make token constructible. --- std/StandardToken.sol | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/std/StandardToken.sol b/std/StandardToken.sol index 4ff1b8f92..f59e04ac7 100644 --- a/std/StandardToken.sol +++ b/std/StandardToken.sol @@ -3,20 +3,28 @@ pragma solidity ^0.4.0; import "./Token.sol"; contract StandardToken is Token { - uint256 public totalSupply; - mapping (address => uint256) public balanceOf; + uint256 supply; + mapping (address => uint256) balance; mapping (address => - mapping (address => uint256)) public allowance; + mapping (address => uint256)) m_allowance; function StandardToken(address _initialOwner, uint256 _supply) { - totalSupply = _supply; - balanceOf[_initialOwner] = _supply; + supply = _supply; + balance[_initialOwner] = _supply; + } + + function balanceOf(address _account) constant returns (uint) { + return balance[_account]; + } + + function totalSupply() constant returns (uint) { + return supply; } function transfer(address _to, uint256 _value) returns (bool success) { - if (balanceOf[msg.sender] >= _value && balanceOf[_to] + _value >= balanceOf[_to]) { - balanceOf[msg.sender] -= _value; - balanceOf[_to] += _value; + if (balance[msg.sender] >= _value && balance[_to] + _value >= balance[_to]) { + balance[msg.sender] -= _value; + balance[_to] += _value; Transfer(msg.sender, _to, _value); return true; } else { @@ -25,9 +33,9 @@ contract StandardToken is Token { } function transferFrom(address _from, address _to, uint256 _value) returns (bool success) { - if (allowance[_from][msg.sender] >= _value && balanceOf[_to] + _value >= balanceOf[_to]) { - allowance[_from][msg.sender] -= _value; - balanceOf[_to] += _value; + if (m_allowance[_from][msg.sender] >= _value && balance[_to] + _value >= balance[_to]) { + m_allowance[_from][msg.sender] -= _value; + balance[_to] += _value; Transfer(_from, _to, _value); return true; } else { @@ -36,8 +44,12 @@ contract StandardToken is Token { } function approve(address _spender, uint256 _value) returns (bool success) { - allowance[msg.sender][_spender] = _value; + m_allowance[msg.sender][_spender] = _value; Approval(msg.sender, _spender, _value); return true; } + + function allowance(address _owner, address _spender) constant returns (uint256 remaining) { + return m_allowance[_owner][_spender]; + } } From 0897e7bceccf6e83def1f9bd0084bbfb214b7904 Mon Sep 17 00:00:00 2001 From: Sebastien Arbogast Date: Fri, 20 Jan 2017 21:52:36 +0100 Subject: [PATCH 049/118] Fixed typo on contract instance Fixed the name of the variable referencing a contract instance --- docs/control-structures.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 6c0b0f279..532dab0c5 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -104,7 +104,7 @@ contract can be called internally. External Function Calls ----------------------- -The expressions ``this.g(8);`` and ``c.g(2);`` (where ``g`` is a contract +The expressions ``this.g(8);`` and ``c.g(2);`` (where ``c`` is a contract instance) are also valid function calls, but this time, the function will be called "externally", via a message call and not directly via jumps. Please note that function calls on ``this`` cannot be used in the constructor, as the From 7ecc8e412d98ceac16cd36d87c9f3def2584cac3 Mon Sep 17 00:00:00 2001 From: Sebastien Arbogast Date: Fri, 20 Jan 2017 22:53:45 +0100 Subject: [PATCH 050/118] Fix the list of reasons for throwing automatically Used aut-enumerated list syntax --- docs/control-structures.rst | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 6c0b0f279..ad49f7a45 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -384,16 +384,16 @@ In the following example, we show how ``throw`` can be used to easily revert an Currently, Solidity automatically generates a runtime exception in the following situations: -1. If you access an array at a too large or negative index (i.e. ``x[i]`` where ``i >= x.length`` or ``i < 0``). -1. If you access a fixed-length ``bytesN`` at a too large or negative index. -1. If you call a function via a message call but it does not finish properly (i.e. it runs out of gas, has no matching function, or throws an exception itself), except when a low level operation ``call``, ``send``, ``delegatecall`` or ``callcode`` is used. The low level operations never throw exceptions but indicate failures by returning ``false``. -1. If you create a contract using the ``new`` keyword but the contract creation does not finish properly (see above for the definition of "not finish properly"). -1. If you divide or modulo by zero (e.g. ``5 / 0`` or ``23 % 0``). -1. If you shift by a negative amount. -1. If you convert a value too big or negative into an enum type. -1. If you perform an external function call targeting a contract that contains no code. -1. If your contract receives Ether via a public function without ``payable`` modifier (including the constructor and the fallback function). -1. If your contract receives Ether via a public accessor function. +#. If you access an array at a too large or negative index (i.e. ``x[i]`` where ``i >= x.length`` or ``i < 0``). +#. If you access a fixed-length ``bytesN`` at a too large or negative index. +#. If you call a function via a message call but it does not finish properly (i.e. it runs out of gas, has no matching function, or throws an exception itself), except when a low level operation ``call``, ``send``, ``delegatecall`` or ``callcode`` is used. The low level operations never throw exceptions but indicate failures by returning ``false``. +#. If you create a contract using the ``new`` keyword but the contract creation does not finish properly (see above for the definition of "not finish properly"). +#. If you divide or modulo by zero (e.g. ``5 / 0`` or ``23 % 0``). +#. If you shift by a negative amount. +#. If you convert a value too big or negative into an enum type. +#. If you perform an external function call targeting a contract that contains no code. +#. If your contract receives Ether via a public function without ``payable`` modifier (including the constructor and the fallback function). +#. If your contract receives Ether via a public accessor function. Internally, Solidity performs an "invalid jump" when an exception is thrown and thus causes the EVM to revert all changes made to the state. The reason for this is that there is no safe way to continue execution, because an expected effect did not occur. Because we want to retain the atomicity of transactions, the safest thing to do is to revert all changes and make the whole transaction (or at least call) without effect. From 318fbb2604c69dc6eef438aad37b12b99586b198 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 23 Jan 2017 14:58:07 +0100 Subject: [PATCH 051/118] Fix transferFrom. --- std/StandardToken.sol | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/std/StandardToken.sol b/std/StandardToken.sol index f59e04ac7..4dad85418 100644 --- a/std/StandardToken.sol +++ b/std/StandardToken.sol @@ -22,19 +22,23 @@ contract StandardToken is Token { } function transfer(address _to, uint256 _value) returns (bool success) { - if (balance[msg.sender] >= _value && balance[_to] + _value >= balance[_to]) { - balance[msg.sender] -= _value; - balance[_to] += _value; - Transfer(msg.sender, _to, _value); + return doTransfer(msg.sender, _to, _value); + } + + function transferFrom(address _from, address _to, uint256 _value) returns (bool success) { + if (m_allowance[_from][msg.sender] >= _value) { + if (doTransfer(_from, _to, _value)) { + m_allowance[_from][msg.sender] -= _value; + } return true; } else { return false; } } - function transferFrom(address _from, address _to, uint256 _value) returns (bool success) { - if (m_allowance[_from][msg.sender] >= _value && balance[_to] + _value >= balance[_to]) { - m_allowance[_from][msg.sender] -= _value; + function doTransfer(address _from, address _to, uint _value) internal returns (bool success) { + if (balance[_from] >= _value && balance[_to] + _value >= balance[_to]) { + balance[_from] -= _value; balance[_to] += _value; Transfer(_from, _to, _value); return true; From 5a56496db9266b0ee97a1ca71bb5e5ca902475af Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Tue, 18 Oct 2016 14:51:49 +0200 Subject: [PATCH 052/118] test: Add a test for #1215 using the original example from @pipermerriam --- test/libsolidity/SolidityNameAndTypeResolution.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 9f6ea2b3b..6b9d50a92 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -1365,6 +1365,17 @@ BOOST_AUTO_TEST_CASE(anonymous_event_too_many_indexed) CHECK_ERROR(text, TypeError, ""); } +BOOST_AUTO_TEST_CASE(events_with_same_name) +{ + char const* text = R"( + contract TestIt { + event A(); + event A(uint i); + } + )"; + BOOST_CHECK(success(text)); +} + BOOST_AUTO_TEST_CASE(event_call) { char const* text = R"( From 08015590f23957153e12f06a2bcba6e9246733bc Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Tue, 18 Oct 2016 14:52:27 +0200 Subject: [PATCH 053/118] analysis: Allow multiple events of the same name Fixes #1215 --- libsolidity/analysis/DeclarationContainer.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp index f8c12c5bd..ac80ab184 100644 --- a/libsolidity/analysis/DeclarationContainer.cpp +++ b/libsolidity/analysis/DeclarationContainer.cpp @@ -58,6 +58,13 @@ Declaration const* DeclarationContainer::conflictingDeclaration( return declaration; } } + else if (dynamic_cast(&_declaration)) + { + // check that all other declarations with the same name are events + for (Declaration const* declaration: declarations) + if (!dynamic_cast(declaration)) + return declaration; + } else if (declarations.size() == 1 && declarations.front() == &_declaration) return nullptr; else if (!declarations.empty()) From 0e85e35a7f5f8129672ac02b38771749f4e5d456 Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Tue, 18 Oct 2016 16:05:16 +0200 Subject: [PATCH 054/118] test: Add an end-to-end test about multiple events of the same name See #1215 --- test/libsolidity/SolidityEndToEndTest.cpp | 41 +++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 191618317..2411e7ffe 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2792,6 +2792,47 @@ BOOST_AUTO_TEST_CASE(event_access_through_base_name) BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("x()"))); } +BOOST_AUTO_TEST_CASE(events_with_same_name) +{ + char const* sourceCode = R"( + contract ClientReceipt { + event Deposit; + event Deposit(address _addr); + event Deposit(address _addr, uint _amount); + function deposit() { + Deposit(); + } + function deposit(address _addr) { + Deposit(_addr); + } + function deposit(address _addr, uint _amount) { + Deposit(_addr, _amount); + } + } + )"; + compileAndRun(sourceCode); + callContractFunction("deposit()"); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data.empty()); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit()"))); + + callContractFunction("deposit(0xabcdeabcdeabcdeabcde)"); + BOOST_REQUIRE_EQUAL(m_logs.size(), 2); + BOOST_CHECK_EQUAL(m_logs[1].address, m_contractAddress); + BOOST_CHECK(m_logs[1].data == encodeArgs(0)); + BOOST_REQUIRE_EQUAL(m_logs[1].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[1].topics[0], dev::keccak256(string("Deposit(address)"))); + + callContractFunction("deposit(0xabcdeabcdeabcdeabcde, 100)"); + BOOST_REQUIRE_EQUAL(m_logs.size(), 3); + BOOST_CHECK_EQUAL(m_logs[2].address, m_contractAddress); + BOOST_CHECK(m_logs[2].data == encodeArgs(0,100)); + BOOST_REQUIRE_EQUAL(m_logs[2].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[2].topics[0], dev::keccak256(string("Deposit(address,uint256)"))); +} + BOOST_AUTO_TEST_CASE(event_anonymous) { char const* sourceCode = R"( From 4fd3641ce5f80b54463a5f5c8aade6c5112070a3 Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Tue, 18 Oct 2016 16:06:46 +0200 Subject: [PATCH 055/118] Changelog: add a point about allowing multiple events that share the same name See #1215 --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index b9e4ecc03..d322fccda 100644 --- a/Changelog.md +++ b/Changelog.md @@ -26,6 +26,7 @@ Features: * Code generator: Inject the Swarm hash of a metadata file into the bytecode. * Code generator: Replace expensive memcpy precompile by simple assembly loop. * Optimizer: Some dead code elimination. + * Type checker: Allow multiple events of the same name (but with different arities or argument types) Bugfixes: * Code generator: throw if calling the identity precompile failed during memory (array) copying. From 133d1c05e1ea1ae505e84df4c0660942f614bb3a Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Wed, 19 Oct 2016 19:51:12 +0200 Subject: [PATCH 056/118] test: fixing inconsistent usage of end-to-end test framework --- test/libsolidity/SolidityEndToEndTest.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 2411e7ffe..6163790b5 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2761,6 +2761,7 @@ BOOST_AUTO_TEST_CASE(event_no_arguments) } } )"; + compileAndRun(sourceCode); callContractFunction("deposit()"); BOOST_REQUIRE_EQUAL(m_logs.size(), 1); @@ -2810,6 +2811,8 @@ BOOST_AUTO_TEST_CASE(events_with_same_name) } } )"; + u160 const c_loggedAddress = m_contractAddress; + compileAndRun(sourceCode); callContractFunction("deposit()"); BOOST_REQUIRE_EQUAL(m_logs.size(), 1); @@ -2818,17 +2821,17 @@ BOOST_AUTO_TEST_CASE(events_with_same_name) BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit()"))); - callContractFunction("deposit(0xabcdeabcdeabcdeabcde)"); + callContractFunction("deposit(address)", c_loggedAddress); BOOST_REQUIRE_EQUAL(m_logs.size(), 2); BOOST_CHECK_EQUAL(m_logs[1].address, m_contractAddress); - BOOST_CHECK(m_logs[1].data == encodeArgs(0)); + BOOST_CHECK(m_logs[1].data == encodeArgs(c_loggedAddress)); BOOST_REQUIRE_EQUAL(m_logs[1].topics.size(), 1); BOOST_CHECK_EQUAL(m_logs[1].topics[0], dev::keccak256(string("Deposit(address)"))); - callContractFunction("deposit(0xabcdeabcdeabcdeabcde, 100)"); + callContractFunction("deposit(address, uint256)", c_loggedAddress, u256(100)); BOOST_REQUIRE_EQUAL(m_logs.size(), 3); BOOST_CHECK_EQUAL(m_logs[2].address, m_contractAddress); - BOOST_CHECK(m_logs[2].data == encodeArgs(0,100)); + BOOST_CHECK(m_logs[2].data == encodeArgs(c_loggedAddress, 100)); BOOST_REQUIRE_EQUAL(m_logs[2].topics.size(), 1); BOOST_CHECK_EQUAL(m_logs[2].topics[0], dev::keccak256(string("Deposit(address,uint256)"))); } From 4c09e81c3e60ddc43709656f9be4f991360b4108 Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Wed, 19 Oct 2016 20:16:28 +0200 Subject: [PATCH 057/118] test: check the results of function calls in the test for multiple events of the same name --- test/libsolidity/SolidityEndToEndTest.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 6163790b5..81cedfb0e 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2800,35 +2800,38 @@ BOOST_AUTO_TEST_CASE(events_with_same_name) event Deposit; event Deposit(address _addr); event Deposit(address _addr, uint _amount); - function deposit() { + function deposit() returns (uint) { Deposit(); + return 1; } - function deposit(address _addr) { + function deposit(address _addr) returns (uint) { Deposit(_addr); + return 1; } - function deposit(address _addr, uint _amount) { + function deposit(address _addr, uint _amount) returns (uint) { Deposit(_addr, _amount); + return 1; } } )"; u160 const c_loggedAddress = m_contractAddress; compileAndRun(sourceCode); - callContractFunction("deposit()"); + BOOST_CHECK(callContractFunction("deposit()") == encodeArgs(u256(1))); BOOST_REQUIRE_EQUAL(m_logs.size(), 1); BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); BOOST_CHECK(m_logs[0].data.empty()); BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit()"))); - callContractFunction("deposit(address)", c_loggedAddress); + BOOST_CHECK(callContractFunction("deposit(address)", c_loggedAddress) == encodeArgs(u256(1))); BOOST_REQUIRE_EQUAL(m_logs.size(), 2); BOOST_CHECK_EQUAL(m_logs[1].address, m_contractAddress); BOOST_CHECK(m_logs[1].data == encodeArgs(c_loggedAddress)); BOOST_REQUIRE_EQUAL(m_logs[1].topics.size(), 1); BOOST_CHECK_EQUAL(m_logs[1].topics[0], dev::keccak256(string("Deposit(address)"))); - callContractFunction("deposit(address, uint256)", c_loggedAddress, u256(100)); + BOOST_CHECK(callContractFunction("deposit(address,uint256)", c_loggedAddress, u256(100)) == encodeArgs(u256(1))); BOOST_REQUIRE_EQUAL(m_logs.size(), 3); BOOST_CHECK_EQUAL(m_logs[2].address, m_contractAddress); BOOST_CHECK(m_logs[2].data == encodeArgs(c_loggedAddress, 100)); From 846f7dc3ea6591b34deb3e6738292ffd87c06ed3 Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Thu, 20 Oct 2016 10:47:15 +0200 Subject: [PATCH 058/118] analysis: Resolve event overloading --- libsolidity/analysis/DeclarationContainer.cpp | 21 ++++++++----------- libsolidity/analysis/NameAndTypeResolver.cpp | 4 ++-- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp index ac80ab184..04836603e 100644 --- a/libsolidity/analysis/DeclarationContainer.cpp +++ b/libsolidity/analysis/DeclarationContainer.cpp @@ -42,28 +42,25 @@ Declaration const* DeclarationContainer::conflictingDeclaration( if (m_invisibleDeclarations.count(*_name)) declarations += m_invisibleDeclarations.at(*_name); - if (dynamic_cast(&_declaration)) + if (dynamic_cast(&_declaration) || + dynamic_cast(&_declaration) + ) { - // check that all other declarations with the same name are functions or a public state variable + // check that all other declarations with the same name are functions or a public state variable or events. + // And then check that the signatures are different. for (Declaration const* declaration: declarations) { - if (dynamic_cast(declaration)) - continue; if (auto variableDeclaration = dynamic_cast(declaration)) { if (variableDeclaration->isStateVariable() && !variableDeclaration->isConstant() && variableDeclaration->isPublic()) continue; return declaration; } - return declaration; - } - } - else if (dynamic_cast(&_declaration)) - { - // check that all other declarations with the same name are events - for (Declaration const* declaration: declarations) - if (!dynamic_cast(declaration)) + if (!dynamic_cast(declaration) && + !dynamic_cast(declaration)) return declaration; + // Or, continue. + } } else if (declarations.size() == 1 && declarations.front() == &_declaration) return nullptr; diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 08323243a..b0a82715d 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -260,8 +260,8 @@ vector NameAndTypeResolver::cleanedDeclarations( for (auto it = _declarations.begin(); it != _declarations.end(); ++it) { solAssert(*it, ""); - // the declaration is functionDefinition or a VariableDeclaration while declarations > 1 - solAssert(dynamic_cast(*it) || dynamic_cast(*it), + // the declaration is functionDefinition, eventDefinition or a VariableDeclaration while declarations > 1 + solAssert(dynamic_cast(*it) || dynamic_cast(*it) || dynamic_cast(*it), "Found overloading involving something not a function or a variable"); shared_ptr functionType { (*it)->functionType(false) }; From 567139486f45f69b22beb846fde7c9b27e1ebce0 Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Fri, 2 Dec 2016 18:58:50 +0100 Subject: [PATCH 059/118] test: somehow log counting system has changed --- test/libsolidity/SolidityEndToEndTest.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 81cedfb0e..c29ae6fcf 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2825,18 +2825,18 @@ BOOST_AUTO_TEST_CASE(events_with_same_name) BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit()"))); BOOST_CHECK(callContractFunction("deposit(address)", c_loggedAddress) == encodeArgs(u256(1))); - BOOST_REQUIRE_EQUAL(m_logs.size(), 2); - BOOST_CHECK_EQUAL(m_logs[1].address, m_contractAddress); - BOOST_CHECK(m_logs[1].data == encodeArgs(c_loggedAddress)); - BOOST_REQUIRE_EQUAL(m_logs[1].topics.size(), 1); - BOOST_CHECK_EQUAL(m_logs[1].topics[0], dev::keccak256(string("Deposit(address)"))); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data == encodeArgs(c_loggedAddress)); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address)"))); BOOST_CHECK(callContractFunction("deposit(address,uint256)", c_loggedAddress, u256(100)) == encodeArgs(u256(1))); - BOOST_REQUIRE_EQUAL(m_logs.size(), 3); - BOOST_CHECK_EQUAL(m_logs[2].address, m_contractAddress); - BOOST_CHECK(m_logs[2].data == encodeArgs(c_loggedAddress, 100)); - BOOST_REQUIRE_EQUAL(m_logs[2].topics.size(), 1); - BOOST_CHECK_EQUAL(m_logs[2].topics[0], dev::keccak256(string("Deposit(address,uint256)"))); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data == encodeArgs(c_loggedAddress, 100)); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address,uint256)"))); } BOOST_AUTO_TEST_CASE(event_anonymous) From 1f2bf7e004f6b87aa4a6f4acb36670d780e134a8 Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Mon, 2 Jan 2017 18:00:32 +0100 Subject: [PATCH 060/118] Changelog: move an item upwards --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index d322fccda..77c0b0b08 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Features: * AST: Use deterministic node identifiers. * Type system: Introduce type identifier strings. * Metadata: Do not include platform in the version number. + * Type checker: Allow multiple events of the same name (but with different arities or argument types) ### 0.4.8 (2017-01-13) @@ -26,7 +27,6 @@ Features: * Code generator: Inject the Swarm hash of a metadata file into the bytecode. * Code generator: Replace expensive memcpy precompile by simple assembly loop. * Optimizer: Some dead code elimination. - * Type checker: Allow multiple events of the same name (but with different arities or argument types) Bugfixes: * Code generator: throw if calling the identity precompile failed during memory (array) copying. From 8dc306d62b1d7ef2816187175958ce4edab0170c Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Fri, 20 Jan 2017 14:11:06 +0100 Subject: [PATCH 061/118] test: add a test case about inheriting multiple events of the same name --- test/libsolidity/SolidityEndToEndTest.cpp | 52 +++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index c29ae6fcf..b2b5a25e4 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2839,6 +2839,58 @@ BOOST_AUTO_TEST_CASE(events_with_same_name) BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address,uint256)"))); } +BOOST_AUTO_TEST_CASE(events_with_same_name_inherited) +{ + char const* sourceCode = R"( + contract A { + event Deposit; + } + + contract B { + event Deposit(address _addr); + } + + contract ClientReceipt is A, B { + event Deposit(address _addr, uint _amount); + function deposit() returns (uint) { + Deposit(); + return 1; + } + function deposit(address _addr) returns (uint) { + Deposit(_addr); + return 1; + } + function deposit(address _addr, uint _amount) returns (uint) { + Deposit(_addr, _amount); + return 1; + } + } + )"; + u160 const c_loggedAddress = m_contractAddress; + + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("deposit()") == encodeArgs(u256(1))); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data.empty()); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit()"))); + + BOOST_CHECK(callContractFunction("deposit(address)", c_loggedAddress) == encodeArgs(u256(1))); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data == encodeArgs(c_loggedAddress)); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address)"))); + + BOOST_CHECK(callContractFunction("deposit(address,uint256)", c_loggedAddress, u256(100)) == encodeArgs(u256(1))); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data == encodeArgs(c_loggedAddress, 100)); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address,uint256)"))); +} + BOOST_AUTO_TEST_CASE(event_anonymous) { char const* sourceCode = R"( From 399b7b695a2ffe5ae2b07628860712fc76dfe03c Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Mon, 23 Jan 2017 14:51:17 +0100 Subject: [PATCH 062/118] analysis: fix format --- libsolidity/analysis/DeclarationContainer.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp index 04836603e..7e8cd2cac 100644 --- a/libsolidity/analysis/DeclarationContainer.cpp +++ b/libsolidity/analysis/DeclarationContainer.cpp @@ -42,9 +42,10 @@ Declaration const* DeclarationContainer::conflictingDeclaration( if (m_invisibleDeclarations.count(*_name)) declarations += m_invisibleDeclarations.at(*_name); - if (dynamic_cast(&_declaration) || + if ( + dynamic_cast(&_declaration) || dynamic_cast(&_declaration) - ) + ) { // check that all other declarations with the same name are functions or a public state variable or events. // And then check that the signatures are different. @@ -56,8 +57,10 @@ Declaration const* DeclarationContainer::conflictingDeclaration( continue; return declaration; } - if (!dynamic_cast(declaration) && - !dynamic_cast(declaration)) + if ( + !dynamic_cast(declaration) && + !dynamic_cast(declaration) + ) return declaration; // Or, continue. } From 3d8b56c2a4b3f3f29de7b9804c85a10d077502f8 Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Mon, 23 Jan 2017 15:24:29 +0100 Subject: [PATCH 063/118] test: add tests about functions and events of the same name --- .../SolidityNameAndTypeResolution.cpp | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 6b9d50a92..39fbc019b 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -1387,6 +1387,53 @@ BOOST_AUTO_TEST_CASE(event_call) CHECK_SUCCESS(text); } +BOOST_AUTO_TEST_CASE(event_function_inheritance_clash) +{ + char const* text = R"( + contract A { + function dup() returns (uint) { + return 1; + } + } + contract B { + event dup(); + } + contract C is A, B { + } + )"; + CHECK_ERROR(text, DeclarationError, "Identifier already declared."); +} + +BOOST_AUTO_TEST_CASE(function_event_inheritance_clash) +{ + char const* text = R"( + contract B { + event dup(); + } + contract A { + function dup() returns (uint) { + return 1; + } + } + contract C is B, A { + } + )"; + CHECK_ERROR(text, DeclarationError, "Identifier already declared."); +} + +BOOST_AUTO_TEST_CASE(function_event_in_contract_clash) +{ + char const* text = R"( + contract A { + event dup(); + function dup() returns (uint) { + return 1; + } + } + )"; + CHECK_ERROR(text, DeclarationError, "Identifier already declared."); +} + BOOST_AUTO_TEST_CASE(event_inheritance) { char const* text = R"( From 4e1fd68b38346ec3e4117dc0454b65f4b236741b Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Mon, 23 Jan 2017 15:24:47 +0100 Subject: [PATCH 064/118] analysis: disallow overloading functions with events --- libsolidity/analysis/DeclarationContainer.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp index 7e8cd2cac..b33c85685 100644 --- a/libsolidity/analysis/DeclarationContainer.cpp +++ b/libsolidity/analysis/DeclarationContainer.cpp @@ -58,7 +58,12 @@ Declaration const* DeclarationContainer::conflictingDeclaration( return declaration; } if ( - !dynamic_cast(declaration) && + dynamic_cast(&_declaration) && + !dynamic_cast(declaration) + ) + return declaration; + if ( + dynamic_cast(&_declaration) && !dynamic_cast(declaration) ) return declaration; From 997f5d751a21263b1f104d547486abca3ee3bff6 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 24 Jan 2017 01:09:10 +0100 Subject: [PATCH 065/118] Create functional assembly output, if possible. --- libevmasm/Assembly.cpp | 100 +++++++++++++++++++++++++++++++------ libevmasm/Assembly.h | 1 - libevmasm/AssemblyItem.cpp | 12 ++++- libevmasm/AssemblyItem.h | 4 +- 4 files changed, 97 insertions(+), 20 deletions(-) diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 0247593bf..b7859c1f0 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -94,7 +94,10 @@ unsigned Assembly::bytesRequired(unsigned subTagSize) const } } -string Assembly::locationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location) const +namespace +{ + +string locationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location) { if (_location.isEmpty() || _sourceCodes.empty() || _location.start >= _location.end || _location.start < 0) return ""; @@ -115,27 +118,92 @@ string Assembly::locationFromSources(StringMap const& _sourceCodes, SourceLocati return cut; } +class Functionalizer +{ +public: + Functionalizer (ostream& _out, string const& _prefix, StringMap const& _sourceCodes): + m_out(_out), m_prefix(_prefix), m_sourceCodes(_sourceCodes) + {} + + void feed(AssemblyItem const& _item) + { + if (!_item.location().isEmpty() && _item.location() != m_location) + { + flush(); + printLocation(); + m_location = _item.location(); + } + if (!( + _item.canBeFunctional() && + _item.returnValues() <= 1 && + _item.arguments() <= int(m_pending.size()) + )) + { + flush(); + m_out << m_prefix << (_item.type() == Tag ? "" : " ") << _item.toAssemblyText() << endl; + return; + } + string expression = _item.toAssemblyText(); + if (_item.arguments() > 0) + { + expression += "("; + for (int i = 0; i < _item.arguments(); ++i) + { + expression += m_pending.back(); + m_pending.pop_back(); + if (i + 1 < _item.arguments()) + expression += ", "; + } + expression += ")"; + } + + m_pending.push_back(expression); + if (_item.returnValues() != 1) + flush(); + } + + void flush() + { + for (string const& expression: m_pending) + m_out << m_prefix << " " << expression << endl; + m_pending.clear(); + } + + void printLocation() + { + if (!m_location.sourceName && m_location.isEmpty()) + return; + m_out << m_prefix << " /*"; + if (m_location.sourceName) + m_out << " \"" + *m_location.sourceName + "\""; + if (!m_location.isEmpty()) + m_out << ":" << to_string(m_location.start) + ":" + to_string(m_location.end); + m_out << " " << locationFromSources(m_sourceCodes, m_location); + m_out << " */" << endl; + } + +private: + strings m_pending; + SourceLocation m_location; + + ostream& m_out; + string const& m_prefix; + StringMap const& m_sourceCodes; +}; + +} + ostream& Assembly::streamAsm(ostream& _out, string const& _prefix, StringMap const& _sourceCodes) const { - for (size_t i = 0; i < m_items.size(); ++i) - { - AssemblyItem const& item = m_items[i]; - if (!item.location().isEmpty() && (i == 0 || m_items[i - 1].location() != item.location())) - { - _out << _prefix << " /*"; - if (item.location().sourceName) - _out << " \"" + *item.location().sourceName + "\""; - if (!item.location().isEmpty()) - _out << ":" << to_string(item.location().start) + ":" + to_string(item.location().end); - _out << " */" << endl; - } - _out << _prefix << (item.type() == Tag ? "" : " ") << item.toAssemblyText() << endl; - } + Functionalizer f(_out, _prefix, _sourceCodes); + + for (auto const& i: m_items) + f.feed(i); + f.flush(); if (!m_data.empty() || !m_subs.empty()) { _out << _prefix << "stop" << endl; - Json::Value data; for (auto const& i: m_data) assertThrow(u256(i.first) < m_subs.size(), AssemblyException, "Data not yet implemented."); diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index 9e7f9f7bd..528c9e74c 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -118,7 +118,6 @@ protected: /// returns the replaced tags. std::map optimiseInternal(bool _enable, bool _isCreation, size_t _runs); - std::string locationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location) const; void donePath() { if (m_totalDeposit != INT_MAX && m_totalDeposit != m_deposit) BOOST_THROW_EXCEPTION(InvalidDeposit()); } unsigned bytesRequired(unsigned subTagSize) const; diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp index 6c7d5425e..26d9fded4 100644 --- a/libevmasm/AssemblyItem.cpp +++ b/libevmasm/AssemblyItem.cpp @@ -76,12 +76,20 @@ unsigned AssemblyItem::bytesRequired(unsigned _addressLength) const BOOST_THROW_EXCEPTION(InvalidOpcode()); } -int AssemblyItem::deposit() const +int AssemblyItem::arguments() const +{ + if (type() == Operation) + return instructionInfo(instruction()).args; + else + return 0; +} + +int AssemblyItem::returnValues() const { switch (m_type) { case Operation: - return instructionInfo(instruction()).ret - instructionInfo(instruction()).args; + return instructionInfo(instruction()).ret; case Push: case PushString: case PushTag: diff --git a/libevmasm/AssemblyItem.h b/libevmasm/AssemblyItem.h index 002b3c874..464368fb5 100644 --- a/libevmasm/AssemblyItem.h +++ b/libevmasm/AssemblyItem.h @@ -116,7 +116,9 @@ public: /// @returns an upper bound for the number of bytes required by this item, assuming that /// the value of a jump tag takes @a _addressLength bytes. unsigned bytesRequired(unsigned _addressLength) const; - int deposit() const; + int arguments() const; + int returnValues() const; + int deposit() const { return returnValues() - arguments(); } /// @returns true if the assembly item can be used in a functional context. bool canBeFunctional() const; From dea59bfbdc610b65018b613dc20322d98e9aa2b6 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 12 Jan 2017 17:56:05 +0100 Subject: [PATCH 066/118] Test for initializing recursive structs. --- test/libsolidity/SolidityEndToEndTest.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index da7adbbf3..e8e5ced1a 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -8992,6 +8992,28 @@ BOOST_AUTO_TEST_CASE(contracts_separated_with_comment) compileAndRun(sourceCode, 0, "C2"); } +BOOST_AUTO_TEST_CASE(recursive_structs) +{ + char const* sourceCode = R"( + contract C { + struct S { + S[] x; + } + S sstorage; + function f() returns (uint) { + S memory s; + s.x = new S[](10); + delete s; + sstorage.x.length++; + delete sstorage; + return 1; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(1))); +} + BOOST_AUTO_TEST_SUITE_END() } From d0e8d340a53395de4c83e4e1d6ccf4c8eab5889a Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 17 Jan 2017 10:47:57 +0100 Subject: [PATCH 067/118] Low level named functions for CompilerContext. --- libsolidity/codegen/CompilerContext.cpp | 15 +++++++++++++++ libsolidity/codegen/CompilerContext.h | 8 ++++++++ 2 files changed, 23 insertions(+) diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index c14ab845d..fcd39b333 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -125,6 +125,21 @@ Declaration const* CompilerContext::nextFunctionToCompile() const return m_functionCompilationQueue.nextFunctionToCompile(); } +eth::AssemblyItem const* CompilerContext::lowLevelFunctionEntryPoint(string const& _name) const +{ + auto it = m_lowLevelFunctions.find(_name); + if (it == m_lowLevelFunctions.end()) + return nullptr; + else + return *it; +} + +void CompilerContext::addLowLevelFunction(string const& _name, eth::AssemblyItem const& _label) +{ + solAssert(lowLevelFunctionEntryPoint(_name) != nullptr, "Low level function with that name already exists."); + m_lowLevelFunctions[_name] = _label.pushTag(); +} + ModifierDefinition const& CompilerContext::functionModifier(string const& _name) const { solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 806715280..34befc13d 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -90,6 +90,12 @@ public: /// as "having code". void startFunction(Declaration const& _function); + /// Returns the label of the low level function with the given name or nullptr if it + /// does not exist. The lifetime of the returned pointer is short. + eth::AssemblyItem const* lowLevelFunctionEntryPoint(std::string const& _name) const; + /// Inserts a low level function entry point into the list of low level functions. + void addLowLevelFunction(std::string const& _name, eth::AssemblyItem const& _label); + ModifierDefinition const& functionModifier(std::string const& _name) const; /// Returns the distance of the given local variable from the bottom of the stack (of the current function). unsigned baseStackOffsetOfVariable(Declaration const& _declaration) const; @@ -248,6 +254,8 @@ private: CompilerContext *m_runtimeContext; /// The index of the runtime subroutine. size_t m_runtimeSub = -1; + /// An index of low-level function labels by name. + std::map m_lowLevelFunctions; }; } From b60623521f052b8a36c61f8632d868cac552bf29 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 19 Jan 2017 17:21:55 +0100 Subject: [PATCH 068/118] Move some util functions to low-level functions. --- libsolidity/codegen/ArrayUtils.cpp | 362 ++++++++++++----------- libsolidity/codegen/CompilerContext.cpp | 69 +++-- libsolidity/codegen/CompilerContext.h | 35 ++- libsolidity/codegen/CompilerUtils.cpp | 67 +++-- libsolidity/codegen/ContractCompiler.cpp | 1 + 5 files changed, 305 insertions(+), 229 deletions(-) diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 352c7177a..6f270b4dd 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -502,60 +502,70 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord } } -void ArrayUtils::clearArray(ArrayType const& _type) const +void ArrayUtils::clearArray(ArrayType const& _typeIn) const { - unsigned stackHeightStart = m_context.stackHeight(); - solAssert(_type.location() == DataLocation::Storage, ""); - if (_type.baseType()->storageBytes() < 32) - { - solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type."); - solAssert(_type.baseType()->storageSize() <= 1, "Invalid storage size for type."); - } - if (_type.baseType()->isValueType()) - solAssert(_type.baseType()->storageSize() <= 1, "Invalid size for value type."); - - m_context << Instruction::POP; // remove byte offset - if (_type.isDynamicallySized()) - clearDynamicArray(_type); - else if (_type.length() == 0 || _type.baseType()->category() == Type::Category::Mapping) - m_context << Instruction::POP; - else if (_type.baseType()->isValueType() && _type.storageSize() <= 5) - { - // unroll loop for small arrays @todo choose a good value - // Note that we loop over storage slots here, not elements. - for (unsigned i = 1; i < _type.storageSize(); ++i) - m_context - << u256(0) << Instruction::DUP2 << Instruction::SSTORE - << u256(1) << Instruction::ADD; - m_context << u256(0) << Instruction::SWAP1 << Instruction::SSTORE; - } - else if (!_type.baseType()->isValueType() && _type.length() <= 4) - { - // unroll loop for small arrays @todo choose a good value - solAssert(_type.baseType()->storageBytes() >= 32, "Invalid storage size."); - for (unsigned i = 1; i < _type.length(); ++i) + TypePointer type = _typeIn.shared_from_this(); + m_context.callLowLevelFunction( + "$clearArray_" + _typeIn.identifier(), + 2, + 0, + [type](CompilerContext& _context) { - m_context << u256(0); - StorageItem(m_context, *_type.baseType()).setToZero(SourceLocation(), false); - m_context - << Instruction::POP - << u256(_type.baseType()->storageSize()) << Instruction::ADD; + ArrayType const& _type = dynamic_cast(*type); + unsigned stackHeightStart = _context.stackHeight(); + solAssert(_type.location() == DataLocation::Storage, ""); + if (_type.baseType()->storageBytes() < 32) + { + solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type."); + solAssert(_type.baseType()->storageSize() <= 1, "Invalid storage size for type."); + } + if (_type.baseType()->isValueType()) + solAssert(_type.baseType()->storageSize() <= 1, "Invalid size for value type."); + + _context << Instruction::POP; // remove byte offset + if (_type.isDynamicallySized()) + ArrayUtils(_context).clearDynamicArray(_type); + else if (_type.length() == 0 || _type.baseType()->category() == Type::Category::Mapping) + _context << Instruction::POP; + else if (_type.baseType()->isValueType() && _type.storageSize() <= 5) + { + // unroll loop for small arrays @todo choose a good value + // Note that we loop over storage slots here, not elements. + for (unsigned i = 1; i < _type.storageSize(); ++i) + _context + << u256(0) << Instruction::DUP2 << Instruction::SSTORE + << u256(1) << Instruction::ADD; + _context << u256(0) << Instruction::SWAP1 << Instruction::SSTORE; + } + else if (!_type.baseType()->isValueType() && _type.length() <= 4) + { + // unroll loop for small arrays @todo choose a good value + solAssert(_type.baseType()->storageBytes() >= 32, "Invalid storage size."); + for (unsigned i = 1; i < _type.length(); ++i) + { + _context << u256(0); + StorageItem(_context, *_type.baseType()).setToZero(SourceLocation(), false); + _context + << Instruction::POP + << u256(_type.baseType()->storageSize()) << Instruction::ADD; + } + _context << u256(0); + StorageItem(_context, *_type.baseType()).setToZero(SourceLocation(), true); + } + else + { + _context << Instruction::DUP1 << _type.length(); + ArrayUtils(_context).convertLengthToSize(_type); + _context << Instruction::ADD << Instruction::SWAP1; + if (_type.baseType()->storageBytes() < 32) + ArrayUtils(_context).clearStorageLoop(IntegerType(256)); + else + ArrayUtils(_context).clearStorageLoop(*_type.baseType()); + _context << Instruction::POP; + } + solAssert(_context.stackHeight() == stackHeightStart - 2, ""); } - m_context << u256(0); - StorageItem(m_context, *_type.baseType()).setToZero(SourceLocation(), true); - } - else - { - m_context << Instruction::DUP1 << _type.length(); - convertLengthToSize(_type); - m_context << Instruction::ADD << Instruction::SWAP1; - if (_type.baseType()->storageBytes() < 32) - clearStorageLoop(IntegerType(256)); - else - clearStorageLoop(*_type.baseType()); - m_context << Instruction::POP; - } - solAssert(m_context.stackHeight() == stackHeightStart - 2, ""); + ); } void ArrayUtils::clearDynamicArray(ArrayType const& _type) const @@ -597,144 +607,154 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const m_context << Instruction::POP; } -void ArrayUtils::resizeDynamicArray(ArrayType const& _type) const +void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const { - solAssert(_type.location() == DataLocation::Storage, ""); - solAssert(_type.isDynamicallySized(), ""); - if (!_type.isByteArray() && _type.baseType()->storageBytes() < 32) - solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type."); + TypePointer type = _typeIn.shared_from_this(); + m_context.callLowLevelFunction( + "$resizeDynamicArray_" + _typeIn.identifier(), + 2, + 0, + [type](CompilerContext& _context) + { + ArrayType const& _type = dynamic_cast(*type); + solAssert(_type.location() == DataLocation::Storage, ""); + solAssert(_type.isDynamicallySized(), ""); + if (!_type.isByteArray() && _type.baseType()->storageBytes() < 32) + solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type."); - unsigned stackHeightStart = m_context.stackHeight(); - eth::AssemblyItem resizeEnd = m_context.newTag(); + unsigned stackHeightStart = _context.stackHeight(); + eth::AssemblyItem resizeEnd = _context.newTag(); - // stack: ref new_length - // fetch old length - retrieveLength(_type, 1); - // stack: ref new_length old_length - solAssert(m_context.stackHeight() - stackHeightStart == 3 - 2, "2"); + // stack: ref new_length + // fetch old length + ArrayUtils(_context).retrieveLength(_type, 1); + // stack: ref new_length old_length + solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "2"); - // Special case for short byte arrays, they are stored together with their length - if (_type.isByteArray()) - { - eth::AssemblyItem regularPath = m_context.newTag(); - // We start by a large case-distinction about the old and new length of the byte array. + // Special case for short byte arrays, they are stored together with their length + if (_type.isByteArray()) + { + eth::AssemblyItem regularPath = _context.newTag(); + // We start by a large case-distinction about the old and new length of the byte array. - m_context << Instruction::DUP3 << Instruction::SLOAD; - // stack: ref new_length current_length ref_value + _context << Instruction::DUP3 << Instruction::SLOAD; + // stack: ref new_length current_length ref_value - solAssert(m_context.stackHeight() - stackHeightStart == 4 - 2, "3"); - m_context << Instruction::DUP2 << u256(31) << Instruction::LT; - eth::AssemblyItem currentIsLong = m_context.appendConditionalJump(); - m_context << Instruction::DUP3 << u256(31) << Instruction::LT; - eth::AssemblyItem newIsLong = m_context.appendConditionalJump(); + solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); + _context << Instruction::DUP2 << u256(31) << Instruction::LT; + eth::AssemblyItem currentIsLong = _context.appendConditionalJump(); + _context << Instruction::DUP3 << u256(31) << Instruction::LT; + eth::AssemblyItem newIsLong = _context.appendConditionalJump(); - // Here: short -> short + // Here: short -> short - // Compute 1 << (256 - 8 * new_size) - eth::AssemblyItem shortToShort = m_context.newTag(); - m_context << shortToShort; - m_context << Instruction::DUP3 << u256(8) << Instruction::MUL; - m_context << u256(0x100) << Instruction::SUB; - m_context << u256(2) << Instruction::EXP; - // Divide and multiply by that value, clearing bits. - m_context << Instruction::DUP1 << Instruction::SWAP2; - m_context << Instruction::DIV << Instruction::MUL; - // Insert 2*length. - m_context << Instruction::DUP3 << Instruction::DUP1 << Instruction::ADD; - m_context << Instruction::OR; - // Store. - m_context << Instruction::DUP4 << Instruction::SSTORE; - solAssert(m_context.stackHeight() - stackHeightStart == 3 - 2, "3"); - m_context.appendJumpTo(resizeEnd); + // Compute 1 << (256 - 8 * new_size) + eth::AssemblyItem shortToShort = _context.newTag(); + _context << shortToShort; + _context << Instruction::DUP3 << u256(8) << Instruction::MUL; + _context << u256(0x100) << Instruction::SUB; + _context << u256(2) << Instruction::EXP; + // Divide and multiply by that value, clearing bits. + _context << Instruction::DUP1 << Instruction::SWAP2; + _context << Instruction::DIV << Instruction::MUL; + // Insert 2*length. + _context << Instruction::DUP3 << Instruction::DUP1 << Instruction::ADD; + _context << Instruction::OR; + // Store. + _context << Instruction::DUP4 << Instruction::SSTORE; + solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "3"); + _context.appendJumpTo(resizeEnd); - m_context.adjustStackOffset(1); // we have to do that because of the jumps - // Here: short -> long + _context.adjustStackOffset(1); // we have to do that because of the jumps + // Here: short -> long - m_context << newIsLong; - // stack: ref new_length current_length ref_value - solAssert(m_context.stackHeight() - stackHeightStart == 4 - 2, "3"); - // Zero out lower-order byte. - m_context << u256(0xff) << Instruction::NOT << Instruction::AND; - // Store at data location. - m_context << Instruction::DUP4; - CompilerUtils(m_context).computeHashStatic(); - m_context << Instruction::SSTORE; - // stack: ref new_length current_length - // Store new length: Compule 2*length + 1 and store it. - m_context << Instruction::DUP2 << Instruction::DUP1 << Instruction::ADD; - m_context << u256(1) << Instruction::ADD; - // stack: ref new_length current_length 2*new_length+1 - m_context << Instruction::DUP4 << Instruction::SSTORE; - solAssert(m_context.stackHeight() - stackHeightStart == 3 - 2, "3"); - m_context.appendJumpTo(resizeEnd); + _context << newIsLong; + // stack: ref new_length current_length ref_value + solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); + // Zero out lower-order byte. + _context << u256(0xff) << Instruction::NOT << Instruction::AND; + // Store at data location. + _context << Instruction::DUP4; + CompilerUtils(_context).computeHashStatic(); + _context << Instruction::SSTORE; + // stack: ref new_length current_length + // Store new length: Compule 2*length + 1 and store it. + _context << Instruction::DUP2 << Instruction::DUP1 << Instruction::ADD; + _context << u256(1) << Instruction::ADD; + // stack: ref new_length current_length 2*new_length+1 + _context << Instruction::DUP4 << Instruction::SSTORE; + solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "3"); + _context.appendJumpTo(resizeEnd); - m_context.adjustStackOffset(1); // we have to do that because of the jumps + _context.adjustStackOffset(1); // we have to do that because of the jumps - m_context << currentIsLong; - m_context << Instruction::DUP3 << u256(31) << Instruction::LT; - m_context.appendConditionalJumpTo(regularPath); + _context << currentIsLong; + _context << Instruction::DUP3 << u256(31) << Instruction::LT; + _context.appendConditionalJumpTo(regularPath); - // Here: long -> short - // Read the first word of the data and store it on the stack. Clear the data location and - // then jump to the short -> short case. + // Here: long -> short + // Read the first word of the data and store it on the stack. Clear the data location and + // then jump to the short -> short case. - // stack: ref new_length current_length ref_value - solAssert(m_context.stackHeight() - stackHeightStart == 4 - 2, "3"); - m_context << Instruction::POP << Instruction::DUP3; - CompilerUtils(m_context).computeHashStatic(); - m_context << Instruction::DUP1 << Instruction::SLOAD << Instruction::SWAP1; - // stack: ref new_length current_length first_word data_location - m_context << Instruction::DUP3; - convertLengthToSize(_type); - m_context << Instruction::DUP2 << Instruction::ADD << Instruction::SWAP1; - // stack: ref new_length current_length first_word data_location_end data_location - clearStorageLoop(IntegerType(256)); - m_context << Instruction::POP; - // stack: ref new_length current_length first_word - solAssert(m_context.stackHeight() - stackHeightStart == 4 - 2, "3"); - m_context.appendJumpTo(shortToShort); + // stack: ref new_length current_length ref_value + solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); + _context << Instruction::POP << Instruction::DUP3; + CompilerUtils(_context).computeHashStatic(); + _context << Instruction::DUP1 << Instruction::SLOAD << Instruction::SWAP1; + // stack: ref new_length current_length first_word data_location + _context << Instruction::DUP3; + ArrayUtils(_context).convertLengthToSize(_type); + _context << Instruction::DUP2 << Instruction::ADD << Instruction::SWAP1; + // stack: ref new_length current_length first_word data_location_end data_location + ArrayUtils(_context).clearStorageLoop(IntegerType(256)); + _context << Instruction::POP; + // stack: ref new_length current_length first_word + solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); + _context.appendJumpTo(shortToShort); - m_context << regularPath; - // stack: ref new_length current_length ref_value - m_context << Instruction::POP; - } + _context << regularPath; + // stack: ref new_length current_length ref_value + _context << Instruction::POP; + } - // Change of length for a regular array (i.e. length at location, data at sha3(location)). - // stack: ref new_length old_length - // store new length - m_context << Instruction::DUP2; - if (_type.isByteArray()) - // For a "long" byte array, store length as 2*length+1 - m_context << Instruction::DUP1 << Instruction::ADD << u256(1) << Instruction::ADD; - m_context<< Instruction::DUP4 << Instruction::SSTORE; - // skip if size is not reduced - m_context << Instruction::DUP2 << Instruction::DUP2 - << Instruction::ISZERO << Instruction::GT; - m_context.appendConditionalJumpTo(resizeEnd); + // Change of length for a regular array (i.e. length at location, data at sha3(location)). + // stack: ref new_length old_length + // store new length + _context << Instruction::DUP2; + if (_type.isByteArray()) + // For a "long" byte array, store length as 2*length+1 + _context << Instruction::DUP1 << Instruction::ADD << u256(1) << Instruction::ADD; + _context<< Instruction::DUP4 << Instruction::SSTORE; + // skip if size is not reduced + _context << Instruction::DUP2 << Instruction::DUP2 + << Instruction::ISZERO << Instruction::GT; + _context.appendConditionalJumpTo(resizeEnd); - // size reduced, clear the end of the array - // stack: ref new_length old_length - convertLengthToSize(_type); - m_context << Instruction::DUP2; - convertLengthToSize(_type); - // stack: ref new_length old_size new_size - // compute data positions - m_context << Instruction::DUP4; - CompilerUtils(m_context).computeHashStatic(); - // stack: ref new_length old_size new_size data_pos - m_context << Instruction::SWAP2 << Instruction::DUP3 << Instruction::ADD; - // stack: ref new_length data_pos new_size delete_end - m_context << Instruction::SWAP2 << Instruction::ADD; - // stack: ref new_length delete_end delete_start - if (_type.isByteArray() || _type.baseType()->storageBytes() < 32) - clearStorageLoop(IntegerType(256)); - else - clearStorageLoop(*_type.baseType()); + // size reduced, clear the end of the array + // stack: ref new_length old_length + ArrayUtils(_context).convertLengthToSize(_type); + _context << Instruction::DUP2; + ArrayUtils(_context).convertLengthToSize(_type); + // stack: ref new_length old_size new_size + // compute data positions + _context << Instruction::DUP4; + CompilerUtils(_context).computeHashStatic(); + // stack: ref new_length old_size new_size data_pos + _context << Instruction::SWAP2 << Instruction::DUP3 << Instruction::ADD; + // stack: ref new_length data_pos new_size delete_end + _context << Instruction::SWAP2 << Instruction::ADD; + // stack: ref new_length delete_end delete_start + if (_type.isByteArray() || _type.baseType()->storageBytes() < 32) + ArrayUtils(_context).clearStorageLoop(IntegerType(256)); + else + ArrayUtils(_context).clearStorageLoop(*_type.baseType()); - m_context << resizeEnd; - // cleanup - m_context << Instruction::POP << Instruction::POP << Instruction::POP; - solAssert(m_context.stackHeight() == stackHeightStart - 2, ""); + _context << resizeEnd; + // cleanup + _context << Instruction::POP << Instruction::POP << Instruction::POP; + solAssert(_context.stackHeight() == stackHeightStart - 2, ""); + } + ); } void ArrayUtils::clearStorageLoop(Type const& _type) const diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index fcd39b333..dad227c7b 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -21,15 +21,18 @@ */ #include -#include -#include -#include +#include #include #include #include #include #include +#include + +#include +#include + using namespace std; namespace dev @@ -57,6 +60,51 @@ void CompilerContext::startFunction(Declaration const& _function) *this << functionEntryLabel(_function); } +void CompilerContext::callLowLevelFunction( + string const& _name, + unsigned _inArgs, + unsigned _outArgs, + function const& _generator +) +{ + eth::AssemblyItem retTag = pushNewTag(); + CompilerUtils(*this).moveIntoStack(_inArgs); + + auto it = m_lowLevelFunctions.find(_name); + if (it == m_lowLevelFunctions.end()) + { + eth::AssemblyItem tag = newTag().pushTag(); + m_lowLevelFunctions.insert(make_pair(_name, tag)); + m_lowLevelFunctionGenerationQueue.push(make_tuple(_name, _inArgs, _outArgs, _generator)); + *this << tag; + } + else + *this << it->second; + appendJump(eth::AssemblyItem::JumpType::IntoFunction); + adjustStackOffset(_outArgs - 1 - _inArgs); + *this << retTag.tag(); +} + +void CompilerContext::appendMissingLowLevelFunctions() +{ + while (!m_lowLevelFunctionGenerationQueue.empty()) + { + string name; + unsigned inArgs; + unsigned outArgs; + function generator; + tie(name, inArgs, outArgs, generator) = m_lowLevelFunctionGenerationQueue.front(); + m_lowLevelFunctionGenerationQueue.pop(); + + setStackOffset(inArgs + 1); + *this << m_lowLevelFunctions.at(name).tag(); + generator(*this); + CompilerUtils(*this).moveToStackTop(outArgs); + appendJump(eth::AssemblyItem::JumpType::OutOfFunction); + solAssert(stackHeight() == outArgs, "Invalid stack height in low-level function " + name + "."); + } +} + void CompilerContext::addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent) { @@ -125,21 +173,6 @@ Declaration const* CompilerContext::nextFunctionToCompile() const return m_functionCompilationQueue.nextFunctionToCompile(); } -eth::AssemblyItem const* CompilerContext::lowLevelFunctionEntryPoint(string const& _name) const -{ - auto it = m_lowLevelFunctions.find(_name); - if (it == m_lowLevelFunctions.end()) - return nullptr; - else - return *it; -} - -void CompilerContext::addLowLevelFunction(string const& _name, eth::AssemblyItem const& _label) -{ - solAssert(lowLevelFunctionEntryPoint(_name) != nullptr, "Low level function with that name already exists."); - m_lowLevelFunctions[_name] = _label.pushTag(); -} - ModifierDefinition const& CompilerContext::functionModifier(string const& _name) const { solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 34befc13d..f024b0103 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -22,16 +22,20 @@ #pragma once +#include +#include +#include + +#include +#include + +#include + #include #include #include #include -#include -#include -#include -#include -#include -#include +#include namespace dev { namespace solidity { @@ -90,11 +94,18 @@ public: /// as "having code". void startFunction(Declaration const& _function); - /// Returns the label of the low level function with the given name or nullptr if it - /// does not exist. The lifetime of the returned pointer is short. - eth::AssemblyItem const* lowLevelFunctionEntryPoint(std::string const& _name) const; - /// Inserts a low level function entry point into the list of low level functions. - void addLowLevelFunction(std::string const& _name, eth::AssemblyItem const& _label); + /// Appends a call to the named low-level function and inserts the generator into the + /// list of low-level-functions to be generated, unless it already exists. + /// Note that the generator should not assume that objects are still alive when it is called, + /// unless they are guaranteed to be alive for the whole run of the compiler (AST nodes, for example). + void callLowLevelFunction( + std::string const& _name, + unsigned _inArgs, + unsigned _outArgs, + std::function const& _generator + ); + /// Generates the code for missing low-level functions, i.e. calls the generators passed above. + void appendMissingLowLevelFunctions(); ModifierDefinition const& functionModifier(std::string const& _name) const; /// Returns the distance of the given local variable from the bottom of the stack (of the current function). @@ -256,6 +267,8 @@ private: size_t m_runtimeSub = -1; /// An index of low-level function labels by name. std::map m_lowLevelFunctions; + /// The queue of low-level functions to generate. + std::queue>> m_lowLevelFunctionGenerationQueue; }; } diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 7d382aba8..caf3b1acd 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -820,37 +820,46 @@ void CompilerUtils::pushZeroValue(Type const& _type) } solAssert(referenceType->location() == DataLocation::Memory, ""); - m_context << u256(max(32u, _type.calldataEncodedSize())); - allocateMemory(); - m_context << Instruction::DUP1; + TypePointer type = _type.shared_from_this(); + m_context.callLowLevelFunction( + "$pushZeroValue_" + referenceType->identifier(), + 0, + 1, + [type](CompilerContext& _context) { + CompilerUtils utils(_context); + _context << u256(max(32u, type->calldataEncodedSize())); + utils.allocateMemory(); + _context << Instruction::DUP1; - if (auto structType = dynamic_cast(&_type)) - for (auto const& member: structType->members(nullptr)) - { - pushZeroValue(*member.type); - storeInMemoryDynamic(*member.type); - } - else if (auto arrayType = dynamic_cast(&_type)) - { - if (arrayType->isDynamicallySized()) - { - // zero length - m_context << u256(0); - storeInMemoryDynamic(IntegerType(256)); - } - else if (arrayType->length() > 0) - { - m_context << arrayType->length() << Instruction::SWAP1; - // stack: items_to_do memory_pos - zeroInitialiseMemoryArray(*arrayType); - // stack: updated_memory_pos - } - } - else - solAssert(false, "Requested initialisation for unknown type: " + _type.toString()); + if (auto structType = dynamic_cast(type.get())) + for (auto const& member: structType->members(nullptr)) + { + utils.pushZeroValue(*member.type); + utils.storeInMemoryDynamic(*member.type); + } + else if (auto arrayType = dynamic_cast(type.get())) + { + if (arrayType->isDynamicallySized()) + { + // zero length + _context << u256(0); + utils.storeInMemoryDynamic(IntegerType(256)); + } + else if (arrayType->length() > 0) + { + _context << arrayType->length() << Instruction::SWAP1; + // stack: items_to_do memory_pos + utils.zeroInitialiseMemoryArray(*arrayType); + // stack: updated_memory_pos + } + } + else + solAssert(false, "Requested initialisation for unknown type: " + type->toString()); - // remove the updated memory pointer - m_context << Instruction::POP; + // remove the updated memory pointer + _context << Instruction::POP; + } + ); } void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable) diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index bdff0da43..9dc1fb37c 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -827,6 +827,7 @@ void ContractCompiler::appendMissingFunctions() function->accept(*this); solAssert(m_context.nextFunctionToCompile() != function, "Compiled the wrong function?"); } + m_context.appendMissingLowLevelFunctions(); } void ContractCompiler::appendModifierOrFunctionCode() From cea020b89e6f5bf20e27d7059ad27392e8526e9b Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 20 Jan 2017 14:19:25 +0100 Subject: [PATCH 069/118] Convert ArrayUtils routines into low-level functions. --- libsolidity/codegen/ArrayUtils.cpp | 473 +++++++++++++++-------------- 1 file changed, 248 insertions(+), 225 deletions(-) diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 6f270b4dd..f6cb7aac2 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -40,9 +40,9 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons // stack layout: [source_ref] [source length] target_ref (top) solAssert(_targetType.location() == DataLocation::Storage, ""); - IntegerType uint256(256); - Type const* targetBaseType = _targetType.isByteArray() ? &uint256 : &(*_targetType.baseType()); - Type const* sourceBaseType = _sourceType.isByteArray() ? &uint256 : &(*_sourceType.baseType()); + TypePointer uint256 = make_shared(256); + TypePointer targetBaseType = _targetType.isByteArray() ? uint256 : _targetType.baseType(); + TypePointer sourceBaseType = _sourceType.isByteArray() ? uint256 : _sourceType.baseType(); // TODO unroll loop for small sizes @@ -70,202 +70,216 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons } // stack: target_ref source_ref source_length - m_context << Instruction::DUP3; - // stack: target_ref source_ref source_length target_ref - retrieveLength(_targetType); - // stack: target_ref source_ref source_length target_ref target_length - if (_targetType.isDynamicallySized()) - // store new target length - if (!_targetType.isByteArray()) - // Otherwise, length will be stored below. - m_context << Instruction::DUP3 << Instruction::DUP3 << Instruction::SSTORE; - if (sourceBaseType->category() == Type::Category::Mapping) - { - solAssert(targetBaseType->category() == Type::Category::Mapping, ""); - solAssert(_sourceType.location() == DataLocation::Storage, ""); - // nothing to copy - m_context - << Instruction::POP << Instruction::POP - << Instruction::POP << Instruction::POP; - return; - } - // stack: target_ref source_ref source_length target_ref target_length - // compute hashes (data positions) - m_context << Instruction::SWAP1; - if (_targetType.isDynamicallySized()) - CompilerUtils(m_context).computeHashStatic(); - // stack: target_ref source_ref source_length target_length target_data_pos - m_context << Instruction::SWAP1; - convertLengthToSize(_targetType); - m_context << Instruction::DUP2 << Instruction::ADD; - // stack: target_ref source_ref source_length target_data_pos target_data_end - m_context << Instruction::SWAP3; - // stack: target_ref target_data_end source_length target_data_pos source_ref - - eth::AssemblyItem copyLoopEndWithoutByteOffset = m_context.newTag(); - - // special case for short byte arrays: Store them together with their length. - if (_targetType.isByteArray()) - { - // stack: target_ref target_data_end source_length target_data_pos source_ref - m_context << Instruction::DUP3 << u256(31) << Instruction::LT; - eth::AssemblyItem longByteArray = m_context.appendConditionalJump(); - // store the short byte array - solAssert(_sourceType.isByteArray(), ""); - if (_sourceType.location() == DataLocation::Storage) + TypePointer targetType = _targetType.shared_from_this(); + TypePointer sourceType = _sourceType.shared_from_this(); + m_context.callLowLevelFunction( + "$copyArrayToStorage_" + sourceType->identifier() + "_to_" + targetType->identifier(), + 3, + 1, + [=](CompilerContext& _context) { - // just copy the slot, it contains length and data - m_context << Instruction::DUP1 << Instruction::SLOAD; - m_context << Instruction::DUP6 << Instruction::SSTORE; - } - else - { - m_context << Instruction::DUP1; - CompilerUtils(m_context).loadFromMemoryDynamic(*sourceBaseType, fromCalldata, true, false); - // stack: target_ref target_data_end source_length target_data_pos source_ref value - // clear the lower-order byte - which will hold the length - m_context << u256(0xff) << Instruction::NOT << Instruction::AND; - // fetch the length and shift it left by one - m_context << Instruction::DUP4 << Instruction::DUP1 << Instruction::ADD; - // combine value and length and store them - m_context << Instruction::OR << Instruction::DUP6 << Instruction::SSTORE; - } - // end of special case, jump right into cleaning target data area - m_context.appendJumpTo(copyLoopEndWithoutByteOffset); - m_context << longByteArray; - // Store length (2*length+1) - m_context << Instruction::DUP3 << Instruction::DUP1 << Instruction::ADD; - m_context << u256(1) << Instruction::ADD; - m_context << Instruction::DUP6 << Instruction::SSTORE; - } + ArrayUtils utils(_context); + ArrayType const& _sourceType = dynamic_cast(*sourceType); + ArrayType const& _targetType = dynamic_cast(*targetType); + // stack: target_ref source_ref source_length + _context << Instruction::DUP3; + // stack: target_ref source_ref source_length target_ref + utils.retrieveLength(_targetType); + // stack: target_ref source_ref source_length target_ref target_length + if (_targetType.isDynamicallySized()) + // store new target length + if (!_targetType.isByteArray()) + // Otherwise, length will be stored below. + _context << Instruction::DUP3 << Instruction::DUP3 << Instruction::SSTORE; + if (sourceBaseType->category() == Type::Category::Mapping) + { + solAssert(targetBaseType->category() == Type::Category::Mapping, ""); + solAssert(_sourceType.location() == DataLocation::Storage, ""); + // nothing to copy + _context + << Instruction::POP << Instruction::POP + << Instruction::POP << Instruction::POP; + return; + } + // stack: target_ref source_ref source_length target_ref target_length + // compute hashes (data positions) + _context << Instruction::SWAP1; + if (_targetType.isDynamicallySized()) + CompilerUtils(_context).computeHashStatic(); + // stack: target_ref source_ref source_length target_length target_data_pos + _context << Instruction::SWAP1; + utils.convertLengthToSize(_targetType); + _context << Instruction::DUP2 << Instruction::ADD; + // stack: target_ref source_ref source_length target_data_pos target_data_end + _context << Instruction::SWAP3; + // stack: target_ref target_data_end source_length target_data_pos source_ref - // skip copying if source length is zero - m_context << Instruction::DUP3 << Instruction::ISZERO; - m_context.appendConditionalJumpTo(copyLoopEndWithoutByteOffset); + eth::AssemblyItem copyLoopEndWithoutByteOffset = _context.newTag(); - if (_sourceType.location() == DataLocation::Storage && _sourceType.isDynamicallySized()) - CompilerUtils(m_context).computeHashStatic(); - // stack: target_ref target_data_end source_length target_data_pos source_data_pos - m_context << Instruction::SWAP2; - convertLengthToSize(_sourceType); - m_context << Instruction::DUP3 << Instruction::ADD; - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end - if (haveByteOffsetTarget) - m_context << u256(0); - if (haveByteOffsetSource) - m_context << u256(0); - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] - eth::AssemblyItem copyLoopStart = m_context.newTag(); - m_context << copyLoopStart; - // check for loop condition - m_context - << dupInstruction(3 + byteOffsetSize) << dupInstruction(2 + byteOffsetSize) - << Instruction::GT << Instruction::ISZERO; - eth::AssemblyItem copyLoopEnd = m_context.appendConditionalJump(); - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] - // copy - if (sourceBaseType->category() == Type::Category::Array) - { - solAssert(byteOffsetSize == 0, "Byte offset for array as base type."); - auto const& sourceBaseArrayType = dynamic_cast(*sourceBaseType); - m_context << Instruction::DUP3; - if (sourceBaseArrayType.location() == DataLocation::Memory) - m_context << Instruction::MLOAD; - m_context << Instruction::DUP3; - copyArrayToStorage(dynamic_cast(*targetBaseType), sourceBaseArrayType); - m_context << Instruction::POP; - } - else if (directCopy) - { - solAssert(byteOffsetSize == 0, "Byte offset for direct copy."); - m_context - << Instruction::DUP3 << Instruction::SLOAD - << Instruction::DUP3 << Instruction::SSTORE; - } - else - { - // Note that we have to copy each element on its own in case conversion is involved. - // We might copy too much if there is padding at the last element, but this way end - // checking is easier. - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] - m_context << dupInstruction(3 + byteOffsetSize); - if (_sourceType.location() == DataLocation::Storage) - { + // special case for short byte arrays: Store them together with their length. + if (_targetType.isByteArray()) + { + // stack: target_ref target_data_end source_length target_data_pos source_ref + _context << Instruction::DUP3 << u256(31) << Instruction::LT; + eth::AssemblyItem longByteArray = _context.appendConditionalJump(); + // store the short byte array + solAssert(_sourceType.isByteArray(), ""); + if (_sourceType.location() == DataLocation::Storage) + { + // just copy the slot, it contains length and data + _context << Instruction::DUP1 << Instruction::SLOAD; + _context << Instruction::DUP6 << Instruction::SSTORE; + } + else + { + _context << Instruction::DUP1; + CompilerUtils(_context).loadFromMemoryDynamic(*sourceBaseType, fromCalldata, true, false); + // stack: target_ref target_data_end source_length target_data_pos source_ref value + // clear the lower-order byte - which will hold the length + _context << u256(0xff) << Instruction::NOT << Instruction::AND; + // fetch the length and shift it left by one + _context << Instruction::DUP4 << Instruction::DUP1 << Instruction::ADD; + // combine value and length and store them + _context << Instruction::OR << Instruction::DUP6 << Instruction::SSTORE; + } + // end of special case, jump right into cleaning target data area + _context.appendJumpTo(copyLoopEndWithoutByteOffset); + _context << longByteArray; + // Store length (2*length+1) + _context << Instruction::DUP3 << Instruction::DUP1 << Instruction::ADD; + _context << u256(1) << Instruction::ADD; + _context << Instruction::DUP6 << Instruction::SSTORE; + } + + // skip copying if source length is zero + _context << Instruction::DUP3 << Instruction::ISZERO; + _context.appendConditionalJumpTo(copyLoopEndWithoutByteOffset); + + if (_sourceType.location() == DataLocation::Storage && _sourceType.isDynamicallySized()) + CompilerUtils(_context).computeHashStatic(); + // stack: target_ref target_data_end source_length target_data_pos source_data_pos + _context << Instruction::SWAP2; + utils.convertLengthToSize(_sourceType); + _context << Instruction::DUP3 << Instruction::ADD; + // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end + if (haveByteOffsetTarget) + _context << u256(0); if (haveByteOffsetSource) - m_context << Instruction::DUP2; + _context << u256(0); + // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] + eth::AssemblyItem copyLoopStart = _context.newTag(); + _context << copyLoopStart; + // check for loop condition + _context + << dupInstruction(3 + byteOffsetSize) << dupInstruction(2 + byteOffsetSize) + << Instruction::GT << Instruction::ISZERO; + eth::AssemblyItem copyLoopEnd = _context.appendConditionalJump(); + // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] + // copy + if (sourceBaseType->category() == Type::Category::Array) + { + solAssert(byteOffsetSize == 0, "Byte offset for array as base type."); + auto const& sourceBaseArrayType = dynamic_cast(*sourceBaseType); + _context << Instruction::DUP3; + if (sourceBaseArrayType.location() == DataLocation::Memory) + _context << Instruction::MLOAD; + _context << Instruction::DUP3; + utils.copyArrayToStorage(dynamic_cast(*targetBaseType), sourceBaseArrayType); + _context << Instruction::POP; + } + else if (directCopy) + { + solAssert(byteOffsetSize == 0, "Byte offset for direct copy."); + _context + << Instruction::DUP3 << Instruction::SLOAD + << Instruction::DUP3 << Instruction::SSTORE; + } else - m_context << u256(0); - StorageItem(m_context, *sourceBaseType).retrieveValue(SourceLocation(), true); + { + // Note that we have to copy each element on its own in case conversion is involved. + // We might copy too much if there is padding at the last element, but this way end + // checking is easier. + // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] + _context << dupInstruction(3 + byteOffsetSize); + if (_sourceType.location() == DataLocation::Storage) + { + if (haveByteOffsetSource) + _context << Instruction::DUP2; + else + _context << u256(0); + StorageItem(_context, *sourceBaseType).retrieveValue(SourceLocation(), true); + } + else if (sourceBaseType->isValueType()) + CompilerUtils(_context).loadFromMemoryDynamic(*sourceBaseType, fromCalldata, true, false); + else + solUnimplemented("Copying of type " + _sourceType.toString(false) + " to storage not yet supported."); + // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] ... + solAssert( + 2 + byteOffsetSize + sourceBaseType->sizeOnStack() <= 16, + "Stack too deep, try removing local variables." + ); + // fetch target storage reference + _context << dupInstruction(2 + byteOffsetSize + sourceBaseType->sizeOnStack()); + if (haveByteOffsetTarget) + _context << dupInstruction(1 + byteOffsetSize + sourceBaseType->sizeOnStack()); + else + _context << u256(0); + StorageItem(_context, *targetBaseType).storeValue(*sourceBaseType, SourceLocation(), true); + } + // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] + // increment source + if (haveByteOffsetSource) + utils.incrementByteOffset(sourceBaseType->storageBytes(), 1, haveByteOffsetTarget ? 5 : 4); + else + { + _context << swapInstruction(2 + byteOffsetSize); + if (sourceIsStorage) + _context << sourceBaseType->storageSize(); + else if (_sourceType.location() == DataLocation::Memory) + _context << sourceBaseType->memoryHeadSize(); + else + _context << sourceBaseType->calldataEncodedSize(true); + _context + << Instruction::ADD + << swapInstruction(2 + byteOffsetSize); + } + // increment target + if (haveByteOffsetTarget) + utils.incrementByteOffset(targetBaseType->storageBytes(), byteOffsetSize, byteOffsetSize + 2); + else + _context + << swapInstruction(1 + byteOffsetSize) + << targetBaseType->storageSize() + << Instruction::ADD + << swapInstruction(1 + byteOffsetSize); + _context.appendJumpTo(copyLoopStart); + _context << copyLoopEnd; + if (haveByteOffsetTarget) + { + // clear elements that might be left over in the current slot in target + // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end target_byte_offset [source_byte_offset] + _context << dupInstruction(byteOffsetSize) << Instruction::ISZERO; + eth::AssemblyItem copyCleanupLoopEnd = _context.appendConditionalJump(); + _context << dupInstruction(2 + byteOffsetSize) << dupInstruction(1 + byteOffsetSize); + StorageItem(_context, *targetBaseType).setToZero(SourceLocation(), true); + utils.incrementByteOffset(targetBaseType->storageBytes(), byteOffsetSize, byteOffsetSize + 2); + _context.appendJumpTo(copyLoopEnd); + + _context << copyCleanupLoopEnd; + _context << Instruction::POP; // might pop the source, but then target is popped next + } + if (haveByteOffsetSource) + _context << Instruction::POP; + _context << copyLoopEndWithoutByteOffset; + + // zero-out leftovers in target + // stack: target_ref target_data_end source_data_pos target_data_pos_updated source_data_end + _context << Instruction::POP << Instruction::SWAP1 << Instruction::POP; + // stack: target_ref target_data_end target_data_pos_updated + utils.clearStorageLoop(*targetBaseType); + _context << Instruction::POP; } - else if (sourceBaseType->isValueType()) - CompilerUtils(m_context).loadFromMemoryDynamic(*sourceBaseType, fromCalldata, true, false); - else - solUnimplemented("Copying of type " + _sourceType.toString(false) + " to storage not yet supported."); - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] ... - solAssert( - 2 + byteOffsetSize + sourceBaseType->sizeOnStack() <= 16, - "Stack too deep, try removing local variables." - ); - // fetch target storage reference - m_context << dupInstruction(2 + byteOffsetSize + sourceBaseType->sizeOnStack()); - if (haveByteOffsetTarget) - m_context << dupInstruction(1 + byteOffsetSize + sourceBaseType->sizeOnStack()); - else - m_context << u256(0); - StorageItem(m_context, *targetBaseType).storeValue(*sourceBaseType, SourceLocation(), true); - } - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] - // increment source - if (haveByteOffsetSource) - incrementByteOffset(sourceBaseType->storageBytes(), 1, haveByteOffsetTarget ? 5 : 4); - else - { - m_context << swapInstruction(2 + byteOffsetSize); - if (sourceIsStorage) - m_context << sourceBaseType->storageSize(); - else if (_sourceType.location() == DataLocation::Memory) - m_context << sourceBaseType->memoryHeadSize(); - else - m_context << sourceBaseType->calldataEncodedSize(true); - m_context - << Instruction::ADD - << swapInstruction(2 + byteOffsetSize); - } - // increment target - if (haveByteOffsetTarget) - incrementByteOffset(targetBaseType->storageBytes(), byteOffsetSize, byteOffsetSize + 2); - else - m_context - << swapInstruction(1 + byteOffsetSize) - << targetBaseType->storageSize() - << Instruction::ADD - << swapInstruction(1 + byteOffsetSize); - m_context.appendJumpTo(copyLoopStart); - m_context << copyLoopEnd; - if (haveByteOffsetTarget) - { - // clear elements that might be left over in the current slot in target - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end target_byte_offset [source_byte_offset] - m_context << dupInstruction(byteOffsetSize) << Instruction::ISZERO; - eth::AssemblyItem copyCleanupLoopEnd = m_context.appendConditionalJump(); - m_context << dupInstruction(2 + byteOffsetSize) << dupInstruction(1 + byteOffsetSize); - StorageItem(m_context, *targetBaseType).setToZero(SourceLocation(), true); - incrementByteOffset(targetBaseType->storageBytes(), byteOffsetSize, byteOffsetSize + 2); - m_context.appendJumpTo(copyLoopEnd); - - m_context << copyCleanupLoopEnd; - m_context << Instruction::POP; // might pop the source, but then target is popped next - } - if (haveByteOffsetSource) - m_context << Instruction::POP; - m_context << copyLoopEndWithoutByteOffset; - - // zero-out leftovers in target - // stack: target_ref target_data_end source_data_pos target_data_pos_updated source_data_end - m_context << Instruction::POP << Instruction::SWAP1 << Instruction::POP; - // stack: target_ref target_data_end target_data_pos_updated - clearStorageLoop(*targetBaseType); - m_context << Instruction::POP; + ); } void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWordBoundaries) const @@ -759,41 +773,50 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const void ArrayUtils::clearStorageLoop(Type const& _type) const { - unsigned stackHeightStart = m_context.stackHeight(); - if (_type.category() == Type::Category::Mapping) - { - m_context << Instruction::POP; - return; - } - // stack: end_pos pos + TypePointer type = _type.shared_from_this(); + m_context.callLowLevelFunction( + "$clearStorageLoop_" + _type.identifier(), + 2, + 1, + [type](CompilerContext& _context) + { + unsigned stackHeightStart = _context.stackHeight(); + if (type->category() == Type::Category::Mapping) + { + _context << Instruction::POP; + return; + } + // stack: end_pos pos - // jump to and return from the loop to allow for duplicate code removal - eth::AssemblyItem returnTag = m_context.pushNewTag(); - m_context << Instruction::SWAP2 << Instruction::SWAP1; + // jump to and return from the loop to allow for duplicate code removal + eth::AssemblyItem returnTag = _context.pushNewTag(); + _context << Instruction::SWAP2 << Instruction::SWAP1; - // stack: end_pos pos - eth::AssemblyItem loopStart = m_context.appendJumpToNew(); - m_context << loopStart; - // check for loop condition - m_context << Instruction::DUP1 << Instruction::DUP3 - << Instruction::GT << Instruction::ISZERO; - eth::AssemblyItem zeroLoopEnd = m_context.newTag(); - m_context.appendConditionalJumpTo(zeroLoopEnd); - // delete - m_context << u256(0); - StorageItem(m_context, _type).setToZero(SourceLocation(), false); - m_context << Instruction::POP; - // increment - m_context << _type.storageSize() << Instruction::ADD; - m_context.appendJumpTo(loopStart); - // cleanup - m_context << zeroLoopEnd; - m_context << Instruction::POP << Instruction::SWAP1; - // "return" - m_context << Instruction::JUMP; + // stack: end_pos pos + eth::AssemblyItem loopStart = _context.appendJumpToNew(); + _context << loopStart; + // check for loop condition + _context << Instruction::DUP1 << Instruction::DUP3 + << Instruction::GT << Instruction::ISZERO; + eth::AssemblyItem zeroLoopEnd = _context.newTag(); + _context.appendConditionalJumpTo(zeroLoopEnd); + // delete + _context << u256(0); + StorageItem(_context, *type).setToZero(SourceLocation(), false); + _context << Instruction::POP; + // increment + _context << type->storageSize() << Instruction::ADD; + _context.appendJumpTo(loopStart); + // cleanup + _context << zeroLoopEnd; + _context << Instruction::POP << Instruction::SWAP1; + // "return" + _context << Instruction::JUMP; - m_context << returnTag; - solAssert(m_context.stackHeight() == stackHeightStart - 1, ""); + _context << returnTag; + solAssert(_context.stackHeight() == stackHeightStart - 1, ""); + } + ); } void ArrayUtils::convertLengthToSize(ArrayType const& _arrayType, bool _pad) const From 82a00e7dc50a8d8828fa6c192f78d4147944eccf Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 20 Jan 2017 14:30:02 +0100 Subject: [PATCH 070/118] Use shared_ptrs to enable shared_from_this. --- libsolidity/codegen/ArrayUtils.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index f6cb7aac2..0b09cc1ad 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -572,7 +572,7 @@ void ArrayUtils::clearArray(ArrayType const& _typeIn) const ArrayUtils(_context).convertLengthToSize(_type); _context << Instruction::ADD << Instruction::SWAP1; if (_type.baseType()->storageBytes() < 32) - ArrayUtils(_context).clearStorageLoop(IntegerType(256)); + ArrayUtils(_context).clearStorageLoop(*make_shared(256)); else ArrayUtils(_context).clearStorageLoop(*_type.baseType()); _context << Instruction::POP; @@ -613,7 +613,7 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const << Instruction::SWAP1; // stack: data_pos_end data_pos if (_type.isByteArray() || _type.baseType()->storageBytes() < 32) - clearStorageLoop(IntegerType(256)); + clearStorageLoop(*make_shared(256)); else clearStorageLoop(*_type.baseType()); // cleanup @@ -720,7 +720,7 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const ArrayUtils(_context).convertLengthToSize(_type); _context << Instruction::DUP2 << Instruction::ADD << Instruction::SWAP1; // stack: ref new_length current_length first_word data_location_end data_location - ArrayUtils(_context).clearStorageLoop(IntegerType(256)); + ArrayUtils(_context).clearStorageLoop(*make_shared(256)); _context << Instruction::POP; // stack: ref new_length current_length first_word solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); @@ -759,7 +759,7 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const _context << Instruction::SWAP2 << Instruction::ADD; // stack: ref new_length delete_end delete_start if (_type.isByteArray() || _type.baseType()->storageBytes() < 32) - ArrayUtils(_context).clearStorageLoop(IntegerType(256)); + ArrayUtils(_context).clearStorageLoop(*make_shared(256)); else ArrayUtils(_context).clearStorageLoop(*_type.baseType()); From 1b87e08e046b786b9c85c08696c72ed162f5f9a1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 20 Jan 2017 14:44:44 +0100 Subject: [PATCH 071/118] Changelog entry. --- Changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Changelog.md b/Changelog.md index cd3e31b20..7dd607d8d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,10 @@ Features: * AST: Use deterministic node identifiers. * Type system: Introduce type identifier strings. * Metadata: Do not include platform in the version number. + * Code generator: Extract array utils into low-level functions. + +Bugfixes: + * Code generator: Allow recursive structs. * Type checker: Allow multiple events of the same name (but with different arities or argument types) ### 0.4.8 (2017-01-13) From 23eca813f578463383afd37b76ce49d87f79c811 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 24 Jan 2017 12:06:11 +0100 Subject: [PATCH 072/118] Change clearStorageLoop to TypePointer. --- libsolidity/codegen/ArrayUtils.cpp | 29 ++++++++++++++--------------- libsolidity/codegen/ArrayUtils.h | 5 ++++- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 0b09cc1ad..4d100d1d2 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -276,7 +276,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons // stack: target_ref target_data_end source_data_pos target_data_pos_updated source_data_end _context << Instruction::POP << Instruction::SWAP1 << Instruction::POP; // stack: target_ref target_data_end target_data_pos_updated - utils.clearStorageLoop(*targetBaseType); + utils.clearStorageLoop(targetBaseType); _context << Instruction::POP; } ); @@ -572,9 +572,9 @@ void ArrayUtils::clearArray(ArrayType const& _typeIn) const ArrayUtils(_context).convertLengthToSize(_type); _context << Instruction::ADD << Instruction::SWAP1; if (_type.baseType()->storageBytes() < 32) - ArrayUtils(_context).clearStorageLoop(*make_shared(256)); + ArrayUtils(_context).clearStorageLoop(make_shared(256)); else - ArrayUtils(_context).clearStorageLoop(*_type.baseType()); + ArrayUtils(_context).clearStorageLoop(_type.baseType()); _context << Instruction::POP; } solAssert(_context.stackHeight() == stackHeightStart - 2, ""); @@ -613,9 +613,9 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const << Instruction::SWAP1; // stack: data_pos_end data_pos if (_type.isByteArray() || _type.baseType()->storageBytes() < 32) - clearStorageLoop(*make_shared(256)); + clearStorageLoop(make_shared(256)); else - clearStorageLoop(*_type.baseType()); + clearStorageLoop(_type.baseType()); // cleanup m_context << endTag; m_context << Instruction::POP; @@ -720,7 +720,7 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const ArrayUtils(_context).convertLengthToSize(_type); _context << Instruction::DUP2 << Instruction::ADD << Instruction::SWAP1; // stack: ref new_length current_length first_word data_location_end data_location - ArrayUtils(_context).clearStorageLoop(*make_shared(256)); + ArrayUtils(_context).clearStorageLoop(make_shared(256)); _context << Instruction::POP; // stack: ref new_length current_length first_word solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); @@ -759,9 +759,9 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const _context << Instruction::SWAP2 << Instruction::ADD; // stack: ref new_length delete_end delete_start if (_type.isByteArray() || _type.baseType()->storageBytes() < 32) - ArrayUtils(_context).clearStorageLoop(*make_shared(256)); + ArrayUtils(_context).clearStorageLoop(make_shared(256)); else - ArrayUtils(_context).clearStorageLoop(*_type.baseType()); + ArrayUtils(_context).clearStorageLoop(_type.baseType()); _context << resizeEnd; // cleanup @@ -771,17 +771,16 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const ); } -void ArrayUtils::clearStorageLoop(Type const& _type) const +void ArrayUtils::clearStorageLoop(TypePointer const& _type) const { - TypePointer type = _type.shared_from_this(); m_context.callLowLevelFunction( - "$clearStorageLoop_" + _type.identifier(), + "$clearStorageLoop_" + _type->identifier(), 2, 1, - [type](CompilerContext& _context) + [_type](CompilerContext& _context) { unsigned stackHeightStart = _context.stackHeight(); - if (type->category() == Type::Category::Mapping) + if (_type->category() == Type::Category::Mapping) { _context << Instruction::POP; return; @@ -802,10 +801,10 @@ void ArrayUtils::clearStorageLoop(Type const& _type) const _context.appendConditionalJumpTo(zeroLoopEnd); // delete _context << u256(0); - StorageItem(_context, *type).setToZero(SourceLocation(), false); + StorageItem(_context, *_type).setToZero(SourceLocation(), false); _context << Instruction::POP; // increment - _context << type->storageSize() << Instruction::ADD; + _context << _type->storageSize() << Instruction::ADD; _context.appendJumpTo(loopStart); // cleanup _context << zeroLoopEnd; diff --git a/libsolidity/codegen/ArrayUtils.h b/libsolidity/codegen/ArrayUtils.h index d0ba2892b..806fbea7b 100644 --- a/libsolidity/codegen/ArrayUtils.h +++ b/libsolidity/codegen/ArrayUtils.h @@ -22,6 +22,8 @@ #pragma once +#include + namespace dev { namespace solidity @@ -30,6 +32,7 @@ namespace solidity class CompilerContext; class Type; class ArrayType; +using TypePointer = std::shared_ptr; /** * Class that provides code generation for handling arrays. @@ -67,7 +70,7 @@ public: /// Appends a loop that clears a sequence of storage slots of the given type (excluding end). /// Stack pre: end_ref start_ref /// Stack post: end_ref - void clearStorageLoop(Type const& _type) const; + void clearStorageLoop(TypePointer const& _type) const; /// Converts length to size (number of storage slots or calldata/memory bytes). /// if @a _pad then add padding to multiples of 32 bytes for calldata/memory. /// Stack pre: length From 7e6f1b3f0008d03e6cdfa186b8f9976570865d4e Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 24 Jan 2017 12:06:21 +0100 Subject: [PATCH 073/118] Use int arithmetics for stack adjustment. --- libsolidity/codegen/CompilerContext.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index dad227c7b..e26f96e8f 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -81,7 +81,7 @@ void CompilerContext::callLowLevelFunction( else *this << it->second; appendJump(eth::AssemblyItem::JumpType::IntoFunction); - adjustStackOffset(_outArgs - 1 - _inArgs); + adjustStackOffset(int(_outArgs) - 1 - _inArgs); *this << retTag.tag(); } From 8e5f1c0d50c0512ac3e3c51ebec3afc500f4721e Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 20 Jan 2017 20:23:49 +0100 Subject: [PATCH 074/118] Test double inclusion of bytecode. --- test/libsolidity/SolidityEndToEndTest.cpp | 29 +++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index e8e5ced1a..646017fbf 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -8992,6 +8992,35 @@ BOOST_AUTO_TEST_CASE(contracts_separated_with_comment) compileAndRun(sourceCode, 0, "C2"); } +BOOST_AUTO_TEST_CASE(include_creation_bytecode_only_once) +{ + char const* sourceCode = R"( + contract D { + bytes a = hex"1237651237125387136581271652831736512837126583171583712358126123765123712538713658127165283173651283712658317158371235812612376512371253871365812716528317365128371265831715837123581261237651237125387136581271652831736512837126583171583712358126"; + bytes b = hex"1237651237125327136581271252831736512837126583171383712358126123765125712538713658127165253173651283712658357158371235812612376512371a5387136581271652a317365128371265a317158371235812612a765123712538a13658127165a83173651283712a58317158371235a126"; + function D(uint) {} + } + contract Double { + function f() { + new D(2); + } + function g() { + new D(3); + } + } + contract Single { + function f() { + new D(2); + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK_LE( + double(m_compiler.object("Double").bytecode.size()), + 1.1 * double(m_compiler.object("Single").bytecode.size()) + ); +} + BOOST_AUTO_TEST_CASE(recursive_structs) { char const* sourceCode = R"( From ead1a3b33fae83ad65210d6f82b2ca12150bf2bb Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 20 Jan 2017 20:16:18 +0100 Subject: [PATCH 075/118] Include creation code only once. --- libsolidity/codegen/ExpressionCompiler.cpp | 32 ++++++++++++---------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 81d3409ea..fe0eeb1cd 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -556,20 +556,24 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) arg->accept(*this); argumentTypes.push_back(arg->annotation().type); } - ContractDefinition const& contract = - dynamic_cast(*function.returnParameterTypes().front()).contractDefinition(); - // copy the contract's code into memory - eth::Assembly const& assembly = m_context.compiledContract(contract); - utils().fetchFreeMemoryPointer(); - // TODO we create a copy here, which is actually what we want. - // This should be revisited at the point where we fix - // https://github.com/ethereum/solidity/issues/1092 - // pushes size - auto subroutine = m_context.addSubroutine(make_shared(assembly)); - m_context << Instruction::DUP1 << subroutine; - m_context << Instruction::DUP4 << Instruction::CODECOPY; - - m_context << Instruction::ADD; + ContractDefinition const* contract = + &dynamic_cast(*function.returnParameterTypes().front()).contractDefinition(); + m_context.callLowLevelFunction( + "$copyContractCreationCodeToMemory_" + contract->type()->identifier(), + 0, + 1, + [contract](CompilerContext& _context) + { + // copy the contract's code into memory + eth::Assembly const& assembly = _context.compiledContract(*contract); + CompilerUtils(_context).fetchFreeMemoryPointer(); + // pushes size + auto subroutine = _context.addSubroutine(make_shared(assembly)); + _context << Instruction::DUP1 << subroutine; + _context << Instruction::DUP4 << Instruction::CODECOPY; + _context << Instruction::ADD; + } + ); utils().encodeToMemory(argumentTypes, function.parameterTypes()); // now on stack: memory_end_ptr // need: size, offset, endowment From 1316bb75651ea365c5246277d2dfd3d366be9070 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 24 Jan 2017 17:38:06 +0100 Subject: [PATCH 076/118] Warn about invalid checksums of addresses. --- Changelog.md | 1 + libsolidity/analysis/TypeChecker.cpp | 10 ++++ libsolidity/ast/AST.cpp | 43 +++++++++++++++- libsolidity/ast/AST.h | 5 ++ libsolidity/ast/Types.cpp | 8 +++ libsolidity/ast/Types.h | 2 + libsolidity/codegen/ExpressionCompiler.cpp | 1 + .../SolidityNameAndTypeResolution.cpp | 49 +++++++++++++++++++ 8 files changed, 117 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 7dd607d8d..a14e9ec89 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Features: * Compiler interface: Report source location for "stack too deep" errors. * AST: Use deterministic node identifiers. * Type system: Introduce type identifier strings. + * Type checker: Warn about invalid checksum for addresses and deduce type from valid ones. * Metadata: Do not include platform in the version number. * Code generator: Extract array utils into low-level functions. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index adb3d54f7..533c787b7 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1565,6 +1565,16 @@ void TypeChecker::endVisit(ElementaryTypeNameExpression const& _expr) void TypeChecker::endVisit(Literal const& _literal) { + if (_literal.looksLikeAddress()) + { + if (_literal.passesAddressChecksum()) + { + _literal.annotation().type = make_shared(0, IntegerType::Modifier::Address); + return; + } + else + warning(_literal.location(), "This looks like an address but has an invalid checksum."); + } _literal.annotation().type = Type::forLiteral(_literal); if (!_literal.annotation().type) fatalTypeError(_literal.location(), "Invalid literal value."); diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 8a43c3f75..e2b50dd71 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -20,8 +20,6 @@ * Solidity abstract syntax tree. */ -#include -#include #include #include #include @@ -30,6 +28,11 @@ #include +#include + +#include +#include + using namespace std; using namespace dev; using namespace dev::solidity; @@ -522,3 +525,39 @@ IdentifierAnnotation& Identifier::annotation() const m_annotation = new IdentifierAnnotation(); return static_cast(*m_annotation); } + +bool Literal::looksLikeAddress() const +{ + if (subDenomination() != SubDenomination::None) + return false; + + string lit = value(); + return lit.substr(0, 2) == "0x" && abs(int(lit.length()) - 42) <= 1; +} + +bool Literal::passesAddressChecksum() const +{ + string lit = value(); + solAssert(lit.substr(0, 2) == "0x", "Expected hex prefix"); + lit = lit.substr(2); + + if (lit.length() != 40) + return false; + + h256 hash = keccak256(boost::algorithm::to_lower_copy(lit, std::locale::classic())); + for (size_t i = 0; i < 40; ++i) + { + char addressCharacter = lit[i]; + bool lowerCase; + if ('a' <= addressCharacter && addressCharacter <= 'f') + lowerCase = true; + else if ('A' <= addressCharacter && addressCharacter <= 'F') + lowerCase = false; + else + continue; + unsigned nibble = (unsigned(hash[i / 2]) >> (4 * (1 - (i % 2)))) & 0xf; + if ((nibble >= 8) == lowerCase) + return false; + } + return true; +} diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 4c75f7ead..743fdaa1d 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -1584,6 +1584,11 @@ public: SubDenomination subDenomination() const { return m_subDenomination; } + /// @returns true if this looks like a checksummed address. + bool looksLikeAddress() const; + /// @returns true if it passes the address checksum test. + bool passesAddressChecksum() const; + private: Token::Value m_token; ASTPointer m_value; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index cefd0603d..971e1f184 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -405,6 +405,14 @@ string IntegerType::toString(bool) const return prefix + dev::toString(m_bits); } +u256 IntegerType::literalValue(Literal const* _literal) const +{ + solAssert(m_modifier == Modifier::Address, ""); + solAssert(_literal, ""); + solAssert(_literal->value().substr(0, 2) == "0x", ""); + return u256(_literal->value()); +} + TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const { if ( diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 1e94631e0..770cbb300 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -314,6 +314,8 @@ public: virtual std::string toString(bool _short) const override; + virtual u256 literalValue(Literal const* _literal) const override; + virtual TypePointer encodingType() const override { return shared_from_this(); } virtual TypePointer interfaceType(bool) const override { return shared_from_this(); } diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index fe0eeb1cd..bda4e04d8 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1308,6 +1308,7 @@ void ExpressionCompiler::endVisit(Literal const& _literal) { case Type::Category::RationalNumber: case Type::Category::Bool: + case Type::Category::Integer: m_context << type->literalValue(&_literal); break; case Type::Category::StringLiteral: diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index ce241c78f..b6067ea5c 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -4983,6 +4983,55 @@ BOOST_AUTO_TEST_CASE(constructible_internal_constructor) success(text); } +BOOST_AUTO_TEST_CASE(address_checksum_type_deduction) +{ + char const* text = R"( + contract C { + function f() { + var x = 0xfA0bFc97E48458494Ccd857e1A85DC91F7F0046E; + x.send(2); + } + } + )"; + success(text); +} + +BOOST_AUTO_TEST_CASE(invalid_address_checksum) +{ + char const* text = R"( + contract C { + function f() { + var x = 0xFA0bFc97E48458494Ccd857e1A85DC91F7F0046E; + } + } + )"; + CHECK_WARNING(text, "checksum"); +} + +BOOST_AUTO_TEST_CASE(invalid_address_no_checksum) +{ + char const* text = R"( + contract C { + function f() { + var x = 0xfa0bfc97e48458494ccd857e1a85dc91f7f0046e; + } + } + )"; + CHECK_WARNING(text, "checksum"); +} + +BOOST_AUTO_TEST_CASE(invalid_address_length) +{ + char const* text = R"( + contract C { + function f() { + var x = 0xA0bFc97E48458494Ccd857e1A85DC91F7F0046E; + } + } + )"; + CHECK_WARNING(text, "checksum"); +} + BOOST_AUTO_TEST_SUITE_END() } From 605455f96b64f0c4b229cc31186f2dcf22433e6d Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 24 Jan 2017 23:36:35 +0100 Subject: [PATCH 077/118] Tests for library checksums. --- scripts/tests.sh | 16 ++------------ test/cmdlineTests.sh | 51 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 14 deletions(-) create mode 100755 test/cmdlineTests.sh diff --git a/scripts/tests.sh b/scripts/tests.sh index dfbda7342..cf18cb262 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -30,20 +30,8 @@ set -e REPO_ROOT="$(dirname "$0")"/.. - # Compile all files in std and examples. - -for f in "$REPO_ROOT"/std/*.sol -do - echo "Compiling $f..." - set +e - output=$("$REPO_ROOT"/build/solc/solc "$f" 2>&1) - failed=$? - # Remove the pre-release warning from the compiler output - output=$(echo "$output" | grep -v 'pre-release') - echo "$output" - set -e - test -z "$output" -a "$failed" -eq 0 -done +echo "Running commandline tests..." +"$REPO_ROOT/test/cmdlineTests.sh" # This conditional is only needed because we don't have a working Homebrew # install for `eth` at the time of writing, so we unzip the ZIP file locally diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh new file mode 100755 index 000000000..fc48654a0 --- /dev/null +++ b/test/cmdlineTests.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash + +#------------------------------------------------------------------------------ +# Bash script to run commandline Solidity tests. +# +# The documentation for solidity is hosted at: +# +# https://solidity.readthedocs.org +# +# ------------------------------------------------------------------------------ +# This file is part of solidity. +# +# solidity is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# solidity is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with solidity. If not, see +# +# (c) 2016 solidity contributors. +#------------------------------------------------------------------------------ + +set -e + +REPO_ROOT="$(dirname "$0")"/.. +SOLC="$REPO_ROOT/build/solc/solc" + + # Compile all files in std and examples. + +for f in "$REPO_ROOT"/std/*.sol +do + echo "Compiling $f..." + set +e + output=$("$SOLC" "$f" 2>&1) + failed=$? + # Remove the pre-release warning from the compiler output + output=$(echo "$output" | grep -v 'pre-release') + echo "$output" + set -e + test -z "$output" -a "$failed" -eq 0 +done + +# Test library checksum +echo 'contact C {}' | "$SOLC" --link --libraries a:0x90f20564390eAe531E810af625A22f51385Cd222 +! echo 'contract C {}' | "$SOLC" --link --libraries a:0x80f20564390eAe531E810af625A22f51385Cd222 2>/dev/null From 3949624a61b1dd0c32e67d30fe7d46b2511c583f Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 24 Jan 2017 23:36:07 +0100 Subject: [PATCH 078/118] Also check library addresses. --- libdevcore/CommonData.cpp | 40 +++++++++++++++++++++++++++++++++-- libdevcore/CommonData.h | 5 +++++ libsolidity/ast/AST.cpp | 22 +------------------ solc/CommandLineInterface.cpp | 5 +++++ 4 files changed, 49 insertions(+), 23 deletions(-) diff --git a/libdevcore/CommonData.cpp b/libdevcore/CommonData.cpp index 062d1b29c..a53d9628e 100644 --- a/libdevcore/CommonData.cpp +++ b/libdevcore/CommonData.cpp @@ -19,8 +19,12 @@ * @date 2014 */ -#include "CommonData.h" -#include "Exceptions.h" +#include +#include +#include + +#include + using namespace std; using namespace dev; @@ -95,3 +99,35 @@ bytes dev::fromHex(std::string const& _s, WhenError _throw) } return ret; } + + +bool dev::passesAddressChecksum(string const& _str, bool _strict) +{ + string s = _str.substr(0, 2) == "0x" ? _str.substr(2) : _str; + + if (s.length() != 40) + return false; + + if (!_strict && ( + _str.find_first_of("abcdef") == string::npos || + _str.find_first_of("ABCDEF") == string::npos + )) + return true; + + h256 hash = keccak256(boost::algorithm::to_lower_copy(s, std::locale::classic())); + for (size_t i = 0; i < 40; ++i) + { + char addressCharacter = s[i]; + bool lowerCase; + if ('a' <= addressCharacter && addressCharacter <= 'f') + lowerCase = true; + else if ('A' <= addressCharacter && addressCharacter <= 'F') + lowerCase = false; + else + continue; + unsigned nibble = (unsigned(hash[i / 2]) >> (4 * (1 - (i % 2)))) & 0xf; + if ((nibble >= 8) == lowerCase) + return false; + } + return true; +} diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index 5ffcdcca8..e0a6d2210 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -179,4 +179,9 @@ bool contains(T const& _t, V const& _v) return std::end(_t) != std::find(std::begin(_t), std::end(_t), _v); } +/// @returns true iff @a _str passess the hex address checksum test. +/// @param _strict if false, hex strings with only uppercase or only lowercase letters +/// are considered valid. +bool passesAddressChecksum(std::string const& _str, bool _strict); + } diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index e2b50dd71..616de54e1 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -539,25 +539,5 @@ bool Literal::passesAddressChecksum() const { string lit = value(); solAssert(lit.substr(0, 2) == "0x", "Expected hex prefix"); - lit = lit.substr(2); - - if (lit.length() != 40) - return false; - - h256 hash = keccak256(boost::algorithm::to_lower_copy(lit, std::locale::classic())); - for (size_t i = 0; i < 40; ++i) - { - char addressCharacter = lit[i]; - bool lowerCase; - if ('a' <= addressCharacter && addressCharacter <= 'f') - lowerCase = true; - else if ('A' <= addressCharacter && addressCharacter <= 'F') - lowerCase = false; - else - continue; - unsigned nibble = (unsigned(hash[i / 2]) >> (4 * (1 - (i % 2)))) & 0xf; - if ((nibble >= 8) == lowerCase) - return false; - } - return true; + return dev::passesAddressChecksum(lit, true); } diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index e49e8517d..b3ffa6bb0 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -432,6 +432,11 @@ bool CommandLineInterface::parseLibraryOption(string const& _input) string addrString(lib.begin() + colon + 1, lib.end()); boost::trim(libName); boost::trim(addrString); + if (!passesAddressChecksum(addrString, false)) + { + cerr << "Invalid checksum on library address \"" << libName << "\": " << addrString << endl; + return false; + } bytes binAddr = fromHex(addrString); h160 address(binAddr, h160::AlignRight); if (binAddr.size() > 20 || address == h160()) From d855eaab13392ef2ee3b75709c84b314d29afab9 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 24 Jan 2017 23:10:01 +0100 Subject: [PATCH 079/118] Documentation. --- docs/types.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/types.rst b/docs/types.rst index 6b67e6840..cb14c8acc 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -171,6 +171,19 @@ Fixed Point Numbers **COMING SOON...** +.. index:: address, literal;address + +.. _address_literals: + +Address Literals +---------------- + +Hexadecimal literals that pass the address checksum test, for example +``0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF`` are of ``address`` type. +Hexadecimal literals that are 20 bytes long (differ from that in at +most one hexadecimal digit) but do not pass the checksum test produce +a warning and are treated as regular rational number literals. + .. index:: literal, literal;rational .. _rational_literals: From 87f744ae5e91353f3608f0f5b8c271918b198989 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 25 Jan 2017 10:45:21 +0100 Subject: [PATCH 080/118] Add some more tests for the checksum routine. --- test/libdevcore/Checksum.cpp | 83 ++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 test/libdevcore/Checksum.cpp diff --git a/test/libdevcore/Checksum.cpp b/test/libdevcore/Checksum.cpp new file mode 100644 index 000000000..322608884 --- /dev/null +++ b/test/libdevcore/Checksum.cpp @@ -0,0 +1,83 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * Unit tests for the address checksum. + */ + +#include + +#include "../TestHelper.h" + +using namespace std; + +namespace dev +{ +namespace test +{ + +BOOST_AUTO_TEST_SUITE(Checksum) + +BOOST_AUTO_TEST_CASE(regular) +{ + BOOST_CHECK(passesAddressChecksum("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", true)); + BOOST_CHECK(passesAddressChecksum("0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359", true)); + BOOST_CHECK(passesAddressChecksum("0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB", true)); + BOOST_CHECK(passesAddressChecksum("0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", true)); +} + +BOOST_AUTO_TEST_CASE(regular_negative) +{ + BOOST_CHECK(!passesAddressChecksum("0x6aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", true)); + BOOST_CHECK(!passesAddressChecksum("0xeB6916095ca1df60bB79Ce92cE3Ea74c37c5d359", true)); + BOOST_CHECK(!passesAddressChecksum("0xebF03B407c01E7cD3CBea99509d93f8DDDC8C6FB", true)); + BOOST_CHECK(!passesAddressChecksum("0xE1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", true)); +} + +BOOST_AUTO_TEST_CASE(regular_invalid_length) +{ + BOOST_CHECK(passesAddressChecksum("0x9426cbfc57389778d313268E7F85F1CDc2fdad60", true)); + BOOST_CHECK(!passesAddressChecksum("0x9426cbfc57389778d313268E7F85F1CDc2fdad6", true)); + BOOST_CHECK(passesAddressChecksum("0x08A61851FFa4637dE289D630Ae8c5dFb0ff9171F", true)); + BOOST_CHECK(!passesAddressChecksum("0x8A61851FFa4637dE289D630Ae8c5dFb0ff9171F", true)); + BOOST_CHECK(passesAddressChecksum("0x00c40cC30cb4675673c9ee382de805c19734986A", true)); + BOOST_CHECK(!passesAddressChecksum("0xc40cC30cb4675673c9ee382de805c19734986A", true)); + BOOST_CHECK(passesAddressChecksum("0xC40CC30cb4675673C9ee382dE805c19734986a00", true)); + BOOST_CHECK(!passesAddressChecksum("0xC40CC30cb4675673C9ee382dE805c19734986a", true)); +} + +BOOST_AUTO_TEST_CASE(homocaps_valid) +{ + BOOST_CHECK(passesAddressChecksum("0x52908400098527886E0F7030069857D2E4169EE7", true)); + BOOST_CHECK(passesAddressChecksum("0x8617E340B3D01FA5F11F306F4090FD50E238070D", true)); + BOOST_CHECK(passesAddressChecksum("0xde709f2102306220921060314715629080e2fb77", true)); + BOOST_CHECK(passesAddressChecksum("0x27b1fdb04752bbc536007a920d24acb045561c26", true)); +} + +BOOST_AUTO_TEST_CASE(homocaps_invalid) +{ + string upper = "0x00AA0000000012400000000DDEEFF000000000BB"; + BOOST_CHECK(passesAddressChecksum(upper, false)); + BOOST_CHECK(!passesAddressChecksum(upper, true)); + string lower = "0x11aa000000000000000d00cc00000000000000bb"; + BOOST_CHECK(passesAddressChecksum(lower, false)); + BOOST_CHECK(!passesAddressChecksum(lower, true)); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} From 5738e865d5375751332d8f884f2b00b006d76945 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 30 Nov 2016 15:04:07 +0100 Subject: [PATCH 081/118] Accept any kind of whitespace after natspec tags --- libsolidity/parsing/DocStringParser.cpp | 62 ++++++++++++++++++------ test/libsolidity/SolidityNatspecJSON.cpp | 23 +++++++++ 2 files changed, 71 insertions(+), 14 deletions(-) diff --git a/libsolidity/parsing/DocStringParser.cpp b/libsolidity/parsing/DocStringParser.cpp index bbee35f56..4c59c47ce 100644 --- a/libsolidity/parsing/DocStringParser.cpp +++ b/libsolidity/parsing/DocStringParser.cpp @@ -16,16 +16,39 @@ static inline string::const_iterator skipLineOrEOS( return (_nlPos == _end) ? _end : ++_nlPos; } -static inline string::const_iterator firstSpaceOrNl( +static inline string::const_iterator firstSpaceOrTab( string::const_iterator _pos, string::const_iterator _end ) { auto spacePos = find(_pos, _end, ' '); - auto nlPos = find(_pos, _end, '\n'); - return (spacePos < nlPos) ? spacePos : nlPos; + auto tabPos = find(_pos, _end, '\t'); + return (spacePos < tabPos) ? spacePos : tabPos; } +static inline string::const_iterator firstWsOrNl( + string::const_iterator _pos, + string::const_iterator _end +) +{ + auto wsPos = firstSpaceOrTab(_pos, _end); + auto nlPos = find(wsPos, _end, '\n'); + return (wsPos < nlPos) ? wsPos : nlPos; +} + + +static inline string::const_iterator skipWhitespace( + string::const_iterator _pos, + string::const_iterator _end +) +{ + auto currPos = _pos; + while ((*currPos == ' ' || *currPos == '\t') && currPos != _end) + currPos += 1; + return currPos; +} + + bool DocStringParser::parse(string const& _docString, ErrorList& _errors) { m_errors = &_errors; @@ -43,7 +66,7 @@ bool DocStringParser::parse(string const& _docString, ErrorList& _errors) if (tagPos != end && tagPos < nlPos) { // we found a tag - auto tagNameEndPos = firstSpaceOrNl(tagPos, end); + auto tagNameEndPos = firstWsOrNl(tagPos, end); if (tagNameEndPos == end) { appendError("End of tag " + string(tagPos, tagNameEndPos) + "not found"); @@ -75,7 +98,7 @@ DocStringParser::iter DocStringParser::parseDocTagLine(iter _pos, iter _end, boo { solAssert(!!m_lastTag, ""); auto nlPos = find(_pos, _end, '\n'); - if (_appending && _pos < _end && *_pos != ' ') + if (_appending && _pos < _end && *_pos != ' ' && *_pos != '\t') m_lastTag->content += " "; copy(_pos, nlPos, back_inserter(m_lastTag->content)); return skipLineOrEOS(nlPos, _end); @@ -83,19 +106,30 @@ DocStringParser::iter DocStringParser::parseDocTagLine(iter _pos, iter _end, boo DocStringParser::iter DocStringParser::parseDocTagParam(iter _pos, iter _end) { - // find param name - auto currPos = find(_pos, _end, ' '); - if (currPos == _end) + // find param name start + auto nameStartPos = skipWhitespace(_pos, _end); + if (nameStartPos == _end) { - appendError("End of param name not found" + string(_pos, _end)); + appendError("No param name given" + string(nameStartPos, _end)); + return _end; + } + auto nameEndPos = firstSpaceOrTab(nameStartPos, _end); + if (nameEndPos == _end) + { + appendError("End of param name not found" + string(nameStartPos, _end)); + return _end; + } + auto paramName = string(nameStartPos, nameEndPos); + + auto descStartPos = skipWhitespace(nameEndPos, _end); + if (descStartPos == _end) + { + appendError("No description given for param" + paramName); return _end; } - auto paramName = string(_pos, currPos); - - currPos += 1; - auto nlPos = find(currPos, _end, '\n'); - auto paramDesc = string(currPos, nlPos); + auto nlPos = find(descStartPos, _end, '\n'); + auto paramDesc = string(descStartPos, nlPos); newTag("param"); m_lastTag->paramName = paramName; m_lastTag->content = paramDesc; diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index e32264c4d..c59b30c34 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -251,6 +251,29 @@ BOOST_AUTO_TEST_CASE(dev_multiple_params) checkNatspec(sourceCode, natspec, false); } +BOOST_AUTO_TEST_CASE(dev_multiple_params_mixed_whitespace) +{ + char const* sourceCode = "contract test {\n" + " /// @dev Multiplies a number by 7 and adds second parameter\n" + " /// @param a Documentation for the first parameter\n" + " /// @param second Documentation for the second parameter\n" + " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" + "}\n"; + + char const* natspec = "{" + "\"methods\":{" + " \"mul(uint256,uint256)\":{ \n" + " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n" + " \"params\": {\n" + " \"a\": \"Documentation for the first parameter\",\n" + " \"second\": \"Documentation for the second parameter\"\n" + " }\n" + " }\n" + "}}"; + + checkNatspec(sourceCode, natspec, false); +} + BOOST_AUTO_TEST_CASE(dev_mutiline_param_description) { char const* sourceCode = R"( From 9ca0fde853735d183c3db5c77c83de7c70c4ec98 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 1 Dec 2016 10:14:19 +0100 Subject: [PATCH 082/118] Fix and better output for tests --- libsolidity/parsing/DocStringParser.cpp | 6 ++++++ test/libsolidity/SolidityNatspecJSON.cpp | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/libsolidity/parsing/DocStringParser.cpp b/libsolidity/parsing/DocStringParser.cpp index 4c59c47ce..5d9a75eff 100644 --- a/libsolidity/parsing/DocStringParser.cpp +++ b/libsolidity/parsing/DocStringParser.cpp @@ -99,7 +99,13 @@ DocStringParser::iter DocStringParser::parseDocTagLine(iter _pos, iter _end, boo solAssert(!!m_lastTag, ""); auto nlPos = find(_pos, _end, '\n'); if (_appending && _pos < _end && *_pos != ' ' && *_pos != '\t') + { m_lastTag->content += " "; + } + else if (!_appending) + { + _pos = skipWhitespace(_pos, _end); + } copy(_pos, nlPos, back_inserter(m_lastTag->content)); return skipLineOrEOS(nlPos, _end); } diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index c59b30c34..0657c321f 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -56,7 +56,7 @@ public: m_reader.parse(_expectedDocumentationString, expectedDocumentation); BOOST_CHECK_MESSAGE( expectedDocumentation == generatedDocumentation, - "Expected " << expectedDocumentation.toStyledString() << + "Expected:\n" << expectedDocumentation.toStyledString() << "\n but got:\n" << generatedDocumentation.toStyledString() ); } From 900c56d996472cb2053b69c7104ef007c13b1e80 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 25 Jan 2017 10:33:09 +0000 Subject: [PATCH 083/118] Do not allow shadowing inline assembly instructions with variables --- Changelog.md | 1 + libsolidity/inlineasm/AsmParser.cpp | 11 ++++++++++- libsolidity/inlineasm/AsmParser.h | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 7dd607d8d..60def101e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,7 @@ Features: Bugfixes: * Code generator: Allow recursive structs. + * Inline assembly: reject shadowing instructions by variables. * Type checker: Allow multiple events of the same name (but with different arities or argument types) ### 0.4.8 (2017-01-13) diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index ef3da2554..c0efb651e 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -130,7 +130,7 @@ assembly::Statement Parser::parseExpression() return operation; } -assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) +std::map Parser::getInstructions() { // Allowed instructions, lowercase names. static map s_instructions; @@ -151,6 +151,12 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) // add alias for selfdestruct s_instructions["selfdestruct"] = solidity::Instruction::SUICIDE; } + return s_instructions; +} + +assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) +{ + map s_instructions = getInstructions(); Statement ret; switch (m_scanner->currentToken()) @@ -204,9 +210,12 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) assembly::VariableDeclaration Parser::parseVariableDeclaration() { + map s_instructions = getInstructions(); VariableDeclaration varDecl = createWithLocation(); expectToken(Token::Let); varDecl.name = m_scanner->currentLiteral(); + if (s_instructions.count(varDecl.name)) + fatalParserError("Cannot shadow instructions with variable declaration."); expectToken(Token::Identifier); expectToken(Token::Colon); expectToken(Token::Assign); diff --git a/libsolidity/inlineasm/AsmParser.h b/libsolidity/inlineasm/AsmParser.h index 8b56ab902..764c53f66 100644 --- a/libsolidity/inlineasm/AsmParser.h +++ b/libsolidity/inlineasm/AsmParser.h @@ -64,6 +64,7 @@ protected: Statement parseStatement(); /// Parses a functional expression that has to push exactly one stack element Statement parseExpression(); + std::map getInstructions(); Statement parseElementaryOperation(bool _onlySinglePusher = false); VariableDeclaration parseVariableDeclaration(); FunctionalInstruction parseFunctionalInstruction(Statement&& _instruction); From 7ff44bec94dd72acb73c2caa26786113c24c8360 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 25 Jan 2017 10:37:33 +0000 Subject: [PATCH 084/118] Add test for shadowing inline assembly instruction --- test/libsolidity/SolidityParser.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index a3bfab757..227663588 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -1479,6 +1479,19 @@ BOOST_AUTO_TEST_CASE(function_type_state_variable) BOOST_CHECK(successParse(text)); } +BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction) +{ + char const* text = R"( + contract A { + function f() { + assembly { + let gas := 1 + } + } + } + )"; + CHECK_PARSE_ERROR(text, "Cannot shadow instructions with variable declaration."); +} BOOST_AUTO_TEST_SUITE_END() From 8e318181e9719fda44899a402038013b757a4a60 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 25 Jan 2017 17:24:25 +0100 Subject: [PATCH 085/118] Rewording in changelog. --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 60def101e..e50cb3bdc 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,7 +10,7 @@ Features: Bugfixes: * Code generator: Allow recursive structs. - * Inline assembly: reject shadowing instructions by variables. + * Inline assembly: Disallow variables named like opcodes. * Type checker: Allow multiple events of the same name (but with different arities or argument types) ### 0.4.8 (2017-01-13) From 27ba665694c4a961e098559cb36176aeafd5ec44 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 25 Jan 2017 17:24:43 +0100 Subject: [PATCH 086/118] Moved test. --- libsolidity/inlineasm/AsmParser.cpp | 2 +- test/libsolidity/InlineAssembly.cpp | 6 ++++++ test/libsolidity/SolidityParser.cpp | 14 -------------- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index c0efb651e..cd3ea0da2 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -215,7 +215,7 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration() expectToken(Token::Let); varDecl.name = m_scanner->currentLiteral(); if (s_instructions.count(varDecl.name)) - fatalParserError("Cannot shadow instructions with variable declaration."); + fatalParserError("Cannot use instruction names for identifier names."); expectToken(Token::Identifier); expectToken(Token::Colon); expectToken(Token::Assign); diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 64073edce..c2dac287b 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -182,6 +182,12 @@ BOOST_AUTO_TEST_CASE(error_tag) BOOST_CHECK(successAssemble("{ invalidJumpLabel }")); } +BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction) +{ + // Error message: "Cannot use instruction names for identifier names." + BOOST_CHECK(!successAssemble("{ let gas := 1 }")); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 227663588..e5362e786 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -1479,20 +1479,6 @@ BOOST_AUTO_TEST_CASE(function_type_state_variable) BOOST_CHECK(successParse(text)); } -BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction) -{ - char const* text = R"( - contract A { - function f() { - assembly { - let gas := 1 - } - } - } - )"; - CHECK_PARSE_ERROR(text, "Cannot shadow instructions with variable declaration."); -} - BOOST_AUTO_TEST_SUITE_END() } From a5696e1f0a87a2912cc64d2538751836658e7c63 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 25 Jan 2017 17:24:50 +0100 Subject: [PATCH 087/118] Renamed function. --- libsolidity/inlineasm/AsmParser.cpp | 8 ++++---- libsolidity/inlineasm/AsmParser.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index cd3ea0da2..048d916dd 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -130,7 +130,7 @@ assembly::Statement Parser::parseExpression() return operation; } -std::map Parser::getInstructions() +std::map const& Parser::instructions() { // Allowed instructions, lowercase names. static map s_instructions; @@ -156,7 +156,7 @@ std::map Parser::getInstructions() assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) { - map s_instructions = getInstructions(); + map const& s_instructions = instructions(); Statement ret; switch (m_scanner->currentToken()) @@ -178,7 +178,7 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) // first search the set of instructions. if (s_instructions.count(literal)) { - dev::solidity::Instruction const& instr = s_instructions[literal]; + dev::solidity::Instruction const& instr = s_instructions.at(literal); if (_onlySinglePusher) { InstructionInfo info = dev::solidity::instructionInfo(instr); @@ -210,7 +210,7 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) assembly::VariableDeclaration Parser::parseVariableDeclaration() { - map s_instructions = getInstructions(); + map const& s_instructions = instructions(); VariableDeclaration varDecl = createWithLocation(); expectToken(Token::Let); varDecl.name = m_scanner->currentLiteral(); diff --git a/libsolidity/inlineasm/AsmParser.h b/libsolidity/inlineasm/AsmParser.h index 764c53f66..643548dd0 100644 --- a/libsolidity/inlineasm/AsmParser.h +++ b/libsolidity/inlineasm/AsmParser.h @@ -64,7 +64,7 @@ protected: Statement parseStatement(); /// Parses a functional expression that has to push exactly one stack element Statement parseExpression(); - std::map getInstructions(); + std::map const& instructions(); Statement parseElementaryOperation(bool _onlySinglePusher = false); VariableDeclaration parseVariableDeclaration(); FunctionalInstruction parseFunctionalInstruction(Statement&& _instruction); From 946a63c26f0c03c31c6342acc65d099ffdc3691f Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 25 Jan 2017 17:27:01 +0100 Subject: [PATCH 088/118] Add test for assignment. --- test/libsolidity/InlineAssembly.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index c2dac287b..9ee848678 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -182,12 +182,18 @@ BOOST_AUTO_TEST_CASE(error_tag) BOOST_CHECK(successAssemble("{ invalidJumpLabel }")); } -BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction) +BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_declaration) { // Error message: "Cannot use instruction names for identifier names." BOOST_CHECK(!successAssemble("{ let gas := 1 }")); } +BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_assignment) +{ + // Error message: "Cannot use instruction names for identifier names." + BOOST_CHECK(!successAssemble("{ 2 =: gas }")); +} + BOOST_AUTO_TEST_SUITE_END() } From f62e269115822f791a0ad2e3699815f0725a0ef7 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 25 Jan 2017 17:29:06 +0100 Subject: [PATCH 089/118] Disallow instructions in assignment. --- libsolidity/inlineasm/AsmParser.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 048d916dd..bed90139e 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -101,6 +101,8 @@ assembly::Statement Parser::parseStatement() { // functional assignment FunctionalAssignment funAss = createWithLocation(identifier.location); + if (instructions().count(identifier.name)) + fatalParserError("Cannot use instruction names for identifier names."); m_scanner->next(); funAss.variableName = identifier; funAss.value.reset(new Statement(parseExpression())); @@ -156,8 +158,6 @@ std::map const& Parser::instructions() assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) { - map const& s_instructions = instructions(); - Statement ret; switch (m_scanner->currentToken()) { @@ -176,9 +176,9 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) else literal = m_scanner->currentLiteral(); // first search the set of instructions. - if (s_instructions.count(literal)) + if (instructions().count(literal)) { - dev::solidity::Instruction const& instr = s_instructions.at(literal); + dev::solidity::Instruction const& instr = instructions().at(literal); if (_onlySinglePusher) { InstructionInfo info = dev::solidity::instructionInfo(instr); @@ -210,11 +210,10 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) assembly::VariableDeclaration Parser::parseVariableDeclaration() { - map const& s_instructions = instructions(); VariableDeclaration varDecl = createWithLocation(); expectToken(Token::Let); varDecl.name = m_scanner->currentLiteral(); - if (s_instructions.count(varDecl.name)) + if (instructions().count(varDecl.name)) fatalParserError("Cannot use instruction names for identifier names."); expectToken(Token::Identifier); expectToken(Token::Colon); From 19833c95ebae4f8b394d0c3a0cacadf766dc81fe Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 26 Jan 2017 13:17:05 +0100 Subject: [PATCH 090/118] Add ethereum-git to archlinux dependencies It's needed for testing, since we need to have the `eth` client installed --- scripts/install_deps.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh index 255176ab5..f21c48d03 100755 --- a/scripts/install_deps.sh +++ b/scripts/install_deps.sh @@ -138,11 +138,13 @@ case $(uname -s) in # All our dependencies can be found in the Arch Linux official repositories. # See https://wiki.archlinux.org/index.php/Official_repositories + # Also adding ethereum-git to allow for testing with the `eth` client sudo pacman -Sy \ base-devel \ boost \ cmake \ git \ + ethereum-git \ ;; #------------------------------------------------------------------------------ From 525758a130941c6f1dfde8a0f884a550e7a7bb50 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 26 Jan 2017 13:40:40 +0100 Subject: [PATCH 091/118] Disallow assignment to non-identifiers. --- libsolidity/inlineasm/AsmParser.cpp | 2 ++ test/libsolidity/InlineAssembly.cpp | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index bed90139e..7ddc6d047 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -71,6 +71,8 @@ assembly::Statement Parser::parseStatement() expectToken(Token::Colon); assignment.variableName.location = location(); assignment.variableName.name = m_scanner->currentLiteral(); + if (instructions().count(assignment.variableName.name)) + fatalParserError("Identifier expected."); assignment.location.end = endPosition(); expectToken(Token::Identifier); return assignment; diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 9ee848678..33721bbf6 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -194,6 +194,12 @@ BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_assignment) BOOST_CHECK(!successAssemble("{ 2 =: gas }")); } +BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_functional_assignment) +{ + // Error message: "Cannot use instruction names for identifier names." + BOOST_CHECK(!successAssemble("{ gas := 2 }")); +} + BOOST_AUTO_TEST_SUITE_END() } From 1b097fd3c795dc5f0e100858a45b2efd8efc59b8 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 26 Jan 2017 13:45:23 +0100 Subject: [PATCH 092/118] Proper error reporting for assembly mode. --- solc/CommandLineInterface.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index e49e8517d..0a6c17fe0 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -921,6 +921,7 @@ bool CommandLineInterface::assemble() m_assemblyStacks[src.first].assemble(); } for (auto const& stack: m_assemblyStacks) + { for (auto const& error: stack.second.errors()) SourceReferenceFormatter::printExceptionInformation( cerr, @@ -928,6 +929,9 @@ bool CommandLineInterface::assemble() (error->type() == Error::Type::Warning) ? "Warning" : "Error", [&](string const& _source) -> Scanner const& { return *scanners.at(_source); } ); + if (!Error::containsOnlyWarnings(stack.second.errors())) + successful = false; + } return successful; } From 8e29d636f710819d082e7b01de42da960d9f5b51 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 26 Jan 2017 13:47:57 +0100 Subject: [PATCH 093/118] Header cleanup. --- solc/CommandLineInterface.cpp | 47 ++++++++++++++++++----------------- solc/CommandLineInterface.h | 8 +++--- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 0a6c17fe0..771621d0a 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -22,28 +22,8 @@ */ #include "CommandLineInterface.h" -#ifdef _WIN32 // windows - #include - #define isatty _isatty - #define fileno _fileno -#else // unix - #include -#endif -#include -#include -#include - -#include -#include -#include - #include "solidity/BuildInfo.h" -#include -#include -#include -#include -#include -#include + #include #include #include @@ -54,9 +34,31 @@ #include #include #include -#include #include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#ifdef _WIN32 // windows + #include + #define isatty _isatty + #define fileno _fileno +#else // unix + #include +#endif +#include +#include +#include + using namespace std; namespace po = boost::program_options; @@ -907,7 +909,6 @@ void CommandLineInterface::writeLinkedFiles() bool CommandLineInterface::assemble() { - //@TODO later, we will use the convenience interface and should also remove the include above bool successful = true; map> scanners; for (auto const& src: m_sourceCodes) diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index b8fc1823b..bcfc43d73 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -21,12 +21,14 @@ */ #pragma once -#include -#include -#include #include #include +#include +#include + +#include + namespace dev { namespace solidity From 873f2dddd635cb246af4368b9950123fb36d9b28 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 26 Jan 2017 13:52:02 +0100 Subject: [PATCH 094/118] Update error message. --- libsolidity/inlineasm/AsmParser.cpp | 2 +- test/libsolidity/InlineAssembly.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 7ddc6d047..fcc92dbbf 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -72,7 +72,7 @@ assembly::Statement Parser::parseStatement() assignment.variableName.location = location(); assignment.variableName.name = m_scanner->currentLiteral(); if (instructions().count(assignment.variableName.name)) - fatalParserError("Identifier expected."); + fatalParserError("Identifier expected, got instruction name."); assignment.location.end = endPosition(); expectToken(Token::Identifier); return assignment; diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 33721bbf6..c051a9822 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -190,7 +190,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_declaration) BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_assignment) { - // Error message: "Cannot use instruction names for identifier names." + // Error message: "Identifier expected, got instruction name." BOOST_CHECK(!successAssemble("{ 2 =: gas }")); } From f610ba77a493f8a6f270c72222d34eedfb183722 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 26 Jan 2017 13:56:50 +0100 Subject: [PATCH 095/118] Simplify length rule. --- docs/types.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/types.rst b/docs/types.rst index cb14c8acc..3fb1db953 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -180,8 +180,8 @@ Address Literals Hexadecimal literals that pass the address checksum test, for example ``0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF`` are of ``address`` type. -Hexadecimal literals that are 20 bytes long (differ from that in at -most one hexadecimal digit) but do not pass the checksum test produce +Hexadecimal literals that are between 39 and 41 digits +long and do not pass the checksum test produce a warning and are treated as regular rational number literals. .. index:: literal, literal;rational From fcf483ee6b284f7e6d6f3c7f593cd95fb298da0d Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 17 Jan 2017 12:02:01 +0000 Subject: [PATCH 096/118] Add option to store literal sources in metadata --- libsolidity/interface/CompilerStack.cpp | 13 +++++++++---- libsolidity/interface/CompilerStack.h | 2 ++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index b26bd5011..3335c40e4 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -726,10 +726,15 @@ string CompilerStack::createOnChainMetadata(Contract const& _contract) const solAssert(s.second.scanner, "Scanner not available"); meta["sources"][s.first]["keccak256"] = "0x" + toHex(dev::keccak256(s.second.scanner->source()).asBytes()); - meta["sources"][s.first]["urls"] = Json::arrayValue; - meta["sources"][s.first]["urls"].append( - "bzzr://" + toHex(dev::swarmHash(s.second.scanner->source()).asBytes()) - ); + if (m_metadataLiteralSources) + meta["sources"][s.first]["content"] = s.second.scanner->source(); + else + { + meta["sources"][s.first]["urls"] = Json::arrayValue; + meta["sources"][s.first]["urls"].append( + "bzzr://" + toHex(dev::swarmHash(s.second.scanner->source()).asBytes()) + ); + } } meta["settings"]["optimizer"]["enabled"] = m_optimize; meta["settings"]["optimizer"]["runs"] = m_optimizeRuns; diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 61edc284a..9ee70215e 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -177,6 +177,7 @@ public: /// Can be one of 4 types defined at @c DocumentationType Json::Value const& metadata(std::string const& _contractName, DocumentationType _type) const; std::string const& onChainMetadata(std::string const& _contractName) const; + void useMetadataLiteralSources(bool _metadataLiteralSources) { m_metadataLiteralSources = _metadataLiteralSources; } /// @returns the previously used scanner, useful for counting lines during error reporting. Scanner const& scanner(std::string const& _sourceName = "") const; @@ -274,6 +275,7 @@ private: std::map m_contracts; std::string m_formalTranslation; ErrorList m_errors; + bool m_metadataLiteralSources = false; }; } From 84bf547f21669fd89e2455463cbb43d965450d1e Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 25 Jan 2017 12:45:18 +0000 Subject: [PATCH 097/118] Add option to solc to use literal sources in metadta --- solc/CommandLineInterface.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index e49e8517d..d33b87a8f 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -98,6 +98,7 @@ static string const g_strSrcMap = "srcmap"; static string const g_strSrcMapRuntime = "srcmap-runtime"; static string const g_strVersion = "version"; static string const g_stdinFileNameStr = ""; +static string const g_strMetadataLiteral = "metadata-literal"; static string const g_argAbi = g_strAbi; static string const g_argAddStandard = g_strAddStandard; @@ -126,6 +127,7 @@ static string const g_argOutputDir = g_strOutputDir; static string const g_argSignatureHashes = g_strSignatureHashes; static string const g_argVersion = g_strVersion; static string const g_stdinFileName = g_stdinFileNameStr; +static string const g_argMetadataLiteral = g_strMetadataLiteral; /// Possible arguments to for --combined-json static set const g_combinedJsonArgs{ @@ -511,7 +513,8 @@ Allowed options)", g_argLink.c_str(), "Switch to linker mode, ignoring all options apart from --libraries " "and modify binaries in place." - ); + ) + (g_argMetadataLiteral.c_str(), "Store referenced sources are literal data in the metadata output."); po::options_description outputComponents("Output Components"); outputComponents.add_options() (g_argAst.c_str(), "AST of all source files.") @@ -634,6 +637,8 @@ bool CommandLineInterface::processInput() auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return m_compiler->scanner(_sourceName); }; try { + if (m_args.count(g_argMetadataLiteral) > 0) + m_compiler->useMetadataLiteralSources(true); if (m_args.count(g_argInputFile)) m_compiler->setRemappings(m_args[g_argInputFile].as>()); for (auto const& sourceCode: m_sourceCodes) From 4b321c653cf5f8cee2d5c1db6668d1dcffebc17b Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 26 Jan 2017 13:26:04 +0000 Subject: [PATCH 098/118] Add literal metadata sources to changelog --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index e50cb3bdc..a8b66abc3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Features: * AST: Use deterministic node identifiers. * Type system: Introduce type identifier strings. * Metadata: Do not include platform in the version number. + * Metadata: Add option to store sources as literal content. * Code generator: Extract array utils into low-level functions. Bugfixes: From 9bcbd93ac59a19320fd56e27c58a6283f2450666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20W=C3=BCstholz?= Date: Sun, 22 Jan 2017 20:49:12 +0100 Subject: [PATCH 099/118] Change translation of implicit throws (issue #1589). This adds a new invalid instruction that is used for encoding implicit throws that are emitted by the compiler. This makes it possible to distinguish such runtime errors from user-provided, explicit throws. --- libevmasm/Instruction.cpp | 2 ++ libevmasm/Instruction.h | 2 ++ libsolidity/codegen/ArrayUtils.cpp | 2 +- libsolidity/codegen/CompilerContext.cpp | 12 ++++++++++++ libsolidity/codegen/CompilerContext.h | 4 ++++ libsolidity/codegen/CompilerUtils.cpp | 6 +++--- libsolidity/codegen/ContractCompiler.cpp | 8 +++++--- libsolidity/codegen/ExpressionCompiler.cpp | 12 ++++++------ test/libsolidity/Assembly.cpp | 4 ++-- test/libsolidity/SolidityExpressionCompiler.cpp | 14 ++++++++++++-- 10 files changed, 49 insertions(+), 17 deletions(-) diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index 17445c599..ea5b5a108 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -154,6 +154,7 @@ const std::map dev::solidity::c_instructions = { "LOG2", Instruction::LOG2 }, { "LOG3", Instruction::LOG3 }, { "LOG4", Instruction::LOG4 }, + { "INVALID", Instruction::INVALID }, { "CREATE", Instruction::CREATE }, { "CALL", Instruction::CALL }, { "CALLCODE", Instruction::CALLCODE }, @@ -288,6 +289,7 @@ static const std::map c_instructionInfo = { Instruction::LOG2, { "LOG2", 0, 4, 0, true, Tier::Special } }, { Instruction::LOG3, { "LOG3", 0, 5, 0, true, Tier::Special } }, { Instruction::LOG4, { "LOG4", 0, 6, 0, true, Tier::Special } }, + { Instruction::INVALID, { "INVALID", 0, 0, 0, true, Tier::Zero } }, { Instruction::CREATE, { "CREATE", 0, 3, 1, true, Tier::Special } }, { Instruction::CALL, { "CALL", 0, 7, 1, true, Tier::Special } }, { Instruction::CALLCODE, { "CALLCODE", 0, 7, 1, true, Tier::Special } }, diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index 2dd451cdf..7432f04d9 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -171,6 +171,8 @@ enum class Instruction: uint8_t LOG3, ///< Makes a log entry; 3 topics. LOG4, ///< Makes a log entry; 4 topics. + INVALID = 0xef, ///< invalid instruction for expressing runtime errors (e.g., division-by-zero) + CREATE = 0xf0, ///< create a new account with associated code CALL, ///< message-call into an account CALLCODE, ///< message-call with another account's code only diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 4d100d1d2..bdd29abd8 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -901,7 +901,7 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType, bool _doBoundsCheck) c // check out-of-bounds access m_context << Instruction::DUP2 << Instruction::LT << Instruction::ISZERO; // out-of-bounds access throws exception - m_context.appendConditionalJumpTo(m_context.errorTag()); + m_context.appendConditionalInvalid(); } if (location == DataLocation::CallData && _arrayType.isDynamicallySized()) // remove length if present diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index e26f96e8f..454503503 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -215,6 +215,18 @@ CompilerContext& CompilerContext::appendJump(eth::AssemblyItem::JumpType _jumpTy return *this << item; } +CompilerContext& CompilerContext::appendInvalid() +{ + return *this << Instruction::INVALID; +} + +CompilerContext& CompilerContext::appendConditionalInvalid() +{ + eth::AssemblyItem falseTag = appendConditionalJump(); + eth::AssemblyItem endTag = appendJumpToNew(); + return *this << falseTag << Instruction::INVALID << endTag; +} + void CompilerContext::resetVisitedNodes(ASTNode const* _node) { stack newStack; diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index f024b0103..58d6cb2ad 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -127,6 +127,10 @@ public: eth::AssemblyItem appendJumpToNew() { return m_asm->appendJump().tag(); } /// Appends a JUMP to a tag already on the stack CompilerContext& appendJump(eth::AssemblyItem::JumpType _jumpType = eth::AssemblyItem::JumpType::Ordinary); + /// Appends an INVALID instruction + CompilerContext& appendInvalid(); + /// Appends a conditional INVALID instruction + CompilerContext& appendConditionalInvalid(); /// Returns an "ErrorTag" eth::AssemblyItem errorTag() { return m_asm->errorTag(); } /// Appends a JUMP to a specific tag diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index caf3b1acd..67877bbfc 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -468,7 +468,7 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp EnumType const& enumType = dynamic_cast(_typeOnStack); solAssert(enumType.numberOfMembers() > 0, "empty enum should have caused a parser error."); m_context << u256(enumType.numberOfMembers() - 1) << Instruction::DUP2 << Instruction::GT; - m_context.appendConditionalJumpTo(m_context.errorTag()); + m_context.appendConditionalInvalid(); enumOverflowCheckPending = false; } break; @@ -497,7 +497,7 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp EnumType const& enumType = dynamic_cast(_targetType); solAssert(enumType.numberOfMembers() > 0, "empty enum should have caused a parser error."); m_context << u256(enumType.numberOfMembers() - 1) << Instruction::DUP2 << Instruction::GT; - m_context.appendConditionalJumpTo(m_context.errorTag()); + m_context.appendConditionalInvalid(); enumOverflowCheckPending = false; } else if (targetTypeCategory == Type::Category::FixedPoint) @@ -807,7 +807,7 @@ void CompilerUtils::pushZeroValue(Type const& _type) { if (funType->location() == FunctionType::Location::Internal) { - m_context << m_context.errorTag(); + m_context.appendInvalid(); return; } } diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 9dc1fb37c..56d03a05f 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -106,7 +106,7 @@ void ContractCompiler::appendCallValueCheck() { // Throw if function is not payable but call contained ether. m_context << Instruction::CALLVALUE; - m_context.appendConditionalJumpTo(m_context.errorTag()); + m_context.appendConditionalInvalid(); } void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _contract) @@ -271,7 +271,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac appendReturnValuePacker(FunctionType(*fallback).returnParameterTypes(), _contract.isLibrary()); } else - m_context.appendJumpTo(m_context.errorTag()); + m_context.appendInvalid(); for (auto const& it: interfaceFunctions) { @@ -918,7 +918,9 @@ eth::AssemblyPointer ContractCompiler::cloneRuntime() a << Instruction::DELEGATECALL; //Propagate error condition (if DELEGATECALL pushes 0 on stack). a << Instruction::ISZERO; - a.appendJumpI(a.errorTag()); + eth::AssemblyItem falseTag = a.appendJumpI(); + eth::AssemblyItem endTag = a.appendJump().tag(); + a << falseTag << Instruction::INVALID << endTag; //@todo adjust for larger return values, make this dynamic. a << u256(0x20) << u256(0) << Instruction::RETURN; return make_shared(a); diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index bda4e04d8..b66a3e129 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -585,7 +585,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << Instruction::CREATE; // Check if zero (out of stack or not enough balance). m_context << Instruction::DUP1 << Instruction::ISZERO; - m_context.appendConditionalJumpTo(m_context.errorTag()); + m_context.appendConditionalInvalid(); if (function.valueSet()) m_context << swapInstruction(1) << Instruction::POP; break; @@ -1234,7 +1234,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) m_context << u256(fixedBytesType.numBytes()); m_context << Instruction::DUP2 << Instruction::LT << Instruction::ISZERO; // out-of-bounds access throws exception - m_context.appendConditionalJumpTo(m_context.errorTag()); + m_context.appendConditionalInvalid(); m_context << Instruction::BYTE; m_context << (u256(1) << (256 - 8)) << Instruction::MUL; @@ -1416,7 +1416,7 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Ty { // Test for division by zero m_context << Instruction::DUP2 << Instruction::ISZERO; - m_context.appendConditionalJumpTo(m_context.errorTag()); + m_context.appendConditionalInvalid(); if (_operator == Token::Div) m_context << (c_isSigned ? Instruction::SDIV : Instruction::DIV); @@ -1477,7 +1477,7 @@ void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator, Type co if (c_amountSigned) { m_context << u256(0) << Instruction::DUP3 << Instruction::SLT; - m_context.appendConditionalJumpTo(m_context.errorTag()); + m_context.appendConditionalInvalid(); } switch (_operator) @@ -1663,7 +1663,7 @@ void ExpressionCompiler::appendExternalFunctionCall( if (funKind == FunctionKind::External || funKind == FunctionKind::CallCode || funKind == FunctionKind::DelegateCall) { m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO; - m_context.appendConditionalJumpTo(m_context.errorTag()); + m_context.appendConditionalInvalid(); existenceChecked = true; } @@ -1699,7 +1699,7 @@ void ExpressionCompiler::appendExternalFunctionCall( { //Propagate error condition (if CALL pushes 0 on stack). m_context << Instruction::ISZERO; - m_context.appendConditionalJumpTo(m_context.errorTag()); + m_context.appendConditionalInvalid(); } utils().popStackSlots(remainsSize); diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index 155dd5c9b..aed3c854b 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -116,8 +116,8 @@ BOOST_AUTO_TEST_CASE(location_test) shared_ptr n = make_shared(""); AssemblyItems items = compileContract(sourceCode); vector locations = - vector(18, SourceLocation(2, 75, n)) + - vector(27, SourceLocation(20, 72, n)) + + vector(17, SourceLocation(2, 75, n)) + + vector(32, SourceLocation(20, 72, n)) + vector{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} + vector(2, SourceLocation(58, 67, n)) + vector(3, SourceLocation(20, 72, n)); diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 0c5a09c3e..ca6301696 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -337,13 +337,23 @@ BOOST_AUTO_TEST_CASE(arithmetics) byte(Instruction::ADD), byte(Instruction::DUP2), byte(Instruction::ISZERO), - byte(Instruction::PUSH1), 0x0, + byte(Instruction::PUSH1), 0x1e, byte(Instruction::JUMPI), + byte(Instruction::PUSH1), 0x20, + byte(Instruction::JUMP), + byte(Instruction::JUMPDEST), + byte(Instruction::INVALID), + byte(Instruction::JUMPDEST), byte(Instruction::MOD), byte(Instruction::DUP2), byte(Instruction::ISZERO), - byte(Instruction::PUSH1), 0x0, + byte(Instruction::PUSH1), 0x2a, byte(Instruction::JUMPI), + byte(Instruction::PUSH1), 0x2c, + byte(Instruction::JUMP), + byte(Instruction::JUMPDEST), + byte(Instruction::INVALID), + byte(Instruction::JUMPDEST), byte(Instruction::DIV), byte(Instruction::MUL)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); From 5b7cc018f0b256fb42f7bee38ad8d1ec4e2ec634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20W=C3=BCstholz?= Date: Mon, 23 Jan 2017 10:46:50 +0100 Subject: [PATCH 100/118] Address feedback from code review. --- docs/control-structures.rst | 2 +- libsolidity/codegen/CompilerContext.cpp | 6 +++--- libsolidity/codegen/ContractCompiler.cpp | 6 +++--- test/libsolidity/Assembly.cpp | 2 +- test/libsolidity/SolidityExpressionCompiler.cpp | 12 ++++-------- 5 files changed, 12 insertions(+), 16 deletions(-) diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 3f012b128..a398d8570 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -395,7 +395,7 @@ Currently, Solidity automatically generates a runtime exception in the following #. If your contract receives Ether via a public function without ``payable`` modifier (including the constructor and the fallback function). #. If your contract receives Ether via a public accessor function. -Internally, Solidity performs an "invalid jump" when an exception is thrown and thus causes the EVM to revert all changes made to the state. The reason for this is that there is no safe way to continue execution, because an expected effect did not occur. Because we want to retain the atomicity of transactions, the safest thing to do is to revert all changes and make the whole transaction (or at least call) without effect. +Internally, Solidity performs an "invalid jump" when a user-provided exception is thrown. In contrast, it performs an invalid (i.e., non-existent) operation if a runtime exception is encountered. In both cases, this causes the EVM to revert all changes made to the state. The reason for this is that there is no safe way to continue execution, because an expected effect did not occur. Because we want to retain the atomicity of transactions, the safest thing to do is to revert all changes and make the whole transaction (or at least call) without effect. .. index:: ! assembly, ! asm, ! evmasm diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 454503503..3bb6c9533 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -222,9 +222,9 @@ CompilerContext& CompilerContext::appendInvalid() CompilerContext& CompilerContext::appendConditionalInvalid() { - eth::AssemblyItem falseTag = appendConditionalJump(); - eth::AssemblyItem endTag = appendJumpToNew(); - return *this << falseTag << Instruction::INVALID << endTag; + *this << Instruction::ISZERO; + eth::AssemblyItem afterTag = appendConditionalJump(); + return *this << Instruction::INVALID << afterTag; } void CompilerContext::resetVisitedNodes(ASTNode const* _node) diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 56d03a05f..3ca2f3758 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -918,9 +918,9 @@ eth::AssemblyPointer ContractCompiler::cloneRuntime() a << Instruction::DELEGATECALL; //Propagate error condition (if DELEGATECALL pushes 0 on stack). a << Instruction::ISZERO; - eth::AssemblyItem falseTag = a.appendJumpI(); - eth::AssemblyItem endTag = a.appendJump().tag(); - a << falseTag << Instruction::INVALID << endTag; + a << Instruction::ISZERO; + eth::AssemblyItem afterTag = a.appendJumpI(); + a << Instruction::INVALID << afterTag; //@todo adjust for larger return values, make this dynamic. a << u256(0x20) << u256(0) << Instruction::RETURN; return make_shared(a); diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index aed3c854b..497bfc777 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -117,7 +117,7 @@ BOOST_AUTO_TEST_CASE(location_test) AssemblyItems items = compileContract(sourceCode); vector locations = vector(17, SourceLocation(2, 75, n)) + - vector(32, SourceLocation(20, 72, n)) + + vector(30, SourceLocation(20, 72, n)) + vector{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} + vector(2, SourceLocation(58, 67, n)) + vector(3, SourceLocation(20, 72, n)); diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index ca6301696..a769776e9 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -337,21 +337,17 @@ BOOST_AUTO_TEST_CASE(arithmetics) byte(Instruction::ADD), byte(Instruction::DUP2), byte(Instruction::ISZERO), - byte(Instruction::PUSH1), 0x1e, + byte(Instruction::ISZERO), + byte(Instruction::PUSH1), 0x1d, byte(Instruction::JUMPI), - byte(Instruction::PUSH1), 0x20, - byte(Instruction::JUMP), - byte(Instruction::JUMPDEST), byte(Instruction::INVALID), byte(Instruction::JUMPDEST), byte(Instruction::MOD), byte(Instruction::DUP2), byte(Instruction::ISZERO), - byte(Instruction::PUSH1), 0x2a, + byte(Instruction::ISZERO), + byte(Instruction::PUSH1), 0x26, byte(Instruction::JUMPI), - byte(Instruction::PUSH1), 0x2c, - byte(Instruction::JUMP), - byte(Instruction::JUMPDEST), byte(Instruction::INVALID), byte(Instruction::JUMPDEST), byte(Instruction::DIV), From c2b3d8bcd28a3047a832cf813df14a97a5b01daa Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 26 Jan 2017 15:57:49 +0100 Subject: [PATCH 101/118] Change code for INVALID opcode to 0xfe. --- libevmasm/Instruction.cpp | 4 ++-- libevmasm/Instruction.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index ea5b5a108..b0f063dae 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -154,12 +154,12 @@ const std::map dev::solidity::c_instructions = { "LOG2", Instruction::LOG2 }, { "LOG3", Instruction::LOG3 }, { "LOG4", Instruction::LOG4 }, - { "INVALID", Instruction::INVALID }, { "CREATE", Instruction::CREATE }, { "CALL", Instruction::CALL }, { "CALLCODE", Instruction::CALLCODE }, { "RETURN", Instruction::RETURN }, { "DELEGATECALL", Instruction::DELEGATECALL }, + { "INVALID", Instruction::INVALID }, { "SUICIDE", Instruction::SUICIDE } }; @@ -289,12 +289,12 @@ static const std::map c_instructionInfo = { Instruction::LOG2, { "LOG2", 0, 4, 0, true, Tier::Special } }, { Instruction::LOG3, { "LOG3", 0, 5, 0, true, Tier::Special } }, { Instruction::LOG4, { "LOG4", 0, 6, 0, true, Tier::Special } }, - { Instruction::INVALID, { "INVALID", 0, 0, 0, true, Tier::Zero } }, { Instruction::CREATE, { "CREATE", 0, 3, 1, true, Tier::Special } }, { Instruction::CALL, { "CALL", 0, 7, 1, true, Tier::Special } }, { Instruction::CALLCODE, { "CALLCODE", 0, 7, 1, true, Tier::Special } }, { Instruction::RETURN, { "RETURN", 0, 2, 0, true, Tier::Zero } }, { Instruction::DELEGATECALL,{ "DELEGATECALL", 0, 6, 1, true, Tier::Special } }, + { Instruction::INVALID, { "INVALID", 0, 0, 0, true, Tier::Zero } }, { Instruction::SUICIDE, { "SUICIDE", 0, 1, 0, true, Tier::Zero } } }; diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index 7432f04d9..a8a72234d 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -171,13 +171,13 @@ enum class Instruction: uint8_t LOG3, ///< Makes a log entry; 3 topics. LOG4, ///< Makes a log entry; 4 topics. - INVALID = 0xef, ///< invalid instruction for expressing runtime errors (e.g., division-by-zero) - CREATE = 0xf0, ///< create a new account with associated code CALL, ///< message-call into an account CALLCODE, ///< message-call with another account's code only RETURN, ///< halt execution returning output data DELEGATECALL, ///< like CALLCODE but keeps caller's value and sender + + INVALID = 0xfe, ///< invalid instruction for expressing runtime errors (e.g., division-by-zero) SUICIDE = 0xff ///< halt execution and register account for later deletion }; From ae2b59d18a181bfbcb563b91866611d2e5e55b41 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 26 Jan 2017 15:59:29 +0100 Subject: [PATCH 102/118] Fix optimizer with regards to INVALID instruction. --- libevmasm/PeepholeOptimiser.cpp | 1 + libevmasm/SemanticInformation.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/libevmasm/PeepholeOptimiser.cpp b/libevmasm/PeepholeOptimiser.cpp index 923ffa67d..528ce1c47 100644 --- a/libevmasm/PeepholeOptimiser.cpp +++ b/libevmasm/PeepholeOptimiser.cpp @@ -199,6 +199,7 @@ struct UnreachableCode it[0] != Instruction::JUMP && it[0] != Instruction::RETURN && it[0] != Instruction::STOP && + it[0] != Instruction::INVALID && it[0] != Instruction::SUICIDE ) return false; diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index 23a00d951..d3ce4735c 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -118,6 +118,7 @@ bool SemanticInformation::altersControlFlow(AssemblyItem const& _item) case Instruction::RETURN: case Instruction::SUICIDE: case Instruction::STOP: + case Instruction::INVALID: return true; default: return false; From 390bebaaf9e2af51c7e2f72337d1e7b23f51486a Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 26 Jan 2017 15:59:48 +0100 Subject: [PATCH 103/118] Split line. --- libsolidity/codegen/CompilerContext.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 3bb6c9533..7577a6067 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -224,7 +224,9 @@ CompilerContext& CompilerContext::appendConditionalInvalid() { *this << Instruction::ISZERO; eth::AssemblyItem afterTag = appendConditionalJump(); - return *this << Instruction::INVALID << afterTag; + *this << Instruction::INVALID; + *this << afterTag; + return *this; } void CompilerContext::resetVisitedNodes(ASTNode const* _node) From d9fbb83861153499b4aec5525db85ec59445abd1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 26 Jan 2017 16:35:51 +0100 Subject: [PATCH 104/118] Allow inserting low-level functions without calling them. --- libsolidity/codegen/CompilerContext.cpp | 21 ++++++++++++++++----- libsolidity/codegen/CompilerContext.h | 10 ++++++++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 7577a6067..a83161098 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -70,19 +70,30 @@ void CompilerContext::callLowLevelFunction( eth::AssemblyItem retTag = pushNewTag(); CompilerUtils(*this).moveIntoStack(_inArgs); + *this << lowLevelFunctionTag(_name, _inArgs, _outArgs, _generator); + + appendJump(eth::AssemblyItem::JumpType::IntoFunction); + adjustStackOffset(int(_outArgs) - 1 - _inArgs); + *this << retTag.tag(); +} + +eth::AssemblyItem CompilerContext::lowLevelFunctionTag( + string const& _name, + unsigned _inArgs, + unsigned _outArgs, + function const& _generator +) +{ auto it = m_lowLevelFunctions.find(_name); if (it == m_lowLevelFunctions.end()) { eth::AssemblyItem tag = newTag().pushTag(); m_lowLevelFunctions.insert(make_pair(_name, tag)); m_lowLevelFunctionGenerationQueue.push(make_tuple(_name, _inArgs, _outArgs, _generator)); - *this << tag; + return tag; } else - *this << it->second; - appendJump(eth::AssemblyItem::JumpType::IntoFunction); - adjustStackOffset(int(_outArgs) - 1 - _inArgs); - *this << retTag.tag(); + return it->second; } void CompilerContext::appendMissingLowLevelFunctions() diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 58d6cb2ad..c37142c91 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -104,6 +104,16 @@ public: unsigned _outArgs, std::function const& _generator ); + /// Returns the tag of the named low-level function and inserts the generator into the + /// list of low-level-functions to be generated, unless it already exists. + /// Note that the generator should not assume that objects are still alive when it is called, + /// unless they are guaranteed to be alive for the whole run of the compiler (AST nodes, for example). + eth::AssemblyItem lowLevelFunctionTag( + std::string const& _name, + unsigned _inArgs, + unsigned _outArgs, + std::function const& _generator + ); /// Generates the code for missing low-level functions, i.e. calls the generators passed above. void appendMissingLowLevelFunctions(); From a98fa41897950b84b0217c9ce3c79c20009d0c8d Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 26 Jan 2017 16:24:03 +0100 Subject: [PATCH 105/118] Uninitialized internal function should call INVALID. --- libsolidity/codegen/CompilerUtils.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 67877bbfc..477f021a8 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -807,7 +807,9 @@ void CompilerUtils::pushZeroValue(Type const& _type) { if (funType->location() == FunctionType::Location::Internal) { - m_context.appendInvalid(); + m_context << m_context.lowLevelFunctionTag("$invalidFunction", 0, 0, [](CompilerContext& _context) { + _context.appendInvalid(); + }); return; } } From 7660736aa269c69a69ef728924f566d72661638a Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 26 Jan 2017 16:28:44 +0100 Subject: [PATCH 106/118] Document special case of zero-initialized internal function. --- docs/control-structures.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/control-structures.rst b/docs/control-structures.rst index a398d8570..d7005717b 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -394,8 +394,13 @@ Currently, Solidity automatically generates a runtime exception in the following #. If you perform an external function call targeting a contract that contains no code. #. If your contract receives Ether via a public function without ``payable`` modifier (including the constructor and the fallback function). #. If your contract receives Ether via a public accessor function. +#. If you call a zero-initialized variable of internal function type. -Internally, Solidity performs an "invalid jump" when a user-provided exception is thrown. In contrast, it performs an invalid (i.e., non-existent) operation if a runtime exception is encountered. In both cases, this causes the EVM to revert all changes made to the state. The reason for this is that there is no safe way to continue execution, because an expected effect did not occur. Because we want to retain the atomicity of transactions, the safest thing to do is to revert all changes and make the whole transaction (or at least call) without effect. +Internally, Solidity performs an "invalid jump" when a user-provided exception is thrown. In contrast, it performs an invalid operation +(code ``0xfe``) if a runtime exception is encountered. In both cases, this causes +the EVM to revert all changes made to the state. The reason for this is that there is no safe way to continue execution, because an expected effect +did not occur. Because we want to retain the atomicity of transactions, the safest thing to do is to revert all changes and make the whole transaction +(or at least call) without effect. .. index:: ! assembly, ! asm, ! evmasm From a9c6ff4ac8c271aaada1965894b34933b662c044 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 26 Jan 2017 18:20:54 +0100 Subject: [PATCH 107/118] Refactor json return type generation. --- libsolidity/ast/Types.cpp | 18 ------- libsolidity/ast/Types.h | 2 - libsolidity/interface/InterfaceHandler.cpp | 48 +++++++++++-------- libsolidity/interface/InterfaceHandler.h | 10 ++++ .../SolidityNameAndTypeResolution.cpp | 22 ++++----- 5 files changed, 49 insertions(+), 51 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 971e1f184..dbabc8db5 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2512,24 +2512,6 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound) ); } -vector const FunctionType::parameterTypeNames(bool _addDataLocation) const -{ - vector names; - for (TypePointer const& t: parameterTypes()) - names.push_back(t->canonicalName(_addDataLocation)); - - return names; -} - -vector const FunctionType::returnParameterTypeNames(bool _addDataLocation) const -{ - vector names; - for (TypePointer const& t: m_returnParameterTypes) - names.push_back(t->canonicalName(_addDataLocation)); - - return names; -} - TypePointer const& FunctionType::selfType() const { solAssert(bound(), "Function is not bound."); diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 770cbb300..a5147f176 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -915,10 +915,8 @@ public: TypePointers parameterTypes() const; std::vector parameterNames() const; - std::vector const parameterTypeNames(bool _addDataLocation) const; TypePointers const& returnParameterTypes() const { return m_returnParameterTypes; } std::vector const& returnParameterNames() const { return m_returnParameterNames; } - std::vector const returnParameterTypeNames(bool _addDataLocation) const; /// @returns the "self" parameter type for a bound function TypePointer const& selfType() const; diff --git a/libsolidity/interface/InterfaceHandler.cpp b/libsolidity/interface/InterfaceHandler.cpp index 9944bb22d..6c1bb0c4d 100644 --- a/libsolidity/interface/InterfaceHandler.cpp +++ b/libsolidity/interface/InterfaceHandler.cpp @@ -30,20 +30,6 @@ Json::Value InterfaceHandler::abiInterface(ContractDefinition const& _contractDe { Json::Value abi(Json::arrayValue); - auto populateParameters = [](vector const& _paramNames, vector const& _paramTypes) - { - Json::Value params(Json::arrayValue); - solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match"); - for (unsigned i = 0; i < _paramNames.size(); ++i) - { - Json::Value param; - param["name"] = _paramNames[i]; - param["type"] = _paramTypes[i]; - params.append(param); - } - return params; - }; - for (auto it: _contractDef.interfaceFunctions()) { auto externalFunctionType = it.second->interfaceFunctionType(); @@ -52,13 +38,15 @@ Json::Value InterfaceHandler::abiInterface(ContractDefinition const& _contractDe method["name"] = it.second->declaration().name(); method["constant"] = it.second->isConstant(); method["payable"] = it.second->isPayable(); - method["inputs"] = populateParameters( + method["inputs"] = formatTypeList( externalFunctionType->parameterNames(), - externalFunctionType->parameterTypeNames(_contractDef.isLibrary()) + externalFunctionType->parameterTypes(), + _contractDef.isLibrary() ); - method["outputs"] = populateParameters( + method["outputs"] = formatTypeList( externalFunctionType->returnParameterNames(), - externalFunctionType->returnParameterTypeNames(_contractDef.isLibrary()) + externalFunctionType->returnParameterTypes(), + _contractDef.isLibrary() ); abi.append(method); } @@ -69,9 +57,10 @@ Json::Value InterfaceHandler::abiInterface(ContractDefinition const& _contractDe auto externalFunction = FunctionType(*_contractDef.constructor(), false).interfaceFunctionType(); solAssert(!!externalFunction, ""); method["payable"] = externalFunction->isPayable(); - method["inputs"] = populateParameters( + method["inputs"] = formatTypeList( externalFunction->parameterNames(), - externalFunction->parameterTypeNames(_contractDef.isLibrary()) + externalFunction->parameterTypes(), + _contractDef.isLibrary() ); abi.append(method); } @@ -179,6 +168,25 @@ Json::Value InterfaceHandler::devDocumentation(ContractDefinition const& _contra return doc; } +Json::Value InterfaceHandler::formatTypeList( + vector const& _names, + vector const& _types, + bool _forLibrary +) +{ + Json::Value params(Json::arrayValue); + solAssert(_names.size() == _types.size(), "Names and types vector size does not match"); + for (unsigned i = 0; i < _names.size(); ++i) + { + solAssert(_types[i], ""); + Json::Value param; + param["name"] = _names[i]; + param["type"] = _types[i]->canonicalName(_forLibrary); + params.append(param); + } + return params; +} + string InterfaceHandler::extractDoc(multimap const& _tags, string const& _name) { string value; diff --git a/libsolidity/interface/InterfaceHandler.h b/libsolidity/interface/InterfaceHandler.h index b7e1bb003..56927d44e 100644 --- a/libsolidity/interface/InterfaceHandler.h +++ b/libsolidity/interface/InterfaceHandler.h @@ -37,6 +37,8 @@ namespace solidity // Forward declarations class ContractDefinition; +class Type; +using TypePointer = std::shared_ptr; struct DocTag; enum class DocumentationType: uint8_t; @@ -84,6 +86,14 @@ public: static Json::Value devDocumentation(ContractDefinition const& _contractDef); private: + /// @returns a json value suitable for a list of types in function input or output + /// parameters or other places. If @a _forLibrary is true, complex types are referenced + /// by name, otherwise they are anonymously expanded. + static Json::Value formatTypeList( + std::vector const& _names, + std::vector const& _types, + bool _forLibrary + ); /// @returns concatenation of all content under the given tag name. static std::string extractDoc(std::multimap const& _tags, std::string const& _name); }; diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index b6067ea5c..472067ecd 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -1102,25 +1102,25 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors) BOOST_REQUIRE((contract = retrieveContract(source, 0)) != nullptr); FunctionTypePointer function = retrieveFunctionBySignature(*contract, "foo()"); BOOST_REQUIRE(function && function->hasDeclaration()); - auto returnParams = function->returnParameterTypeNames(false); - BOOST_CHECK_EQUAL(returnParams.at(0), "uint256"); + auto returnParams = function->returnParameterTypes(); + BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(false), "uint256"); BOOST_CHECK(function->isConstant()); function = retrieveFunctionBySignature(*contract, "map(uint256)"); BOOST_REQUIRE(function && function->hasDeclaration()); - auto params = function->parameterTypeNames(false); - BOOST_CHECK_EQUAL(params.at(0), "uint256"); - returnParams = function->returnParameterTypeNames(false); - BOOST_CHECK_EQUAL(returnParams.at(0), "bytes4"); + auto params = function->parameterTypes(); + BOOST_CHECK_EQUAL(params.at(0)->canonicalName(false), "uint256"); + returnParams = function->returnParameterTypes(); + BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(false), "bytes4"); BOOST_CHECK(function->isConstant()); function = retrieveFunctionBySignature(*contract, "multiple_map(uint256,uint256)"); BOOST_REQUIRE(function && function->hasDeclaration()); - params = function->parameterTypeNames(false); - BOOST_CHECK_EQUAL(params.at(0), "uint256"); - BOOST_CHECK_EQUAL(params.at(1), "uint256"); - returnParams = function->returnParameterTypeNames(false); - BOOST_CHECK_EQUAL(returnParams.at(0), "bytes4"); + params = function->parameterTypes(); + BOOST_CHECK_EQUAL(params.at(0)->canonicalName(false), "uint256"); + BOOST_CHECK_EQUAL(params.at(1)->canonicalName(false), "uint256"); + returnParams = function->returnParameterTypes(); + BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(false), "bytes4"); BOOST_CHECK(function->isConstant()); } From cc7834f2a96e305bba3c7470271560dbf99d17f9 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 27 Jan 2017 00:00:05 +0100 Subject: [PATCH 108/118] Doc tags followed by newline are now parsed properly --- libsolidity/parsing/DocStringParser.cpp | 2 +- test/libsolidity/SolidityNatspecJSON.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libsolidity/parsing/DocStringParser.cpp b/libsolidity/parsing/DocStringParser.cpp index 5d9a75eff..481a33497 100644 --- a/libsolidity/parsing/DocStringParser.cpp +++ b/libsolidity/parsing/DocStringParser.cpp @@ -31,8 +31,8 @@ static inline string::const_iterator firstWsOrNl( string::const_iterator _end ) { + auto nlPos = find(_pos, _end, '\n'); auto wsPos = firstSpaceOrTab(_pos, _end); - auto nlPos = find(wsPos, _end, '\n'); return (wsPos < nlPos) ? wsPos : nlPos; } diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index 0657c321f..42b989dd1 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -215,7 +215,7 @@ BOOST_AUTO_TEST_CASE(dev_desc_after_nl) char const* natspec = "{" "\"methods\":{" " \"mul(uint256,uint256)\":{ \n" - " \"details\": \" Multiplies a number by 7 and adds second parameter\",\n" + " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n" " \"params\": {\n" " \"a\": \"Documentation for the first parameter\",\n" " \"second\": \"Documentation for the second parameter\"\n" @@ -402,7 +402,7 @@ BOOST_AUTO_TEST_CASE(dev_return_desc_after_nl) " \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n" " \"second\": \"Documentation for the second parameter\"\n" " },\n" - " \"return\": \" The result of the multiplication\"\n" + " \"return\": \"The result of the multiplication\"\n" " }\n" "}}"; From 98b51b378e42ba67d727c0d2245314cdaab2fd53 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 27 Jan 2017 00:09:00 +0100 Subject: [PATCH 109/118] More verbose function naming --- libsolidity/parsing/DocStringParser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libsolidity/parsing/DocStringParser.cpp b/libsolidity/parsing/DocStringParser.cpp index 481a33497..c2af82de9 100644 --- a/libsolidity/parsing/DocStringParser.cpp +++ b/libsolidity/parsing/DocStringParser.cpp @@ -26,7 +26,7 @@ static inline string::const_iterator firstSpaceOrTab( return (spacePos < tabPos) ? spacePos : tabPos; } -static inline string::const_iterator firstWsOrNl( +static inline string::const_iterator firstWhitespaceOrNewline( string::const_iterator _pos, string::const_iterator _end ) @@ -66,7 +66,7 @@ bool DocStringParser::parse(string const& _docString, ErrorList& _errors) if (tagPos != end && tagPos < nlPos) { // we found a tag - auto tagNameEndPos = firstWsOrNl(tagPos, end); + auto tagNameEndPos = firstWhitespaceOrNewline(tagPos, end); if (tagNameEndPos == end) { appendError("End of tag " + string(tagPos, tagNameEndPos) + "not found"); From bff8fc23e6cc602511b52aaa665e63b948eba068 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 27 Jan 2017 10:18:53 +0100 Subject: [PATCH 110/118] Changelog and review suggestions. --- Changelog.md | 2 ++ docs/control-structures.rst | 2 +- libsolidity/codegen/ContractCompiler.cpp | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 71a1e096a..c87d16477 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,8 @@ Features: * Metadata: Do not include platform in the version number. * Metadata: Add option to store sources as literal content. * Code generator: Extract array utils into low-level functions. + * Code generator: Internal errors (array out of bounds, etc.) now cause a reversion by using an invalid + instruction (0xfe) instead of an invalid jump. Invalid jump is still kept for explicit throws. Bugfixes: * Code generator: Allow recursive structs. diff --git a/docs/control-structures.rst b/docs/control-structures.rst index d7005717b..ff9b245a9 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -397,7 +397,7 @@ Currently, Solidity automatically generates a runtime exception in the following #. If you call a zero-initialized variable of internal function type. Internally, Solidity performs an "invalid jump" when a user-provided exception is thrown. In contrast, it performs an invalid operation -(code ``0xfe``) if a runtime exception is encountered. In both cases, this causes +(instruction ``0xfe``) if a runtime exception is encountered. In both cases, this causes the EVM to revert all changes made to the state. The reason for this is that there is no safe way to continue execution, because an expected effect did not occur. Because we want to retain the atomicity of transactions, the safest thing to do is to revert all changes and make the whole transaction (or at least call) without effect. diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 3ca2f3758..4d33927db 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -919,7 +919,7 @@ eth::AssemblyPointer ContractCompiler::cloneRuntime() //Propagate error condition (if DELEGATECALL pushes 0 on stack). a << Instruction::ISZERO; a << Instruction::ISZERO; - eth::AssemblyItem afterTag = a.appendJumpI(); + eth::AssemblyItem afterTag = a.appendJumpI().tag(); a << Instruction::INVALID << afterTag; //@todo adjust for larger return values, make this dynamic. a << u256(0x20) << u256(0) << Instruction::RETURN; From 0e021e76a58d0ae7a7fe1647f079e7e5087b4641 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 27 Jan 2017 11:19:48 +0100 Subject: [PATCH 111/118] Minor changes. --- libsolidity/parsing/DocStringParser.cpp | 36 ++++++++++++------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/libsolidity/parsing/DocStringParser.cpp b/libsolidity/parsing/DocStringParser.cpp index c2af82de9..8e9121266 100644 --- a/libsolidity/parsing/DocStringParser.cpp +++ b/libsolidity/parsing/DocStringParser.cpp @@ -1,14 +1,19 @@ #include -#include #include +#include +#include + using namespace std; using namespace dev; using namespace dev::solidity; -static inline string::const_iterator skipLineOrEOS( +namespace +{ + +string::const_iterator skipLineOrEOS( string::const_iterator _nlPos, string::const_iterator _end ) @@ -16,38 +21,35 @@ static inline string::const_iterator skipLineOrEOS( return (_nlPos == _end) ? _end : ++_nlPos; } -static inline string::const_iterator firstSpaceOrTab( +string::const_iterator firstSpaceOrTab( string::const_iterator _pos, string::const_iterator _end ) { - auto spacePos = find(_pos, _end, ' '); - auto tabPos = find(_pos, _end, '\t'); - return (spacePos < tabPos) ? spacePos : tabPos; + return boost::range::find_first_of(make_pair(_pos, _end), " \t"); } -static inline string::const_iterator firstWhitespaceOrNewline( +string::const_iterator firstWhitespaceOrNewline( string::const_iterator _pos, string::const_iterator _end ) { - auto nlPos = find(_pos, _end, '\n'); - auto wsPos = firstSpaceOrTab(_pos, _end); - return (wsPos < nlPos) ? wsPos : nlPos; + return boost::range::find_first_of(make_pair(_pos, _end), " \t\n"); } -static inline string::const_iterator skipWhitespace( +string::const_iterator skipWhitespace( string::const_iterator _pos, string::const_iterator _end ) { auto currPos = _pos; - while ((*currPos == ' ' || *currPos == '\t') && currPos != _end) + while (currPos != _end && (*currPos == ' ' || *currPos == '\t')) currPos += 1; return currPos; } +} bool DocStringParser::parse(string const& _docString, ErrorList& _errors) { @@ -99,13 +101,9 @@ DocStringParser::iter DocStringParser::parseDocTagLine(iter _pos, iter _end, boo solAssert(!!m_lastTag, ""); auto nlPos = find(_pos, _end, '\n'); if (_appending && _pos < _end && *_pos != ' ' && *_pos != '\t') - { m_lastTag->content += " "; - } else if (!_appending) - { _pos = skipWhitespace(_pos, _end); - } copy(_pos, nlPos, back_inserter(m_lastTag->content)); return skipLineOrEOS(nlPos, _end); } @@ -116,13 +114,13 @@ DocStringParser::iter DocStringParser::parseDocTagParam(iter _pos, iter _end) auto nameStartPos = skipWhitespace(_pos, _end); if (nameStartPos == _end) { - appendError("No param name given" + string(nameStartPos, _end)); + appendError("No param name given"); return _end; } auto nameEndPos = firstSpaceOrTab(nameStartPos, _end); if (nameEndPos == _end) { - appendError("End of param name not found" + string(nameStartPos, _end)); + appendError("End of param name not found: " + string(nameStartPos, _end)); return _end; } auto paramName = string(nameStartPos, nameEndPos); @@ -130,7 +128,7 @@ DocStringParser::iter DocStringParser::parseDocTagParam(iter _pos, iter _end) auto descStartPos = skipWhitespace(nameEndPos, _end); if (descStartPos == _end) { - appendError("No description given for param" + paramName); + appendError("No description given for param " + paramName); return _end; } From f01c8c07e5647e02a45ad0802578153c7273668a Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 27 Jan 2017 12:13:14 +0100 Subject: [PATCH 112/118] Tests for natspect parsing failure cases --- test/libsolidity/SolidityNatspecJSON.cpp | 42 ++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index 42b989dd1..ac55382bf 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -635,6 +635,48 @@ BOOST_AUTO_TEST_CASE(dev_documenting_nonexistent_param) expectNatspecError(sourceCode); } +BOOST_AUTO_TEST_CASE(dev_documenting_no_paramname) +{ + char const* sourceCode = R"( + contract test { + /// @dev Multiplies a number by 7 and adds second parameter + /// @param a Documentation for the first parameter + /// @param + function mul(uint a, uint second) returns(uint d) { return a * 7 + second; } + } + )"; + + expectNatspecError(sourceCode); +} + +BOOST_AUTO_TEST_CASE(dev_documenting_no_paramname_end) +{ + char const* sourceCode = R"( + contract test { + /// @dev Multiplies a number by 7 and adds second parameter + /// @param a Documentation for the first parameter + /// @param se + function mul(uint a, uint second) returns(uint d) { return a * 7 + second; } + } + )"; + + expectNatspecError(sourceCode); +} + +BOOST_AUTO_TEST_CASE(dev_documenting_no_param_description) +{ + char const* sourceCode = R"( + contract test { + /// @dev Multiplies a number by 7 and adds second parameter + /// @param a Documentation for the first parameter + /// @param second + function mul(uint a, uint second) returns(uint d) { return a * 7 + second; } + } + )"; + + expectNatspecError(sourceCode); +} + BOOST_AUTO_TEST_SUITE_END() } From c08f659634b5808a1283861939053df2584cab7d Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 27 Jan 2017 14:09:19 +0100 Subject: [PATCH 113/118] Changelog entry. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 7dd607d8d..4edf76f0e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,6 +11,7 @@ Features: Bugfixes: * Code generator: Allow recursive structs. * Type checker: Allow multiple events of the same name (but with different arities or argument types) + * Natspec parser: Fix error with ``@param`` parsing and whitespace. ### 0.4.8 (2017-01-13) From 965dc7201626ceb9f5406ca84189a478cd8a55bd Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 27 Jan 2017 13:24:05 +0000 Subject: [PATCH 114/118] Mention in changelog that invalid as an opcode is valid inline assembly --- Changelog.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 8dd1b89c7..7a9d48180 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,13 +4,14 @@ Features: * Compiler interface: Contracts and libraries can be referenced with a ``file:`` prefix to make them unique. * Compiler interface: Report source location for "stack too deep" errors. * AST: Use deterministic node identifiers. + * Inline assembly: introduce ``invalid`` (EIP141) as an opcode. * Type system: Introduce type identifier strings. * Type checker: Warn about invalid checksum for addresses and deduce type from valid ones. * Metadata: Do not include platform in the version number. * Metadata: Add option to store sources as literal content. * Code generator: Extract array utils into low-level functions. * Code generator: Internal errors (array out of bounds, etc.) now cause a reversion by using an invalid - instruction (0xfe) instead of an invalid jump. Invalid jump is still kept for explicit throws. + instruction (0xfe - EIP141) instead of an invalid jump. Invalid jump is still kept for explicit throws. Bugfixes: * Code generator: Allow recursive structs. From bfa3b4ca78fd1afe0756631ce0d1ccb9a6d9c467 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 27 Jan 2017 13:26:40 +0000 Subject: [PATCH 115/118] Mention invalid in docs --- docs/control-structures.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/control-structures.rst b/docs/control-structures.rst index ff9b245a9..c83d654e7 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -632,6 +632,8 @@ The opcodes ``pushi`` and ``jumpdest`` cannot be used directly. +-------------------------+------+-----------------------------------------------------------------+ | selfdestruct(a) | `-` | end execution, destroy current contract and send funds to a | +-------------------------+------+-----------------------------------------------------------------+ +| invalid | `-` | end execution with invalid instruction | ++-------------------------+------+-----------------------------------------------------------------+ | log0(p, s) | `-` | log without topics and data mem[p..(p+s)) | +-------------------------+------+-----------------------------------------------------------------+ | log1(p, s, t1) | `-` | log with topic t1 and data mem[p..(p+s)) | From eb530aa217387092a84057b550c7665a4acf72b6 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 27 Jan 2017 21:24:58 +0000 Subject: [PATCH 116/118] Add tests for invalid instruction --- test/libsolidity/InlineAssembly.cpp | 5 +++++ test/libsolidity/SolidityEndToEndTest.cpp | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index c051a9822..cf0343a92 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -182,6 +182,11 @@ BOOST_AUTO_TEST_CASE(error_tag) BOOST_CHECK(successAssemble("{ invalidJumpLabel }")); } +BOOST_AUTO_TEST_CASE(designated_invalid_instruction) +{ + BOOST_CHECK(successAssemble("{ invalid }")); +} + BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_declaration) { // Error message: "Cannot use instruction names for identifier names." diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 646017fbf..4075a0160 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -9043,6 +9043,21 @@ BOOST_AUTO_TEST_CASE(recursive_structs) BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(1))); } +BOOST_AUTO_TEST_CASE(invalid_instruction) +{ + char const* sourceCode = R"( + contract C { + function f() { + assembly { + invalid + } + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f()") == encodeArgs()); +} + BOOST_AUTO_TEST_SUITE_END() } From b3f0d713a18b78b577e95175876646e53be5df41 Mon Sep 17 00:00:00 2001 From: VoR0220 Date: Mon, 30 Jan 2017 13:41:33 -0600 Subject: [PATCH 117/118] fix for linker wrt binaries generated with import statements Signed-off-by: VoR0220 --- solc/CommandLineInterface.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 2c1f06440..dd80e1894 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -426,7 +426,9 @@ bool CommandLineInterface::parseLibraryOption(string const& _input) for (string const& lib: libraries) if (!lib.empty()) { - auto colon = lib.find(':'); + //search for last colon in string as our binaries output placeholders in the form of file:Name + //so we need to search for the second `:` in the string + auto colon = lib.rfind(':'); if (colon == string::npos) { cerr << "Colon separator missing in library address specifier \"" << lib << "\"" << endl; From 7b18c9df1dfa0076566bfa1e4a3bc5e5ba9c8594 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 31 Jan 2017 17:54:03 +0100 Subject: [PATCH 118/118] Release date for 0.4.9 --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 7a9d48180..03ee21937 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,4 @@ -### 0.4.9 (unreleased) +### 0.4.9 (2017-01-31) Features: * Compiler interface: Contracts and libraries can be referenced with a ``file:`` prefix to make them unique.