From a63d850f5053d26d0ca9868ecd0a293824414b96 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 12 Nov 2020 14:50:51 +0000 Subject: [PATCH 01/29] Improve ewasm commandline tests --- .../input.json | 2 +- .../output.json | 2 +- .../input.json | 22 +++++++++++++++++++ .../output.json | 2 ++ 4 files changed, 26 insertions(+), 2 deletions(-) rename test/cmdlineTests/{standard_eWasm_requested => standard_ewasm_requested}/input.json (85%) rename test/cmdlineTests/{standard_eWasm_requested => standard_ewasm_requested}/output.json (74%) create mode 100644 test/cmdlineTests/standard_ewasm_requested_abstract/input.json create mode 100644 test/cmdlineTests/standard_ewasm_requested_abstract/output.json diff --git a/test/cmdlineTests/standard_eWasm_requested/input.json b/test/cmdlineTests/standard_ewasm_requested/input.json similarity index 85% rename from test/cmdlineTests/standard_eWasm_requested/input.json rename to test/cmdlineTests/standard_ewasm_requested/input.json index 25cc1705d..d3c6cc892 100644 --- a/test/cmdlineTests/standard_eWasm_requested/input.json +++ b/test/cmdlineTests/standard_ewasm_requested/input.json @@ -16,7 +16,7 @@ }, "outputSelection": { - "*": { "*": ["ewasm.wast"] } + "*": { "*": ["ewasm.wast", "ewasm.wasm"] } } } } diff --git a/test/cmdlineTests/standard_eWasm_requested/output.json b/test/cmdlineTests/standard_ewasm_requested/output.json similarity index 74% rename from test/cmdlineTests/standard_eWasm_requested/output.json rename to test/cmdlineTests/standard_ewasm_requested/output.json index f7a0debaa..a4224fbab 100644 --- a/test/cmdlineTests/standard_eWasm_requested/output.json +++ b/test/cmdlineTests/standard_ewasm_requested/output.json @@ -1,4 +1,4 @@ -{"contracts":{"A":{"C":{"ewasm":{"wast":"(module +{"contracts":{"A":{"C":{"ewasm":{"wasm":"0061736d01000000013a0860000060017e017e60047e7e7e7e017f60087e7e7e7e7e7e7e7e00600c7e7e7e7e7e7e7e7e7e7e7e7e0060017f0060027f7f0060037f7f7f0002510408657468657265756d08636f6465436f7079000708657468657265756d06726576657274000608657468657265756d0c67657443616c6c56616c7565000508657468657265756d0666696e6973680006030a090002020401010103030503010001060100071102066d656d6f72790200046d61696e0004009d030c435f325f6465706c6f7965640061736d0100000001160460000060017e017e60047e7e7e7e017f60027f7f0002130108657468657265756d067265766572740003030504000201010503010001060100071102066d656d6f72790200046d61696e00010ab60204ca0104017e027f057e037f02404200210020002000200042c00010022101200141c0006a210220022001490440000b20001003421086210320032000421088100384422086210420042000422088100484210520022005370000200241086a2005370000200241106a20053700004280011003421086210620064280014210881003844220862107200241186a2007428001422088100484370000200020002000200010022108200020002000200010022109200941c0006a210a200a2009490440000b200a200810000b0b2901017f024042002000200184200284520440000b42002003422088520440000b2003a721040b20040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100342108621022002200042108810038421010b20010b0aec0309dc0103017e027f057e02404200210020002000200042c00010052101200141c0006a210220022001490440000b2000100a210320022003370000200241086a2003370000200241106a2003370000200241186a428001100a370000410010024100290000100a2104410041086a290000100a2105410041106a290000100a210620042005842006410041186a290000100a84845045044020002000200020002000200020002000100c0b4290032107200020002000200020002000200042ce012000200020002007100720002000200020002000200020002007100b0b0b2901017f024042002000200184200284520440000b42002003422088520440000b2003a721040b20040b2601027f0240200020012002200310052105200541c0006a210420042005490440000b0b20040b25000240200020012002200310062004200520062007100520082009200a200b100510000b0b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100842108621022002200042108810088421010b20010b1e01027e02402000100942208621022002200042208810098421010b20010b1b000240200020012002200310062004200520062007100510030b0b1b000240200020012002200310062004200520062007100510010b0b","wast":"(module ;; custom section for sub-module ;; The Keccak-256 hash of the text representation of \"C_2_deployed\": f03f5b9154b9eb6803a947177e38e92e2860de95e90ba0e75eb71a58f18ed589 ;; (@custom \"C_2_deployed\" \"0061736d0100000001160460000060017e017e60047e7e7e7e017f60027f7f0002130108657468657265756d067265766572740003030504000201010503010001060100071102066d656d6f72790200046d61696e00010ab60204ca0104017e027f057e037f02404200210020002000200042c00010022101200141c0006a210220022001490440000b20001003421086210320032000421088100384422086210420042000422088100484210520022005370000200241086a2005370000200241106a20053700004280011003421086210620064280014210881003844220862107200241186a2007428001422088100484370000200020002000200010022108200020002000200010022109200941c0006a210a200a2009490440000b200a200810000b0b2901017f024042002000200184200284520440000b42002003422088520440000b2003a721040b20040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100342108621022002200042108810038421010b20010b\") diff --git a/test/cmdlineTests/standard_ewasm_requested_abstract/input.json b/test/cmdlineTests/standard_ewasm_requested_abstract/input.json new file mode 100644 index 000000000..313fa478c --- /dev/null +++ b/test/cmdlineTests/standard_ewasm_requested_abstract/input.json @@ -0,0 +1,22 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; abstract contract C { }" + } + }, + "settings": + { + "optimizer": + { + "enabled": true, + "details": {"yul": true} + }, + "outputSelection": + { + "*": { "*": ["ewasm.wast", "ewasm.wasm"] } + } + } +} diff --git a/test/cmdlineTests/standard_ewasm_requested_abstract/output.json b/test/cmdlineTests/standard_ewasm_requested_abstract/output.json new file mode 100644 index 000000000..55ff78db9 --- /dev/null +++ b/test/cmdlineTests/standard_ewasm_requested_abstract/output.json @@ -0,0 +1,2 @@ +{"errors":[{"component":"general","formattedMessage":"InternalCompilerError: +","message":"Internal compiler error (/project/solidity/libsolidity/interface/CompilerStack.cpp:1260):","severity":"error","type":"InternalCompilerError"}],"sources":{"A":{"id":0}}} From fcedaba7f77c1ed4ca5eb26550ab7d686a502f58 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 28 Oct 2020 23:40:06 +0000 Subject: [PATCH 02/29] [ewasm] Allow compiling abstract/interface contracts --- libsolidity/interface/CompilerStack.cpp | 3 +++ .../cmdlineTests/standard_ewasm_requested_abstract/output.json | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 5961e58a9..dee9940e6 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -1256,6 +1256,9 @@ void CompilerStack::generateEwasm(ContractDefinition const& _contract) if (m_hasError) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called generateEwasm with errors.")); + if (!_contract.canBeDeployed()) + return; + Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName()); solAssert(!compiledContract.yulIROptimized.empty(), ""); if (!compiledContract.ewasm.empty()) diff --git a/test/cmdlineTests/standard_ewasm_requested_abstract/output.json b/test/cmdlineTests/standard_ewasm_requested_abstract/output.json index 55ff78db9..2858515df 100644 --- a/test/cmdlineTests/standard_ewasm_requested_abstract/output.json +++ b/test/cmdlineTests/standard_ewasm_requested_abstract/output.json @@ -1,2 +1 @@ -{"errors":[{"component":"general","formattedMessage":"InternalCompilerError: -","message":"Internal compiler error (/project/solidity/libsolidity/interface/CompilerStack.cpp:1260):","severity":"error","type":"InternalCompilerError"}],"sources":{"A":{"id":0}}} +{"contracts":{"A":{"C":{"ewasm":{"wasm":"","wast":""}}}},"sources":{"A":{"id":0}}} From 301d7ea39e70de91b63e11bd2c958ec5768ed7a7 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 28 Oct 2020 18:59:19 +0000 Subject: [PATCH 03/29] Add viaIR option to CompilerStack It also sets the experimental flag in the metadata to true. --- libsolidity/interface/CompilerStack.cpp | 53 +++++++++++++++++++++++-- libsolidity/interface/CompilerStack.h | 10 +++++ test/libsolidity/Metadata.cpp | 32 +++++++++++++++ 3 files changed, 91 insertions(+), 4 deletions(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index dee9940e6..874be0a00 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -131,6 +131,13 @@ void CompilerStack::setRemappings(vector const& _remappings) m_remappings = _remappings; } +void CompilerStack::setViaIR(bool _viaIR) +{ + if (m_stackState >= ParsedAndImported) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set viaIR before parsing.")); + m_viaIR = _viaIR; +} + void CompilerStack::setEVMVersion(langutil::EVMVersion _version) { if (m_stackState >= ParsedAndImported) @@ -213,6 +220,7 @@ void CompilerStack::reset(bool _keepSettings) { m_remappings.clear(); m_libraries.clear(); + m_viaIR = false; m_evmVersion = langutil::EVMVersion(); m_modelCheckerSettings = ModelCheckerSettings{}; m_enabledSMTSolvers = smtutil::SMTSolverChoice::All(); @@ -532,10 +540,15 @@ bool CompilerStack::compile(State _stopAfter) { try { - if (m_generateEvmBytecode) - compileContract(*contract, otherCompilers); - if (m_generateIR || m_generateEwasm) + if (m_viaIR || m_generateIR || m_generateEwasm) generateIR(*contract); + if (m_generateEvmBytecode) + { + if (m_viaIR) + generateEVMFromIR(*contract); + else + compileContract(*contract, otherCompilers); + } if (m_generateEwasm) generateEwasm(*contract); } @@ -1250,6 +1263,36 @@ void CompilerStack::generateIR(ContractDefinition const& _contract) tie(compiledContract.yulIR, compiledContract.yulIROptimized) = generator.run(_contract, otherYulSources); } +void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract) +{ + solAssert(m_stackState >= AnalysisPerformed, ""); + if (m_hasError) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called generateEVMFromIR with errors.")); + + if (!_contract.canBeDeployed()) + return; + + Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName()); + solAssert(!compiledContract.yulIROptimized.empty(), ""); + if (!compiledContract.object.bytecode.empty()) + return; + + // Re-parse the Yul IR in EVM dialect + yul::AssemblyStack stack(m_evmVersion, yul::AssemblyStack::Language::StrictAssembly, m_optimiserSettings); + stack.parseAndAnalyze("", compiledContract.yulIROptimized); + stack.optimize(); + + //cout << yul::AsmPrinter{}(*stack.parserResult()->code) << endl; + + // TODO: support passing metadata + auto result = stack.assemble(yul::AssemblyStack::Machine::EVM); + compiledContract.object = std::move(*result.bytecode); + // TODO: support runtimeObject + // TODO: add EIP-170 size check for runtimeObject + // TODO: refactor assemblyItems, runtimeAssemblyItems, generatedSources, + // assemblyString, assemblyJSON, and functionEntryPoints to work with this code path +} + void CompilerStack::generateEwasm(ContractDefinition const& _contract) { solAssert(m_stackState >= AnalysisPerformed, ""); @@ -1395,6 +1438,8 @@ string CompilerStack::createMetadata(Contract const& _contract) const static vector hashes{"ipfs", "bzzr1", "none"}; meta["settings"]["metadata"]["bytecodeHash"] = hashes.at(unsigned(m_metadataHash)); + if (m_viaIR) + meta["settings"]["viaIR"] = m_viaIR; meta["settings"]["evmVersion"] = m_evmVersion.name(); meta["settings"]["compilationTarget"][_contract.contract->sourceUnitName()] = *_contract.contract->annotation().canonicalName; @@ -1517,7 +1562,7 @@ bytes CompilerStack::createCBORMetadata(Contract const& _contract) const else solAssert(m_metadataHash == MetadataHash::None, "Invalid metadata hash"); - if (experimentalMode) + if (experimentalMode || m_viaIR) encoder.pushBool("experimental", true); if (m_release) encoder.pushBytes("solc", VersionCompactBytes); diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index a0c02e881..eb4e7d8a7 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -162,6 +162,10 @@ public: m_parserErrorRecovery = _wantErrorRecovery; } + /// Sets the pipeline to go through the Yul IR or not. + /// Must be set before parsing. + void setViaIR(bool _viaIR); + /// Set the EVM version used before running compile. /// When called without an argument it will revert to the default version. /// Must be set before parsing. @@ -399,7 +403,12 @@ private: /// The IR is stored but otherwise unused. void generateIR(ContractDefinition const& _contract); + /// Generate EVM representation for a single contract. + /// Depends on output generated by generateIR. + void generateEVMFromIR(ContractDefinition const& _contract); + /// Generate Ewasm representation for a single contract. + /// Depends on output generated by generateIR. void generateEwasm(ContractDefinition const& _contract); /// Links all the known library addresses in the available objects. Any unknown @@ -455,6 +464,7 @@ private: OptimiserSettings m_optimiserSettings; RevertStrings m_revertStrings = RevertStrings::Default; State m_stopAfter = State::CompilationSuccessful; + bool m_viaIR = false; langutil::EVMVersion m_evmVersion; ModelCheckerSettings m_modelCheckerSettings; smtutil::SMTSolverChoice m_enabledSMTSolvers; diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index 665a17590..b90d11d09 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -282,6 +282,38 @@ BOOST_AUTO_TEST_CASE(metadata_useLiteralContent) check(sourceCode, false); } +BOOST_AUTO_TEST_CASE(metadata_viair) +{ + char const* sourceCode = R"( + pragma solidity >=0.0; + contract test { + } + )"; + + auto check = [](char const* _src, bool _viair) + { + CompilerStack compilerStack; + compilerStack.setSources({{"", std::string(_src)}}); + compilerStack.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); + compilerStack.setOptimiserSettings(solidity::test::CommonOptions::get().optimize); + compilerStack.setViaIR(_viair); + BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); + string metadata_str = compilerStack.metadata("test"); + Json::Value metadata; + util::jsonParseStrict(metadata_str, metadata); + BOOST_CHECK(solidity::test::isValidMetadata(metadata_str)); + BOOST_CHECK(metadata.isMember("settings")); + if (_viair) + { + BOOST_CHECK(metadata["settings"].isMember("viaIR")); + BOOST_CHECK(metadata["settings"]["viaIR"].asBool()); + } + }; + + check(sourceCode, true); + check(sourceCode, false); +} + BOOST_AUTO_TEST_CASE(metadata_revert_strings) { CompilerStack compilerStack; From e074582bf179c4ab3cb2c463d72dff39cd7b8470 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 28 Oct 2020 23:13:16 +0000 Subject: [PATCH 04/29] Add --experimental-via-ir option to solc --- Changelog.md | 2 + solc/CommandLineInterface.cpp | 8 ++ test/cmdlineTests/viair_subobjects/args | 1 + test/cmdlineTests/viair_subobjects/err | 5 + test/cmdlineTests/viair_subobjects/input.sol | 9 ++ test/cmdlineTests/viair_subobjects/output | 109 +++++++++++++++++++ 6 files changed, 134 insertions(+) create mode 100644 test/cmdlineTests/viair_subobjects/args create mode 100644 test/cmdlineTests/viair_subobjects/err create mode 100644 test/cmdlineTests/viair_subobjects/input.sol create mode 100644 test/cmdlineTests/viair_subobjects/output diff --git a/Changelog.md b/Changelog.md index a8af33eb4..c05dc2fe7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,8 @@ Language Features: * Immutable variables with literal number values are considered pure. Compiler Features: + * Command Line Interface: New option ``--experimental-via-ir`` allows switching compilation process to go through + the Yul intermediate representation. This is highly experimental and is used for development purposes. * Command Line Interface: Report error if file could not be read in ``--standard-json`` mode. * Command Line interface: Report proper error for each output file which could not be written. Previously an exception was thrown, and execution aborted, on the first error. * SMTChecker: Add division by zero checks in the CHC engine. diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 146a095f0..e1c98dcef 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -127,6 +127,7 @@ static string const g_strEVM = "evm"; static string const g_strEVM15 = "evm15"; static string const g_strEVMVersion = "evm-version"; static string const g_strEwasm = "ewasm"; +static string const g_strExperimentalViaIR = "experimental-via-ir"; static string const g_strGeneratedSources = "generated-sources"; static string const g_strGeneratedSourcesRuntime = "generated-sources-runtime"; static string const g_strGas = "gas"; @@ -211,6 +212,7 @@ static string const g_argYul = g_strYul; static string const g_argIR = g_strIR; static string const g_argIROptimized = g_strIROptimized; static string const g_argEwasm = g_strEwasm; +static string const g_argExperimentalViaIR = g_strExperimentalViaIR; static string const g_argLibraries = g_strLibraries; static string const g_argLink = g_strLink; static string const g_argMachine = g_strMachine; @@ -825,6 +827,10 @@ General Information)").c_str(), "Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon, " "byzantium, constantinople, petersburg, istanbul (default) or berlin." ) + ( + g_strExperimentalViaIR.c_str(), + "Turn on experimental compilation mode via the IR (EXPERIMENTAL)." + ) ( g_strRevertStrings.c_str(), po::value()->value_name(boost::join(g_revertStringsArgs, ",")), @@ -1456,6 +1462,8 @@ bool CommandLineInterface::processInput() if (m_args.count(g_argLibraries)) m_compiler->setLibraries(m_libraries); + if (m_args.count(g_argExperimentalViaIR)) + m_compiler->setViaIR(true); m_compiler->setEVMVersion(m_evmVersion); m_compiler->setRevertStringBehaviour(m_revertStrings); // TODO: Perhaps we should not compile unless requested diff --git a/test/cmdlineTests/viair_subobjects/args b/test/cmdlineTests/viair_subobjects/args new file mode 100644 index 000000000..1a5580b80 --- /dev/null +++ b/test/cmdlineTests/viair_subobjects/args @@ -0,0 +1 @@ +--ir-optimized --experimental-via-ir --optimize --bin --bin-runtime \ No newline at end of file diff --git a/test/cmdlineTests/viair_subobjects/err b/test/cmdlineTests/viair_subobjects/err new file mode 100644 index 000000000..c355ba16e --- /dev/null +++ b/test/cmdlineTests/viair_subobjects/err @@ -0,0 +1,5 @@ +Warning: Unused local variable. + --> viair_subobjects/input.sol:7:9: + | +7 | C c = new C(); + | ^^^ diff --git a/test/cmdlineTests/viair_subobjects/input.sol b/test/cmdlineTests/viair_subobjects/input.sol new file mode 100644 index 000000000..13f9f3c60 --- /dev/null +++ b/test/cmdlineTests/viair_subobjects/input.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.6.0; + +contract C {} +contract D { + function f() public { + C c = new C(); + } +} diff --git a/test/cmdlineTests/viair_subobjects/output b/test/cmdlineTests/viair_subobjects/output new file mode 100644 index 000000000..717534fab --- /dev/null +++ b/test/cmdlineTests/viair_subobjects/output @@ -0,0 +1,109 @@ + +======= viair_subobjects/input.sol:C ======= +Binary: +60806040523415600f5760006000fd5b600a80601e600039806000f350fe608060405260006000fd +Binary of the runtime part: + +Optimized IR: +/******************************************************* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *******************************************************/ + +object "C_2" { + code { + { + mstore(64, 128) + if callvalue() { revert(0, 0) } + let _1 := datasize("C_2_deployed") + codecopy(0, dataoffset("C_2_deployed"), _1) + return(0, _1) + } + } + object "C_2_deployed" { + code { + { + mstore(64, 128) + revert(0, 0) + } + } + } +} + + +======= viair_subobjects/input.sol:D ======= +Binary: +608060405234156100105760006000fd5b60d380610020600039806000f350fe6080604052600436101515610074576000803560e01c6326121ff0141561007257341561002a578081fd5b806003193601121561003a578081fd5b6028806080016080811067ffffffffffffffff8211171561005757fe5b50806100ab60803980608083f05050806100708261007e565bf35b505b60006000fd6100a9565b6000604051905081810181811067ffffffffffffffff8211171561009e57fe5b80604052505b919050565bfe60806040523415600f5760006000fd5b600a80601e600039806000f350fe608060405260006000fd +Binary of the runtime part: + +Optimized IR: +/******************************************************* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *******************************************************/ + +object "D_13" { + code { + { + mstore(64, 128) + if callvalue() { revert(0, 0) } + let _1 := datasize("D_13_deployed") + codecopy(0, dataoffset("D_13_deployed"), _1) + return(0, _1) + } + } + object "D_13_deployed" { + code { + { + mstore(64, 128) + if iszero(lt(calldatasize(), 4)) + { + let _1 := 0 + if eq(0x26121ff0, shr(224, calldataload(_1))) + { + if callvalue() { revert(_1, _1) } + if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) } + let _2 := datasize("C_2") + let _3 := add(128, _2) + if or(gt(_3, 0xffffffffffffffff), lt(_3, 128)) { invalid() } + datacopy(128, dataoffset("C_2"), _2) + pop(create(_1, 128, _2)) + return(allocateMemory(_1), _1) + } + } + revert(0, 0) + } + function allocateMemory(size) -> memPtr + { + memPtr := mload(64) + let newFreePtr := add(memPtr, size) + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { invalid() } + mstore(64, newFreePtr) + } + } + object "C_2" { + code { + { + mstore(64, 128) + if callvalue() { revert(0, 0) } + let _1 := datasize("C_2_deployed") + codecopy(0, dataoffset("C_2_deployed"), _1) + return(0, _1) + } + } + object "C_2_deployed" { + code { + { + mstore(64, 128) + revert(0, 0) + } + } + } + } + } +} + From a3381d7260a3338c3f40b2ee828501120e460230 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 12 Nov 2020 01:31:06 +0000 Subject: [PATCH 05/29] Add settings.viaIR to standard json --- Changelog.md | 1 + docs/using-the-compiler.rst | 3 + libsolidity/interface/StandardCompiler.cpp | 10 +- libsolidity/interface/StandardCompiler.h | 1 + .../standard_viair_requested/input.json | 21 ++ .../standard_viair_requested/output.json | 210 ++++++++++++++++++ 6 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 test/cmdlineTests/standard_viair_requested/input.json create mode 100644 test/cmdlineTests/standard_viair_requested/output.json diff --git a/Changelog.md b/Changelog.md index c05dc2fe7..7021f7261 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ Language Features: Compiler Features: * Command Line Interface: New option ``--experimental-via-ir`` allows switching compilation process to go through the Yul intermediate representation. This is highly experimental and is used for development purposes. + * Standard JSON: New option ``settings.viaIR`` allows the same switch as ``--experimental-via-ir`` on the commandline. * Command Line Interface: Report error if file could not be read in ``--standard-json`` mode. * Command Line interface: Report proper error for each output file which could not be written. Previously an exception was thrown, and execution aborted, on the first error. * SMTChecker: Add division by zero checks in the CHC engine. diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index d84af1088..589bf1e29 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -252,6 +252,9 @@ Input Description // Affects type checking and code generation. Can be homestead, // tangerineWhistle, spuriousDragon, byzantium, constantinople, petersburg, istanbul or berlin "evmVersion": "byzantium", + // Optional: Change compilation pipeline to go through the Yul intermediate representation. + // This is a highly EXPERIMENTAL feature, not to be used for production. This is false by default. + "viaIR": true, // Optional: Debugging settings "debug": { // How to treat revert (and require) reason strings. Settings are diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index fa51a5761..5758c4994 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -414,7 +414,7 @@ std::optional checkAuxiliaryInputKeys(Json::Value const& _input) std::optional checkSettingsKeys(Json::Value const& _input) { - static set keys{"parserErrorRecovery", "debug", "evmVersion", "libraries", "metadata", "optimizer", "outputSelection", "remappings", "stopAfter"}; + static set keys{"parserErrorRecovery", "debug", "evmVersion", "libraries", "metadata", "optimizer", "outputSelection", "remappings", "stopAfter", "viaIR"}; return checkKeys(_input, keys, "settings"); } @@ -749,6 +749,13 @@ std::variant StandardCompiler: ret.parserErrorRecovery = settings["parserErrorRecovery"].asBool(); } + if (settings.isMember("viaIR")) + { + if (!settings["viaIR"].isBool()) + return formatFatalError("JSONError", "\"settings.viaIR\" must be a Boolean."); + ret.viaIR = settings["viaIR"].asBool(); + } + if (settings.isMember("evmVersion")) { if (!settings["evmVersion"].isString()) @@ -906,6 +913,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting compilerStack.setSources(sourceList); for (auto const& smtLib2Response: _inputsAndSettings.smtLib2Responses) compilerStack.addSMTLib2Response(smtLib2Response.first, smtLib2Response.second); + compilerStack.setViaIR(_inputsAndSettings.viaIR); compilerStack.setEVMVersion(_inputsAndSettings.evmVersion); compilerStack.setParserErrorRecovery(_inputsAndSettings.parserErrorRecovery); compilerStack.setRemappings(_inputsAndSettings.remappings); diff --git a/libsolidity/interface/StandardCompiler.h b/libsolidity/interface/StandardCompiler.h index 54b3fd3cf..c474ac721 100644 --- a/libsolidity/interface/StandardCompiler.h +++ b/libsolidity/interface/StandardCompiler.h @@ -72,6 +72,7 @@ private: CompilerStack::MetadataHash metadataHash = CompilerStack::MetadataHash::IPFS; Json::Value outputSelection; ModelCheckerSettings modelCheckerSettings = ModelCheckerSettings{}; + bool viaIR = false; }; /// Parses the input json (and potentially invokes the read callback) and either returns diff --git a/test/cmdlineTests/standard_viair_requested/input.json b/test/cmdlineTests/standard_viair_requested/input.json new file mode 100644 index 000000000..6a970357e --- /dev/null +++ b/test/cmdlineTests/standard_viair_requested/input.json @@ -0,0 +1,21 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; contract C {} contract D { function f() public { C c = new C(); } }" + } + }, + "settings": + { + "optimizer": { + "enabled": true + }, + "outputSelection": + { + "*": { "*": ["ir", "evm.bytecode.object", "evm.bytecode.generatedSources", "evm.deployedBytecode.object"] } + }, + "viaIR": true + } +} diff --git a/test/cmdlineTests/standard_viair_requested/output.json b/test/cmdlineTests/standard_viair_requested/output.json new file mode 100644 index 000000000..7bf9dd4da --- /dev/null +++ b/test/cmdlineTests/standard_viair_requested/output.json @@ -0,0 +1,210 @@ +{"contracts":{"A":{"C":{"evm":{"bytecode":{"generatedSources":[],"linkReferences":{},"object":"","opcodes":"","sourceMap":""},"deployedBytecode":{"generatedSources":[],"immutableReferences":{},"linkReferences":{},"object":"","opcodes":"","sourceMap":""}},"ir":"/******************************************************* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *******************************************************/ + + +object \"C_2\" { + code { + mstore(64, 128) + if callvalue() { revert(0, 0) } + + constructor_C_2() + + codecopy(0, dataoffset(\"C_2_deployed\"), datasize(\"C_2_deployed\")) + + return(0, datasize(\"C_2_deployed\")) + + function constructor_C_2() { + + } + + } + object \"C_2_deployed\" { + code { + mstore(64, 128) + + if iszero(lt(calldatasize(), 4)) + { + let selector := shift_right_224_unsigned(calldataload(0)) + switch selector + + default {} + } + if iszero(calldatasize()) { } + revert(0, 0) + + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + + } + + } + +} + +"},"D":{"evm":{"bytecode":{"generatedSources":[],"linkReferences":{},"object":"","opcodes":"","sourceMap":""},"deployedBytecode":{"generatedSources":[],"immutableReferences":{},"linkReferences":{},"object":"","opcodes":"","sourceMap":""}},"ir":"/******************************************************* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *******************************************************/ + + +object \"D_13\" { + code { + mstore(64, 128) + if callvalue() { revert(0, 0) } + + constructor_D_13() + + codecopy(0, dataoffset(\"D_13_deployed\"), datasize(\"D_13_deployed\")) + + return(0, datasize(\"D_13_deployed\")) + + function constructor_D_13() { + + } + + } + object \"D_13_deployed\" { + code { + mstore(64, 128) + + if iszero(lt(calldatasize(), 4)) + { + let selector := shift_right_224_unsigned(calldataload(0)) + switch selector + + case 0x26121ff0 + { + // f() + if callvalue() { revert(0, 0) } + abi_decode_tuple_(4, calldatasize()) + fun_f_12() + let memPos := allocateMemory(0) + let memEnd := abi_encode_tuple__to__fromStack(memPos ) + return(memPos, sub(memEnd, memPos)) + } + + default {} + } + if iszero(calldatasize()) { } + revert(0, 0) + + function abi_decode_tuple_(headStart, dataEnd) { + if slt(sub(dataEnd, headStart), 0) { revert(0, 0) } + + } + + function abi_encode_tuple__to__fromStack(headStart ) -> tail { + tail := add(headStart, 0) + + } + + function allocateMemory(size) -> memPtr { + memPtr := mload(64) + let newFreePtr := add(memPtr, size) + // protect against overflow + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error() } + mstore(64, newFreePtr) + } + + function allocateTemporaryMemory() -> memPtr { + memPtr := mload(64) + } + + function fun_f_12() { + + let _1 := allocateTemporaryMemory() + let _2 := add(_1, datasize(\"C_2\")) + if or(gt(_2, 0xffffffffffffffff), lt(_2, _1)) { panic_error() } + datacopy(_1, dataoffset(\"C_2\"), datasize(\"C_2\")) + _2 := abi_encode_tuple__to__fromStack(_2) + + let expr_9_address := create(0, _1, sub(_2, _1)) + + releaseTemporaryMemory() + let vloc_c_6_address := expr_9_address + + } + + function panic_error() { + invalid() + } + + function releaseTemporaryMemory() { + } + + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + + } + /******************************************************* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *******************************************************/ + + object \"C_2\" { + code { + mstore(64, 128) + if callvalue() { revert(0, 0) } + + constructor_C_2() + + codecopy(0, dataoffset(\"C_2_deployed\"), datasize(\"C_2_deployed\")) + + return(0, datasize(\"C_2_deployed\")) + + function constructor_C_2() { + + } + + } + object \"C_2_deployed\" { + code { + mstore(64, 128) + + if iszero(lt(calldatasize(), 4)) + { + let selector := shift_right_224_unsigned(calldataload(0)) + switch selector + + default {} + } + if iszero(calldatasize()) { } + revert(0, 0) + + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + + } + + } + + } + + } + +} + +"}}},"errors":[{"component":"general","errorCode":"2072","formattedMessage":"A:2:73: Warning: Unused local variable. +pragma solidity >=0.0; contract C {} contract D { function f() public { C c = new C(); } } + ^-^ +","message":"Unused local variable.","severity":"warning","sourceLocation":{"end":111,"file":"A","start":108},"type":"Warning"}],"sources":{"A":{"id":0}}} From 5ca7a24896dd6176a0717b7ac574dce8bb0f8348 Mon Sep 17 00:00:00 2001 From: Martin Blicha Date: Mon, 9 Nov 2020 14:50:41 +0100 Subject: [PATCH 06/29] [SMTChecker] Added support for precise modeling of external calls to `this`. Modeling external calls to this, since we can trust these calls. fixed problem with transaction data not being restored after trusted external call update to the tests additional tests changelog entry added tests for external getters of this --- Changelog.md | 1 + libsolidity/formal/BMC.cpp | 15 +--- libsolidity/formal/CHC.cpp | 72 +++++++++++++++---- libsolidity/formal/CHC.h | 1 + libsolidity/formal/SMTEncoder.cpp | 13 ++++ libsolidity/formal/SMTEncoder.h | 4 ++ .../functions/this_external_call.sol | 2 - .../functions/this_external_call_2.sol | 16 +++++ .../functions/this_external_call_return.sol | 2 - .../functions/this_external_call_sender.sol | 28 ++++++++ .../this_external_call_tx_origin.sol | 14 ++++ .../functions/this_external_getter_1.sol | 12 ++++ .../functions/this_external_getter_2.sol | 12 ++++ .../functions/this_external_getter_3.sol | 12 ++++ .../smtCheckerTests/functions/this_state.sol | 2 - 15 files changed, 172 insertions(+), 34 deletions(-) create mode 100644 test/libsolidity/smtCheckerTests/functions/this_external_call_2.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/this_external_call_sender.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/this_external_call_tx_origin.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/this_external_getter_1.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/this_external_getter_2.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/this_external_getter_3.sol diff --git a/Changelog.md b/Changelog.md index a8af33eb4..b44121578 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,7 @@ Compiler Features: * Command Line interface: Report proper error for each output file which could not be written. Previously an exception was thrown, and execution aborted, on the first error. * SMTChecker: Add division by zero checks in the CHC engine. * SMTChecker: Support ``selector`` for expressions with value known at compile-time. + * SMTChecker: More precise analysis of external calls using ``this``. * Command Line Interface: New option ``--model-checker-timeout`` sets a timeout in milliseconds for each individual query performed by the SMTChecker. * Standard JSON: New option ``modelCheckerSettings.timeout`` sets a timeout in milliseconds for each individual query performed by the SMTChecker. * Assembler: Perform linking in assembly mode when library addresses are provided. diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index 6013f1cd1..b6e05bdd0 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -96,20 +96,7 @@ bool BMC::shouldInlineFunctionCall(FunctionCall const& _funCall) FunctionType const& funType = dynamic_cast(*_funCall.expression().annotation().type); if (funType.kind() == FunctionType::Kind::External) - { - auto memberAccess = dynamic_cast(&_funCall.expression()); - if (!memberAccess) - return false; - - auto identifier = dynamic_cast(&memberAccess->expression()); - if (!( - identifier && - identifier->name() == "this" && - identifier->annotation().referencedDeclaration && - dynamic_cast(identifier->annotation().referencedDeclaration) - )) - return false; - } + return isTrustedExternalCall(&_funCall.expression()); else if (funType.kind() != FunctionType::Kind::Internal) return false; diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index 6fc432d06..be3413770 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -562,8 +562,6 @@ void CHC::internalFunctionCall(FunctionCall const& _funCall) m_context.addAssertion(interface(*contract)); } - auto previousError = errorFlag().currentValue(); - m_context.addAssertion(predicate(_funCall)); connectBlocks( @@ -572,8 +570,6 @@ void CHC::internalFunctionCall(FunctionCall const& _funCall) (errorFlag().currentValue() > 0) ); m_context.addAssertion(errorFlag().currentValue() == 0); - errorFlag().increaseIndex(); - m_context.addAssertion(errorFlag().currentValue() == previousError); } void CHC::externalFunctionCall(FunctionCall const& _funCall) @@ -583,6 +579,11 @@ void CHC::externalFunctionCall(FunctionCall const& _funCall) /// so we just add the nondet_interface predicate. solAssert(m_currentContract, ""); + if (isTrustedExternalCall(&_funCall.expression())) + { + externalFunctionCallToTrustedCode(_funCall); + return; + } FunctionType const& funType = dynamic_cast(*_funCall.expression().annotation().type); auto kind = funType.kind(); @@ -615,6 +616,42 @@ void CHC::externalFunctionCall(FunctionCall const& _funCall) m_context.addAssertion(errorFlag().currentValue() == 0); } +void CHC::externalFunctionCallToTrustedCode(FunctionCall const& _funCall) +{ + solAssert(m_currentContract, ""); + FunctionType const& funType = dynamic_cast(*_funCall.expression().annotation().type); + auto kind = funType.kind(); + solAssert(kind == FunctionType::Kind::External || kind == FunctionType::Kind::BareStaticCall, ""); + + auto const* function = functionCallToDefinition(_funCall); + if (!function) + return; + + // External call creates a new transaction. + auto originalTx = state().tx(); + auto txOrigin = state().txMember("tx.origin"); + state().newTx(); + // set the transaction sender as this contract + m_context.addAssertion(state().txMember("msg.sender") == state().thisAddress()); + // set the origin to be the current transaction origin + m_context.addAssertion(state().txMember("tx.origin") == txOrigin); + + smtutil::Expression pred = predicate(_funCall); + + auto txConstraints = m_context.state().txConstraints(*function); + m_context.addAssertion(pred && txConstraints); + // restore the original transaction data + state().newTx(); + m_context.addAssertion(originalTx == state().tx()); + + connectBlocks( + m_currentBlock, + (m_currentFunction && !m_currentFunction->isConstructor()) ? summary(*m_currentFunction) : summary(*m_currentContract), + (errorFlag().currentValue() > 0) + ); + m_context.addAssertion(errorFlag().currentValue() == 0); +} + void CHC::unknownFunctionCall(FunctionCall const&) { /// Function calls are not handled at the moment, @@ -1011,27 +1048,34 @@ smtutil::Expression CHC::predicate(Predicate const& _block) smtutil::Expression CHC::predicate(FunctionCall const& _funCall) { - /// Used only for internal calls. + FunctionType const& funType = dynamic_cast(*_funCall.expression().annotation().type); + auto kind = funType.kind(); + solAssert(kind == FunctionType::Kind::Internal || kind == FunctionType::Kind::External || kind == FunctionType::Kind::BareStaticCall, ""); auto const* function = functionCallToDefinition(_funCall); if (!function) return smtutil::Expression(true); + auto contractAddressValue = [this](FunctionCall const& _f) { + FunctionType const& funType = dynamic_cast(*_f.expression().annotation().type); + if (funType.kind() == FunctionType::Kind::Internal) + return state().thisAddress(); + if (MemberAccess const* callBase = dynamic_cast(&_f.expression())) + return expr(callBase->expression()); + solAssert(false, "Unreachable!"); + }; errorFlag().increaseIndex(); - vector args{errorFlag().currentValue(), state().thisAddress(), state().crypto(), state().tx(), state().state()}; + vector args{errorFlag().currentValue(), contractAddressValue(_funCall), state().crypto(), state().tx(), state().state()}; - FunctionType const& funType = dynamic_cast(*_funCall.expression().annotation().type); - solAssert(funType.kind() == FunctionType::Kind::Internal, ""); - - /// Internal calls can be made to the contract itself or a library. auto const* contract = function->annotation().contract; auto const& hierarchy = m_currentContract->annotation().linearizedBaseContracts; - solAssert(contract->isLibrary() || find(hierarchy.begin(), hierarchy.end(), contract) != hierarchy.end(), ""); + solAssert(kind != FunctionType::Kind::Internal || contract->isLibrary() || contains(hierarchy, contract), ""); /// If the call is to a library, we use that library as the called contract. - /// If it is not, we use the current contract even if it is a call to a contract - /// up in the inheritance hierarchy, since the interfaces/predicates are different. - auto const* calledContract = contract->isLibrary() ? contract : m_currentContract; + /// If the call is to a contract not in the inheritance hierarchy, we also use that as the called contract. + /// Otherwise, the call is to some contract in the inheritance hierarchy of the current contract. + /// In this case we use current contract as the called one since the interfaces/predicates are different. + auto const* calledContract = contains(hierarchy, contract) ? m_currentContract : contract; solAssert(calledContract, ""); bool usesStaticCall = function->stateMutability() == StateMutability::Pure || function->stateMutability() == StateMutability::View; diff --git a/libsolidity/formal/CHC.h b/libsolidity/formal/CHC.h index 8a169e86d..9f712ba39 100644 --- a/libsolidity/formal/CHC.h +++ b/libsolidity/formal/CHC.h @@ -88,6 +88,7 @@ private: void visitAddMulMod(FunctionCall const& _funCall) override; void internalFunctionCall(FunctionCall const& _funCall); void externalFunctionCall(FunctionCall const& _funCall); + void externalFunctionCallToTrustedCode(FunctionCall const& _funCall); void unknownFunctionCall(FunctionCall const& _funCall); void makeArrayPopVerificationTarget(FunctionCall const& _arrayPop) override; /// Creates underflow/overflow verification targets. diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 7ee339b1e..d58abc2ee 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -2391,6 +2391,19 @@ MemberAccess const* SMTEncoder::isEmptyPush(Expression const& _expr) const return nullptr; } +bool SMTEncoder::isTrustedExternalCall(Expression const* _expr) { + auto memberAccess = dynamic_cast(_expr); + if (!memberAccess) + return false; + + auto identifier = dynamic_cast(&memberAccess->expression()); + return identifier && + identifier->name() == "this" && + identifier->annotation().referencedDeclaration && + dynamic_cast(identifier->annotation().referencedDeclaration) + ; +} + string SMTEncoder::extraComment() { string extra; diff --git a/libsolidity/formal/SMTEncoder.h b/libsolidity/formal/SMTEncoder.h index 49cac0082..d56eeca18 100644 --- a/libsolidity/formal/SMTEncoder.h +++ b/libsolidity/formal/SMTEncoder.h @@ -295,6 +295,10 @@ protected: /// otherwise nullptr. MemberAccess const* isEmptyPush(Expression const& _expr) const; + /// @returns true if the given identifier is a contract which is known and trusted. + /// This means we don't have to abstract away effects of external function calls to this contract. + static bool isTrustedExternalCall(Expression const* _expr); + /// Creates symbolic expressions for the returned values /// and set them as the components of the symbolic tuple. void createReturnedExpressions(FunctionCall const& _funCall); diff --git a/test/libsolidity/smtCheckerTests/functions/this_external_call.sol b/test/libsolidity/smtCheckerTests/functions/this_external_call.sol index 489d7f44e..de69baf53 100644 --- a/test/libsolidity/smtCheckerTests/functions/this_external_call.sol +++ b/test/libsolidity/smtCheckerTests/functions/this_external_call.sol @@ -9,9 +9,7 @@ contract C function g(uint y) public { require(y < 1000); this.f(y); - // Fails as false positive because CHC does not support `this`. assert(x < 1000); } } // ---- -// Warning 6328: (227-243): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/this_external_call_2.sol b/test/libsolidity/smtCheckerTests/functions/this_external_call_2.sol new file mode 100644 index 000000000..7fca557ad --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/this_external_call_2.sol @@ -0,0 +1,16 @@ +pragma experimental SMTChecker; + +contract C { + uint a; + function f(uint x) public { + this.g(x); + assert(a == x); + assert(a != 42); + } + + function g(uint x) public { + a = x; + } +} +// ---- +// Warning 6328: (141-156): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/this_external_call_return.sol b/test/libsolidity/smtCheckerTests/functions/this_external_call_return.sol index 0307490dc..cb94af867 100644 --- a/test/libsolidity/smtCheckerTests/functions/this_external_call_return.sol +++ b/test/libsolidity/smtCheckerTests/functions/this_external_call_return.sol @@ -10,9 +10,7 @@ contract C function g(uint y) public { require(y < 1000); uint z = this.f(y); - // Fails as false positive because CHC does not support `this`. assert(z < 1000); } } // ---- -// Warning 6328: (263-279): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/this_external_call_sender.sol b/test/libsolidity/smtCheckerTests/functions/this_external_call_sender.sol new file mode 100644 index 000000000..78f4169cc --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/this_external_call_sender.sol @@ -0,0 +1,28 @@ +pragma experimental SMTChecker; + +contract C { + address lastCaller; + + constructor() { + lastCaller = msg.sender; + } + + modifier log { + lastCaller = msg.sender; + _; + } + + function test() log public { + assert(lastCaller == msg.sender); + this.g(); + assert(lastCaller == address(this)); + assert(lastCaller == msg.sender); + assert(lastCaller == address(0)); + } + + function g() log public { + } +} +// ---- +// Warning 6328: (347-379): CHC: Assertion violation happens here. +// Warning 6328: (389-421): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/this_external_call_tx_origin.sol b/test/libsolidity/smtCheckerTests/functions/this_external_call_tx_origin.sol new file mode 100644 index 000000000..04dd5599d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/this_external_call_tx_origin.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; + +contract C { + + function test() view public { + require(address(this) != tx.origin); + assert(!this.g()); + } + + function g() view public returns (bool) { + return msg.sender == tx.origin; + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/functions/this_external_getter_1.sol b/test/libsolidity/smtCheckerTests/functions/this_external_getter_1.sol new file mode 100644 index 000000000..60c1a57d5 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/this_external_getter_1.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; + +contract C { + uint public x; + + function f() public view { + uint y = this.x(); + assert(y == x); // This fails as false positive because of lack of support for external getters. + } +} +// ---- +// Warning 6328: (114-128): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/this_external_getter_2.sol b/test/libsolidity/smtCheckerTests/functions/this_external_getter_2.sol new file mode 100644 index 000000000..5091da3f3 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/this_external_getter_2.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; + +contract C { + mapping (uint => uint) public map; + + function f() public view { + uint y = this.map(2); + assert(y == map[2]); // This fails as false positive because of lack of support for external getters. + } +} +// ---- +// Warning 6328: (137-156): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/this_external_getter_3.sol b/test/libsolidity/smtCheckerTests/functions/this_external_getter_3.sol new file mode 100644 index 000000000..aaa9605cc --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/this_external_getter_3.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; + +contract C { + mapping (uint => mapping (uint => uint)) public map; + + function f() public view { + uint y = this.map(2, 3); + assert(y == map[2][3]); // This fails as false positive because of lack of support for external getters. + } +} +// ---- +// Warning 6328: (158-180): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/this_state.sol b/test/libsolidity/smtCheckerTests/functions/this_state.sol index 7cef58b3e..9f417380e 100644 --- a/test/libsolidity/smtCheckerTests/functions/this_state.sol +++ b/test/libsolidity/smtCheckerTests/functions/this_state.sol @@ -6,7 +6,6 @@ contract C function g() public { x = 0; this.h(); - // Fails as false positive because CHC does not support `this`. assert(x == 2); } function h() public { @@ -14,4 +13,3 @@ contract C } } // ---- -// Warning 6328: (186-200): CHC: Assertion violation happens here. From bdf05bf8a0d980af299dc508cbe721b38a02a503 Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Fri, 6 Nov 2020 14:29:43 +0100 Subject: [PATCH 07/29] Moving try catch test to semantic tests. --- test/libsolidity/SolidityEndToEndTest.cpp | 53 ------------------- .../tryCatch/try_catch_library_call.sol | 45 ++++++++++++++++ 2 files changed, 45 insertions(+), 53 deletions(-) create mode 100644 test/libsolidity/semanticTests/tryCatch/try_catch_library_call.sol diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index c497c46b2..3072d2038 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -6690,59 +6690,6 @@ BOOST_AUTO_TEST_CASE(dirty_scratch_space_prior_to_constant_optimiser) ); } -BOOST_AUTO_TEST_CASE(try_catch_library_call) -{ - char const* sourceCode = R"( - library L { - struct S { uint x; } - function integer(uint t, bool b) public view returns (uint) { - if (b) { - return t; - } else { - revert("failure"); - } - } - function stru(S storage t, bool b) public view returns (uint) { - if (b) { - return t.x; - } else { - revert("failure"); - } - } - } - contract C { - using L for L.S; - L.S t; - function f(bool b) public returns (uint, string memory) { - uint x = 8; - try L.integer(x, b) returns (uint _x) { - return (_x, ""); - } catch Error(string memory message) { - return (18, message); - } - } - function g(bool b) public returns (uint, string memory) { - t.x = 9; - try t.stru(b) returns (uint x) { - return (x, ""); - } catch Error(string memory message) { - return (19, message); - } - } - } - )"; - if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) - { - compileAndRun(sourceCode, 0, "L", bytes()); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"L", m_contractAddress}}); - - ABI_CHECK(callContractFunction("f(bool)", true), encodeArgs(8, 0x40, 0)); - ABI_CHECK(callContractFunction("f(bool)", false), encodeArgs(18, 0x40, 7, "failure")); - ABI_CHECK(callContractFunction("g(bool)", true), encodeArgs(9, 0x40, 0)); - ABI_CHECK(callContractFunction("g(bool)", false), encodeArgs(19, 0x40, 7, "failure")); - } -} - BOOST_AUTO_TEST_CASE(strip_reason_strings) { char const* sourceCode = R"( diff --git a/test/libsolidity/semanticTests/tryCatch/try_catch_library_call.sol b/test/libsolidity/semanticTests/tryCatch/try_catch_library_call.sol new file mode 100644 index 000000000..e7bad76b0 --- /dev/null +++ b/test/libsolidity/semanticTests/tryCatch/try_catch_library_call.sol @@ -0,0 +1,45 @@ +library L { + struct S { uint x; } + function integer(uint t, bool b) public view returns (uint) { + if (b) { + return t; + } else { + revert("failure"); + } + } + function stru(S storage t, bool b) public view returns (uint) { + if (b) { + return t.x; + } else { + revert("failure"); + } + } +} +contract C { + using L for L.S; + L.S t; + function f(bool b) public returns (uint, string memory) { + uint x = 8; + try L.integer(x, b) returns (uint _x) { + return (_x, ""); + } catch Error(string memory message) { + return (18, message); + } + } + function g(bool b) public returns (uint, string memory) { + t.x = 9; + try t.stru(b) returns (uint x) { + return (x, ""); + } catch Error(string memory message) { + return (19, message); + } + } +} +// ==== +// EVMVersion: >=byzantium +// ---- +// library: L +// f(bool): true -> 8, 0x40, 0 +// f(bool): false -> 18, 0x40, 7, "failure" +// g(bool): true -> 9, 0x40, 0 +// g(bool): false -> 19, 0x40, 7, "failure" From 11033c95361216c0e8ce4bb153465e18c3c55ecc Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Thu, 12 Nov 2020 15:29:08 +0100 Subject: [PATCH 08/29] Moving some bytes and array tests to semanticTests --- test/libsolidity/SolidityEndToEndTest.cpp | 398 ------------------ .../array/array_pop_array_transition.sol | 26 ++ .../array/array_pop_storage_empty.sol | 12 + .../array/array_pop_uint16_transition.sol | 23 + .../array/array_pop_uint24_transition.sol | 23 + .../array_copy_storage_storage_dyn_dyn.sol | 22 + .../array_copy_storage_storage_struct.sol | 20 + .../copying/array_copy_target_leftover.sol | 19 + .../array/copying/bytes_inside_mappings.sol | 15 + .../array/copying/copy_removes_bytes_data.sol | 12 + .../array/delete_removes_bytes_data.sol | 12 + .../array/dynamic_array_cleanup.sol | 23 + .../array/dynamic_multi_array_cleanup.sol | 23 + .../array/fixed_array_cleanup.sol | 17 + .../array/short_fixed_array_cleanup.sol | 17 + ...truct_containing_bytes_copy_and_delete.sol | 33 ++ 16 files changed, 297 insertions(+), 398 deletions(-) create mode 100644 test/libsolidity/semanticTests/array/array_pop_array_transition.sol create mode 100644 test/libsolidity/semanticTests/array/array_pop_storage_empty.sol create mode 100644 test/libsolidity/semanticTests/array/array_pop_uint16_transition.sol create mode 100644 test/libsolidity/semanticTests/array/array_pop_uint24_transition.sol create mode 100644 test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dyn_dyn.sol create mode 100644 test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_struct.sol create mode 100644 test/libsolidity/semanticTests/array/copying/array_copy_target_leftover.sol create mode 100644 test/libsolidity/semanticTests/array/copying/bytes_inside_mappings.sol create mode 100644 test/libsolidity/semanticTests/array/copying/copy_removes_bytes_data.sol create mode 100644 test/libsolidity/semanticTests/array/delete_removes_bytes_data.sol create mode 100644 test/libsolidity/semanticTests/array/dynamic_array_cleanup.sol create mode 100644 test/libsolidity/semanticTests/array/dynamic_multi_array_cleanup.sol create mode 100644 test/libsolidity/semanticTests/array/fixed_array_cleanup.sol create mode 100644 test/libsolidity/semanticTests/array/short_fixed_array_cleanup.sol create mode 100644 test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 3072d2038..f8472d191 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2593,25 +2593,6 @@ BOOST_AUTO_TEST_CASE(copying_bytes_multiassign) ABI_CHECK(callContractFunction("val()"), encodeArgs(0x80)); } -BOOST_AUTO_TEST_CASE(delete_removes_bytes_data) -{ - char const* sourceCode = R"( - contract c { - fallback() external { data = msg.data; } - function del() public returns (bool) { delete data; return true; } - bytes data; - } - )"; - ALSO_VIA_YUL( - DISABLE_EWASM_TESTRUN() - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("---", 7), bytes()); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("del()", 7), encodeArgs(true)); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ); -} - BOOST_AUTO_TEST_CASE(copy_from_calldata_removes_bytes_data) { char const* sourceCode = R"( @@ -2633,83 +2614,6 @@ BOOST_AUTO_TEST_CASE(copy_from_calldata_removes_bytes_data) ); } -BOOST_AUTO_TEST_CASE(copy_removes_bytes_data) -{ - char const* sourceCode = R"( - contract c { - function set() public returns (bool) { data1 = msg.data; return true; } - function reset() public returns (bool) { data1 = data2; return true; } - bytes data1; - bytes data2; - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("set()", 1, 2, 3, 4, 5), encodeArgs(true)); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("reset()"), encodeArgs(true)); - BOOST_CHECK(storageEmpty(m_contractAddress)); -} - -BOOST_AUTO_TEST_CASE(bytes_inside_mappings) -{ - char const* sourceCode = R"( - contract c { - function set(uint key) public returns (bool) { data[key] = msg.data; return true; } - function copy(uint from, uint to) public returns (bool) { data[to] = data[from]; return true; } - mapping(uint => bytes) data; - } - )"; - compileAndRun(sourceCode); - // store a short byte array at 1 and a longer one at 2 - ABI_CHECK(callContractFunction("set(uint256)", 1, 2), encodeArgs(true)); - ABI_CHECK(callContractFunction("set(uint256)", 2, 2, 3, 4, 5), encodeArgs(true)); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - // copy shorter to longer - ABI_CHECK(callContractFunction("copy(uint256,uint256)", 1, 2), encodeArgs(true)); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - // copy empty to both - ABI_CHECK(callContractFunction("copy(uint256,uint256)", 99, 1), encodeArgs(true)); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("copy(uint256,uint256)", 99, 2), encodeArgs(true)); - BOOST_CHECK(storageEmpty(m_contractAddress)); -} - -BOOST_AUTO_TEST_CASE(struct_containing_bytes_copy_and_delete) -{ - char const* sourceCode = R"( - contract c { - struct Struct { uint a; bytes data; uint b; } - Struct data1; - Struct data2; - function set(uint _a, bytes calldata _data, uint _b) external returns (bool) { - data1.a = _a; - data1.b = _b; - data1.data = _data; - return true; - } - function copy() public returns (bool) { - data1 = data2; - return true; - } - function del() public returns (bool) { - delete data1; - return true; - } - } - )"; - compileAndRun(sourceCode); - string data = "123456789012345678901234567890123"; - BOOST_CHECK(storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("set(uint256,bytes,uint256)", 12, 0x60, 13, u256(data.length()), data), encodeArgs(true)); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("copy()"), encodeArgs(true)); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("set(uint256,bytes,uint256)", 12, 0x60, 13, u256(data.length()), data), encodeArgs(true)); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("del()"), encodeArgs(true)); - BOOST_CHECK(storageEmpty(m_contractAddress)); -} - BOOST_AUTO_TEST_CASE(storing_invalid_boolean) { char const* sourceCode = R"( @@ -2894,197 +2798,6 @@ BOOST_AUTO_TEST_CASE(bytes_in_arguments) ); } -BOOST_AUTO_TEST_CASE(fixed_array_cleanup) -{ - char const* sourceCode = R"( - contract c { - uint spacer1; - uint spacer2; - uint[20] data; - function fill() public { - for (uint i = 0; i < data.length; ++i) data[i] = i+1; - } - function clear() public { delete data; } - } - )"; - ALSO_VIA_YUL( - DISABLE_EWASM_TESTRUN() - - compileAndRun(sourceCode); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("fill()"), bytes()); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("clear()"), bytes()); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ); -} - -BOOST_AUTO_TEST_CASE(short_fixed_array_cleanup) -{ - char const* sourceCode = R"( - contract c { - uint spacer1; - uint spacer2; - uint[3] data; - function fill() public { - for (uint i = 0; i < data.length; ++i) data[i] = i+1; - } - function clear() public { delete data; } - } - )"; - ALSO_VIA_YUL( - DISABLE_EWASM_TESTRUN() - - compileAndRun(sourceCode); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("fill()"), bytes()); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("clear()"), bytes()); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ); -} - -BOOST_AUTO_TEST_CASE(dynamic_array_cleanup) -{ - char const* sourceCode = R"( - contract c { - uint[20] spacer; - uint[] dynamic; - function fill() public { - for (uint i = 0; i < 21; ++i) - dynamic.push(i + 1); - } - function halfClear() public { - while (dynamic.length > 5) - dynamic.pop(); - } - function fullClear() public { delete dynamic; } - } - )"; - ALSO_VIA_YUL( - DISABLE_EWASM_TESTRUN() - - compileAndRun(sourceCode); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("fill()"), bytes()); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("halfClear()"), bytes()); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("fullClear()"), bytes()); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ); -} - -BOOST_AUTO_TEST_CASE(dynamic_multi_array_cleanup) -{ - char const* sourceCode = R"( - contract c { - struct s { uint[][] d; } - s[] data; - function fill() public returns (uint) { - while (data.length < 3) - data.push(); - while (data[2].d.length < 4) - data[2].d.push(); - while (data[2].d[3].length < 5) - data[2].d[3].push(); - data[2].d[3][4] = 8; - return data[2].d[3][4]; - } - function clear() public { delete data; } - } - )"; - ALSO_VIA_YUL( - DISABLE_EWASM_TESTRUN() - - compileAndRun(sourceCode); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("fill()"), encodeArgs(8)); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("clear()"), bytes()); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ); -} - -BOOST_AUTO_TEST_CASE(array_copy_storage_storage_dyn_dyn) -{ - char const* sourceCode = R"( - contract c { - uint[] data1; - uint[] data2; - function setData1(uint length, uint index, uint value) public { - data1 = new uint[](length); - if (index < length) - data1[index] = value; - } - function copyStorageStorage() public { data2 = data1; } - function getData2(uint index) public returns (uint len, uint val) { - len = data2.length; if (index < len) val = data2[index]; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("setData1(uint256,uint256,uint256)", 10, 5, 4), bytes()); - ABI_CHECK(callContractFunction("copyStorageStorage()"), bytes()); - ABI_CHECK(callContractFunction("getData2(uint256)", 5), encodeArgs(10, 4)); - ABI_CHECK(callContractFunction("setData1(uint256,uint256,uint256)", 0, 0, 0), bytes()); - ABI_CHECK(callContractFunction("copyStorageStorage()"), bytes()); - ABI_CHECK(callContractFunction("getData2(uint256)", 0), encodeArgs(0, 0)); - BOOST_CHECK(storageEmpty(m_contractAddress)); -} - -BOOST_AUTO_TEST_CASE(array_copy_target_leftover) -{ - // test that leftover elements in the last slot of target are correctly cleared during assignment - char const* sourceCode = R"( - contract c { - byte[10] data1; - bytes2[32] data2; - function test() public returns (uint check, uint res1, uint res2) { - uint i; - for (i = 0; i < data2.length; ++i) - data2[i] = 0xffff; - check = uint(uint16(data2[31])) * 0x10000 | uint(uint16(data2[14])); - for (i = 0; i < data1.length; ++i) - data1[i] = byte(uint8(1 + i)); - data2 = data1; - for (i = 0; i < 16; ++i) - res1 |= uint(uint16(data2[i])) * 0x10000**i; - for (i = 0; i < 16; ++i) - res2 |= uint(uint16(data2[16 + i])) * 0x10000**i; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(u256("0xffffffff"), asString(fromHex("0000000000000000000000000a00090008000700060005000400030002000100")), asString(fromHex("0000000000000000000000000000000000000000000000000000000000000000")))); -} - -BOOST_AUTO_TEST_CASE(array_copy_storage_storage_struct) -{ - char const* sourceCode = R"( - contract c { - struct Data { uint x; uint y; } - Data[] data1; - Data[] data2; - function test() public returns (uint x, uint y) { - while (data1.length < 9) - data1.push(); - data1[8].x = 4; - data1[8].y = 5; - data2 = data1; - x = data2[8].x; - y = data2[8].y; - while (data1.length > 0) - data1.pop(); - data2 = data1; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(4, 5)); - BOOST_CHECK(storageEmpty(m_contractAddress)); -} - BOOST_AUTO_TEST_CASE(array_copy_storage_abi) { // NOTE: This does not really test copying from storage to ABI directly, @@ -3141,117 +2854,6 @@ BOOST_AUTO_TEST_CASE(array_copy_storage_abi) ); } -BOOST_AUTO_TEST_CASE(array_pop_uint16_transition) -{ - char const* sourceCode = R"( - contract c { - uint16[] data; - function test() public returns (uint16 x, uint16 y, uint16 z) { - for (uint i = 1; i <= 48; i++) - data.push(uint16(i)); - for (uint j = 1; j <= 10; j++) - data.pop(); - x = data[data.length - 1]; - for (uint k = 1; k <= 10; k++) - data.pop(); - y = data[data.length - 1]; - for (uint l = 1; l <= 10; l++) - data.pop(); - z = data[data.length - 1]; - for (uint m = 1; m <= 18; m++) - data.pop(); - } - } - )"; - ALSO_VIA_YUL( - DISABLE_EWASM_TESTRUN() - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(38, 28, 18)); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ); -} - -BOOST_AUTO_TEST_CASE(array_pop_uint24_transition) -{ - char const* sourceCode = R"( - contract c { - uint256 a; - uint256 b; - uint256 c; - uint24[] data; - function test() public returns (uint24 x, uint24 y) { - for (uint i = 1; i <= 30; i++) - data.push(uint24(i)); - for (uint j = 1; j <= 10; j++) - data.pop(); - x = data[data.length - 1]; - for (uint k = 1; k <= 10; k++) - data.pop(); - y = data[data.length - 1]; - for (uint l = 1; l <= 10; l++) - data.pop(); - } - } - )"; - ALSO_VIA_YUL( - DISABLE_EWASM_TESTRUN() - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(20, 10)); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ); -} - -BOOST_AUTO_TEST_CASE(array_pop_array_transition) -{ - char const* sourceCode = R"( - contract c { - uint256 a; - uint256 b; - uint256 c; - uint16[] inner = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; - uint16[][] data; - function test() public returns (uint x, uint y, uint z) { - for (uint i = 1; i <= 48; i++) - data.push(inner); - for (uint j = 1; j <= 10; j++) - data.pop(); - x = data[data.length - 1][0]; - for (uint k = 1; k <= 10; k++) - data.pop(); - y = data[data.length - 1][1]; - for (uint l = 1; l <= 10; l++) - data.pop(); - z = data[data.length - 1][2]; - for (uint m = 1; m <= 18; m++) - data.pop(); - delete inner; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(1, 2, 3)); - BOOST_CHECK(storageEmpty(m_contractAddress)); -} - -BOOST_AUTO_TEST_CASE(array_pop_storage_empty) -{ - char const* sourceCode = R"( - contract c { - uint[] data; - function test() public { - data.push(7); - data.pop(); - } - } - )"; - ALSO_VIA_YUL( - DISABLE_EWASM_TESTRUN() - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs()); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ); -} - BOOST_AUTO_TEST_CASE(byte_array_pop_storage_empty) { char const* sourceCode = R"( diff --git a/test/libsolidity/semanticTests/array/array_pop_array_transition.sol b/test/libsolidity/semanticTests/array/array_pop_array_transition.sol new file mode 100644 index 000000000..d826dea30 --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_pop_array_transition.sol @@ -0,0 +1,26 @@ +contract c { + uint256 a; + uint256 b; + uint256 c; + uint16[] inner = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + uint16[][] data; + function test() public returns (uint x, uint y, uint z) { + for (uint i = 1; i <= 48; i++) + data.push(inner); + for (uint j = 1; j <= 10; j++) + data.pop(); + x = data[data.length - 1][0]; + for (uint k = 1; k <= 10; k++) + data.pop(); + y = data[data.length - 1][1]; + for (uint l = 1; l <= 10; l++) + data.pop(); + z = data[data.length - 1][2]; + for (uint m = 1; m <= 18; m++) + data.pop(); + delete inner; + } +} +// ---- +// test() -> 1, 2, 3 +// storage: empty diff --git a/test/libsolidity/semanticTests/array/array_pop_storage_empty.sol b/test/libsolidity/semanticTests/array/array_pop_storage_empty.sol new file mode 100644 index 000000000..24f210177 --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_pop_storage_empty.sol @@ -0,0 +1,12 @@ +contract c { + uint[] data; + function test() public { + data.push(7); + data.pop(); + } +} +// ==== +// compileViaYul: also +// ---- +// test() -> +// storage: empty diff --git a/test/libsolidity/semanticTests/array/array_pop_uint16_transition.sol b/test/libsolidity/semanticTests/array/array_pop_uint16_transition.sol new file mode 100644 index 000000000..8cecac615 --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_pop_uint16_transition.sol @@ -0,0 +1,23 @@ +contract c { + uint16[] data; + function test() public returns (uint16 x, uint16 y, uint16 z) { + for (uint i = 1; i <= 48; i++) + data.push(uint16(i)); + for (uint j = 1; j <= 10; j++) + data.pop(); + x = data[data.length - 1]; + for (uint k = 1; k <= 10; k++) + data.pop(); + y = data[data.length - 1]; + for (uint l = 1; l <= 10; l++) + data.pop(); + z = data[data.length - 1]; + for (uint m = 1; m <= 18; m++) + data.pop(); + } +} +// ==== +// compileViaYul: also +// ---- +// test() -> 38, 28, 18 +// storage: empty diff --git a/test/libsolidity/semanticTests/array/array_pop_uint24_transition.sol b/test/libsolidity/semanticTests/array/array_pop_uint24_transition.sol new file mode 100644 index 000000000..467774cc2 --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_pop_uint24_transition.sol @@ -0,0 +1,23 @@ +contract c { + uint256 a; + uint256 b; + uint256 c; + uint24[] data; + function test() public returns (uint24 x, uint24 y) { + for (uint i = 1; i <= 30; i++) + data.push(uint24(i)); + for (uint j = 1; j <= 10; j++) + data.pop(); + x = data[data.length - 1]; + for (uint k = 1; k <= 10; k++) + data.pop(); + y = data[data.length - 1]; + for (uint l = 1; l <= 10; l++) + data.pop(); + } +} +// ==== +// compileViaYul: also +// ---- +// test() -> 20, 10 +// storage: empty diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dyn_dyn.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dyn_dyn.sol new file mode 100644 index 000000000..c183d8a82 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dyn_dyn.sol @@ -0,0 +1,22 @@ + +contract c { + uint[] data1; + uint[] data2; + function setData1(uint length, uint index, uint value) public { + data1 = new uint[](length); + if (index < length) + data1[index] = value; + } + function copyStorageStorage() public { data2 = data1; } + function getData2(uint index) public returns (uint len, uint val) { + len = data2.length; if (index < len) val = data2[index]; + } +} +// ---- +// setData1(uint256,uint256,uint256): 10, 5, 4 -> +// copyStorageStorage() -> +// getData2(uint256): 5 -> 10, 4 +// setData1(uint256,uint256,uint256): 0, 0, 0 -> +// copyStorageStorage() -> +// getData2(uint256): 0 -> 0, 0 +// storage: empty diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_struct.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_struct.sol new file mode 100644 index 000000000..5c53eaa77 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_struct.sol @@ -0,0 +1,20 @@ +contract c { + struct Data { uint x; uint y; } + Data[] data1; + Data[] data2; + function test() public returns (uint x, uint y) { + while (data1.length < 9) + data1.push(); + data1[8].x = 4; + data1[8].y = 5; + data2 = data1; + x = data2[8].x; + y = data2[8].y; + while (data1.length > 0) + data1.pop(); + data2 = data1; + } +} +// ---- +// test() -> 4, 5 +// storage: empty diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_target_leftover.sol b/test/libsolidity/semanticTests/array/copying/array_copy_target_leftover.sol new file mode 100644 index 000000000..88d741654 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_copy_target_leftover.sol @@ -0,0 +1,19 @@ +contract c { + byte[10] data1; + bytes2[32] data2; + function test() public returns (uint check, uint res1, uint res2) { + uint i; + for (i = 0; i < data2.length; ++i) + data2[i] = 0xffff; + check = uint(uint16(data2[31])) * 0x10000 | uint(uint16(data2[14])); + for (i = 0; i < data1.length; ++i) + data1[i] = byte(uint8(1 + i)); + data2 = data1; + for (i = 0; i < 16; ++i) + res1 |= uint(uint16(data2[i])) * 0x10000**i; + for (i = 0; i < 16; ++i) + res2 |= uint(uint16(data2[16 + i])) * 0x10000**i; + } +} +// ---- +// test() -> 0xffffffff, 0x0000000000000000000000000a00090008000700060005000400030002000100, 0x0000000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/array/copying/bytes_inside_mappings.sol b/test/libsolidity/semanticTests/array/copying/bytes_inside_mappings.sol new file mode 100644 index 000000000..21b5e9c5d --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/bytes_inside_mappings.sol @@ -0,0 +1,15 @@ +contract c { + function set(uint key) public returns (bool) { data[key] = msg.data; return true; } + function copy(uint from, uint to) public returns (bool) { data[to] = data[from]; return true; } + mapping(uint => bytes) data; +} +// ---- +// set(uint256): 1, 2 -> true +// set(uint256): 2, 2, 3, 4, 5 -> true +// storage: nonempty +// copy(uint256,uint256): 1, 2 -> true +// storage: nonempty +// copy(uint256,uint256): 99, 1 -> true +// storage: nonempty +// copy(uint256,uint256): 99, 2 -> true +// storage: empty diff --git a/test/libsolidity/semanticTests/array/copying/copy_removes_bytes_data.sol b/test/libsolidity/semanticTests/array/copying/copy_removes_bytes_data.sol new file mode 100644 index 000000000..bb97ae2c1 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/copy_removes_bytes_data.sol @@ -0,0 +1,12 @@ + +contract c { + function set() public returns (bool) { data1 = msg.data; return true; } + function reset() public returns (bool) { data1 = data2; return true; } + bytes data1; + bytes data2; +} +// ---- +// set(): 1, 2, 3, 4, 5 -> true +// storage: nonempty +// reset() -> true +// storage: empty diff --git a/test/libsolidity/semanticTests/array/delete_removes_bytes_data.sol b/test/libsolidity/semanticTests/array/delete_removes_bytes_data.sol new file mode 100644 index 000000000..28d6e5d8f --- /dev/null +++ b/test/libsolidity/semanticTests/array/delete_removes_bytes_data.sol @@ -0,0 +1,12 @@ +contract c { + fallback() external { data = msg.data; } + function del() public returns (bool) { delete data; return true; } + bytes data; +} +// ==== +// compileViaYul: also +// ---- +// (): 7 -> +// storage: nonempty +// del(): 7 -> true +// storage: empty diff --git a/test/libsolidity/semanticTests/array/dynamic_array_cleanup.sol b/test/libsolidity/semanticTests/array/dynamic_array_cleanup.sol new file mode 100644 index 000000000..07c3f63aa --- /dev/null +++ b/test/libsolidity/semanticTests/array/dynamic_array_cleanup.sol @@ -0,0 +1,23 @@ +contract c { + uint[20] spacer; + uint[] dynamic; + function fill() public { + for (uint i = 0; i < 21; ++i) + dynamic.push(i + 1); + } + function halfClear() public { + while (dynamic.length > 5) + dynamic.pop(); + } + function fullClear() public { delete dynamic; } +} +// ==== +// compileViaYul: also +// ---- +// storage: empty +// fill() -> +// storage: nonempty +// halfClear() -> +// storage: nonempty +// fullClear() -> +// storage: empty diff --git a/test/libsolidity/semanticTests/array/dynamic_multi_array_cleanup.sol b/test/libsolidity/semanticTests/array/dynamic_multi_array_cleanup.sol new file mode 100644 index 000000000..c58abad52 --- /dev/null +++ b/test/libsolidity/semanticTests/array/dynamic_multi_array_cleanup.sol @@ -0,0 +1,23 @@ +contract c { + struct s { uint[][] d; } + s[] data; + function fill() public returns (uint) { + while (data.length < 3) + data.push(); + while (data[2].d.length < 4) + data[2].d.push(); + while (data[2].d[3].length < 5) + data[2].d[3].push(); + data[2].d[3][4] = 8; + return data[2].d[3][4]; + } + function clear() public { delete data; } +} +// ==== +// compileViaYul: also +// ---- +// storage: empty +// fill() -> 8 +// storage: nonempty +// clear() -> +// storage: empty diff --git a/test/libsolidity/semanticTests/array/fixed_array_cleanup.sol b/test/libsolidity/semanticTests/array/fixed_array_cleanup.sol new file mode 100644 index 000000000..9a6f35452 --- /dev/null +++ b/test/libsolidity/semanticTests/array/fixed_array_cleanup.sol @@ -0,0 +1,17 @@ +contract c { + uint spacer1; + uint spacer2; + uint[20] data; + function fill() public { + for (uint i = 0; i < data.length; ++i) data[i] = i+1; + } + function clear() public { delete data; } +} +// ==== +// compileViaYul: also +// ---- +// storage: empty +// fill() -> +// storage: nonempty +// clear() -> +// storage: empty diff --git a/test/libsolidity/semanticTests/array/short_fixed_array_cleanup.sol b/test/libsolidity/semanticTests/array/short_fixed_array_cleanup.sol new file mode 100644 index 000000000..8909ed48e --- /dev/null +++ b/test/libsolidity/semanticTests/array/short_fixed_array_cleanup.sol @@ -0,0 +1,17 @@ +contract c { + uint spacer1; + uint spacer2; + uint[3] data; + function fill() public { + for (uint i = 0; i < data.length; ++i) data[i] = i+1; + } + function clear() public { delete data; } +} +// ==== +// compileViaYul: also +// ---- +// storage: empty +// fill() -> +// storage: nonempty +// clear() -> +// storage: empty diff --git a/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol b/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol new file mode 100644 index 000000000..c6e25f9d5 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol @@ -0,0 +1,33 @@ +contract c { + struct Struct { uint a; bytes data; uint b; } + Struct data1; + Struct data2; + function set(uint _a, bytes calldata _data, uint _b) external returns (bool) { + data1.a = _a; + data1.b = _b; + data1.data = _data; + return true; + } + function copy() public returns (bool) { + data1 = data2; + return true; + } + function del() public returns (bool) { + delete data1; + return true; + } + function test(uint256 i) public returns (byte) { + return data1.data[i]; + } +} +// ---- +// storage: empty +// set(uint256,bytes,uint256): 12, 0x60, 13, 33, "12345678901234567890123456789012", "3" -> true +// test(uint256): 32 -> "3" +// storage: nonempty +// copy() -> true +// storage: empty +// set(uint256,bytes,uint256): 12, 0x60, 13, 33, "12345678901234567890123456789012", "3" -> true +// storage: nonempty +// del() -> true +// storage: empty From 31981bad120c2f8dc4c7c1c78665c3f7b19f747e Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Fri, 13 Nov 2020 09:38:11 +0100 Subject: [PATCH 09/29] Organizing array tests into more directories. --- .../array/{ => copying}/array_copy_cleanup_uint128.sol | 0 .../array/{ => copying}/array_copy_cleanup_uint40.sol | 0 .../semanticTests/array/{ => delete}/bytes_delete_element.sol | 0 .../semanticTests/array/{ => delete}/delete_bytes_array.sol | 0 .../semanticTests/array/{ => delete}/delete_memory_array.sol | 0 .../array/{ => delete}/delete_on_array_of_structs.sol | 0 .../array/{ => delete}/delete_removes_bytes_data.sol | 0 .../semanticTests/array/{ => delete}/delete_storage_array.sol | 0 .../array/{ => delete}/delete_storage_array_packed.sol | 0 .../{ => indexAccess}/arrays_complex_memory_index_access.sol | 0 .../array/{ => indexAccess}/bytes_memory_index_access.sol | 0 .../array/{ => indexAccess}/fixed_bytes_index_access.sol | 0 .../semanticTests/array/{ => indexAccess}/index_access.sol | 0 .../array/{ => indexAccess}/inline_array_index_access_ints.sol | 0 .../array/{ => indexAccess}/inline_array_index_access_strings.sol | 0 .../memory_arrays_dynamic_index_access_write.sol | 0 .../array/{ => indexAccess}/memory_arrays_index_access_write.sol | 0 test/libsolidity/semanticTests/array/{ => pop}/array_pop.sol | 0 .../semanticTests/array/{ => pop}/array_pop_array_transition.sol | 0 .../semanticTests/array/{ => pop}/array_pop_empty_exception.sol | 0 .../semanticTests/array/{ => pop}/array_pop_isolated.sol | 0 .../semanticTests/array/{ => pop}/array_pop_storage_empty.sol | 0 .../semanticTests/array/{ => pop}/array_pop_uint16_transition.sol | 0 .../semanticTests/array/{ => pop}/array_pop_uint24_transition.sol | 0 test/libsolidity/semanticTests/array/{ => pop}/byte_array_pop.sol | 0 .../semanticTests/array/{ => pop}/byte_array_pop_copy_long.sol | 0 .../array/{ => pop}/byte_array_pop_empty_exception.sol | 0 .../semanticTests/array/{ => pop}/byte_array_pop_isolated.sol | 0 .../semanticTests/array/{ => pop}/byte_array_pop_masking_long.sol | 0 test/libsolidity/semanticTests/array/{ => push}/array_push.sol | 0 .../semanticTests/array/{ => push}/array_push_nested.sol | 0 .../semanticTests/array/{ => push}/array_push_packed_array.sol | 0 .../semanticTests/array/{ => push}/array_push_struct.sol | 0 .../semanticTests/array/{ => push}/byte_array_push.sol | 0 .../semanticTests/array/{ => push}/byte_array_push_transition.sol | 0 .../semanticTests/array/{ => push}/push_no_args_1d.sol | 0 .../semanticTests/array/{ => push}/push_no_args_2d.sol | 0 .../semanticTests/array/{ => push}/push_no_args_bytes.sol | 0 .../semanticTests/array/{ => push}/push_no_args_struct.sol | 0 39 files changed, 0 insertions(+), 0 deletions(-) rename test/libsolidity/semanticTests/array/{ => copying}/array_copy_cleanup_uint128.sol (100%) rename test/libsolidity/semanticTests/array/{ => copying}/array_copy_cleanup_uint40.sol (100%) rename test/libsolidity/semanticTests/array/{ => delete}/bytes_delete_element.sol (100%) rename test/libsolidity/semanticTests/array/{ => delete}/delete_bytes_array.sol (100%) rename test/libsolidity/semanticTests/array/{ => delete}/delete_memory_array.sol (100%) rename test/libsolidity/semanticTests/array/{ => delete}/delete_on_array_of_structs.sol (100%) rename test/libsolidity/semanticTests/array/{ => delete}/delete_removes_bytes_data.sol (100%) rename test/libsolidity/semanticTests/array/{ => delete}/delete_storage_array.sol (100%) rename test/libsolidity/semanticTests/array/{ => delete}/delete_storage_array_packed.sol (100%) rename test/libsolidity/semanticTests/array/{ => indexAccess}/arrays_complex_memory_index_access.sol (100%) rename test/libsolidity/semanticTests/array/{ => indexAccess}/bytes_memory_index_access.sol (100%) rename test/libsolidity/semanticTests/array/{ => indexAccess}/fixed_bytes_index_access.sol (100%) rename test/libsolidity/semanticTests/array/{ => indexAccess}/index_access.sol (100%) rename test/libsolidity/semanticTests/array/{ => indexAccess}/inline_array_index_access_ints.sol (100%) rename test/libsolidity/semanticTests/array/{ => indexAccess}/inline_array_index_access_strings.sol (100%) rename test/libsolidity/semanticTests/array/{ => indexAccess}/memory_arrays_dynamic_index_access_write.sol (100%) rename test/libsolidity/semanticTests/array/{ => indexAccess}/memory_arrays_index_access_write.sol (100%) rename test/libsolidity/semanticTests/array/{ => pop}/array_pop.sol (100%) rename test/libsolidity/semanticTests/array/{ => pop}/array_pop_array_transition.sol (100%) rename test/libsolidity/semanticTests/array/{ => pop}/array_pop_empty_exception.sol (100%) rename test/libsolidity/semanticTests/array/{ => pop}/array_pop_isolated.sol (100%) rename test/libsolidity/semanticTests/array/{ => pop}/array_pop_storage_empty.sol (100%) rename test/libsolidity/semanticTests/array/{ => pop}/array_pop_uint16_transition.sol (100%) rename test/libsolidity/semanticTests/array/{ => pop}/array_pop_uint24_transition.sol (100%) rename test/libsolidity/semanticTests/array/{ => pop}/byte_array_pop.sol (100%) rename test/libsolidity/semanticTests/array/{ => pop}/byte_array_pop_copy_long.sol (100%) rename test/libsolidity/semanticTests/array/{ => pop}/byte_array_pop_empty_exception.sol (100%) rename test/libsolidity/semanticTests/array/{ => pop}/byte_array_pop_isolated.sol (100%) rename test/libsolidity/semanticTests/array/{ => pop}/byte_array_pop_masking_long.sol (100%) rename test/libsolidity/semanticTests/array/{ => push}/array_push.sol (100%) rename test/libsolidity/semanticTests/array/{ => push}/array_push_nested.sol (100%) rename test/libsolidity/semanticTests/array/{ => push}/array_push_packed_array.sol (100%) rename test/libsolidity/semanticTests/array/{ => push}/array_push_struct.sol (100%) rename test/libsolidity/semanticTests/array/{ => push}/byte_array_push.sol (100%) rename test/libsolidity/semanticTests/array/{ => push}/byte_array_push_transition.sol (100%) rename test/libsolidity/semanticTests/array/{ => push}/push_no_args_1d.sol (100%) rename test/libsolidity/semanticTests/array/{ => push}/push_no_args_2d.sol (100%) rename test/libsolidity/semanticTests/array/{ => push}/push_no_args_bytes.sol (100%) rename test/libsolidity/semanticTests/array/{ => push}/push_no_args_struct.sol (100%) diff --git a/test/libsolidity/semanticTests/array/array_copy_cleanup_uint128.sol b/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint128.sol similarity index 100% rename from test/libsolidity/semanticTests/array/array_copy_cleanup_uint128.sol rename to test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint128.sol diff --git a/test/libsolidity/semanticTests/array/array_copy_cleanup_uint40.sol b/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint40.sol similarity index 100% rename from test/libsolidity/semanticTests/array/array_copy_cleanup_uint40.sol rename to test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint40.sol diff --git a/test/libsolidity/semanticTests/array/bytes_delete_element.sol b/test/libsolidity/semanticTests/array/delete/bytes_delete_element.sol similarity index 100% rename from test/libsolidity/semanticTests/array/bytes_delete_element.sol rename to test/libsolidity/semanticTests/array/delete/bytes_delete_element.sol diff --git a/test/libsolidity/semanticTests/array/delete_bytes_array.sol b/test/libsolidity/semanticTests/array/delete/delete_bytes_array.sol similarity index 100% rename from test/libsolidity/semanticTests/array/delete_bytes_array.sol rename to test/libsolidity/semanticTests/array/delete/delete_bytes_array.sol diff --git a/test/libsolidity/semanticTests/array/delete_memory_array.sol b/test/libsolidity/semanticTests/array/delete/delete_memory_array.sol similarity index 100% rename from test/libsolidity/semanticTests/array/delete_memory_array.sol rename to test/libsolidity/semanticTests/array/delete/delete_memory_array.sol diff --git a/test/libsolidity/semanticTests/array/delete_on_array_of_structs.sol b/test/libsolidity/semanticTests/array/delete/delete_on_array_of_structs.sol similarity index 100% rename from test/libsolidity/semanticTests/array/delete_on_array_of_structs.sol rename to test/libsolidity/semanticTests/array/delete/delete_on_array_of_structs.sol diff --git a/test/libsolidity/semanticTests/array/delete_removes_bytes_data.sol b/test/libsolidity/semanticTests/array/delete/delete_removes_bytes_data.sol similarity index 100% rename from test/libsolidity/semanticTests/array/delete_removes_bytes_data.sol rename to test/libsolidity/semanticTests/array/delete/delete_removes_bytes_data.sol diff --git a/test/libsolidity/semanticTests/array/delete_storage_array.sol b/test/libsolidity/semanticTests/array/delete/delete_storage_array.sol similarity index 100% rename from test/libsolidity/semanticTests/array/delete_storage_array.sol rename to test/libsolidity/semanticTests/array/delete/delete_storage_array.sol diff --git a/test/libsolidity/semanticTests/array/delete_storage_array_packed.sol b/test/libsolidity/semanticTests/array/delete/delete_storage_array_packed.sol similarity index 100% rename from test/libsolidity/semanticTests/array/delete_storage_array_packed.sol rename to test/libsolidity/semanticTests/array/delete/delete_storage_array_packed.sol diff --git a/test/libsolidity/semanticTests/array/arrays_complex_memory_index_access.sol b/test/libsolidity/semanticTests/array/indexAccess/arrays_complex_memory_index_access.sol similarity index 100% rename from test/libsolidity/semanticTests/array/arrays_complex_memory_index_access.sol rename to test/libsolidity/semanticTests/array/indexAccess/arrays_complex_memory_index_access.sol diff --git a/test/libsolidity/semanticTests/array/bytes_memory_index_access.sol b/test/libsolidity/semanticTests/array/indexAccess/bytes_memory_index_access.sol similarity index 100% rename from test/libsolidity/semanticTests/array/bytes_memory_index_access.sol rename to test/libsolidity/semanticTests/array/indexAccess/bytes_memory_index_access.sol diff --git a/test/libsolidity/semanticTests/array/fixed_bytes_index_access.sol b/test/libsolidity/semanticTests/array/indexAccess/fixed_bytes_index_access.sol similarity index 100% rename from test/libsolidity/semanticTests/array/fixed_bytes_index_access.sol rename to test/libsolidity/semanticTests/array/indexAccess/fixed_bytes_index_access.sol diff --git a/test/libsolidity/semanticTests/array/index_access.sol b/test/libsolidity/semanticTests/array/indexAccess/index_access.sol similarity index 100% rename from test/libsolidity/semanticTests/array/index_access.sol rename to test/libsolidity/semanticTests/array/indexAccess/index_access.sol diff --git a/test/libsolidity/semanticTests/array/inline_array_index_access_ints.sol b/test/libsolidity/semanticTests/array/indexAccess/inline_array_index_access_ints.sol similarity index 100% rename from test/libsolidity/semanticTests/array/inline_array_index_access_ints.sol rename to test/libsolidity/semanticTests/array/indexAccess/inline_array_index_access_ints.sol diff --git a/test/libsolidity/semanticTests/array/inline_array_index_access_strings.sol b/test/libsolidity/semanticTests/array/indexAccess/inline_array_index_access_strings.sol similarity index 100% rename from test/libsolidity/semanticTests/array/inline_array_index_access_strings.sol rename to test/libsolidity/semanticTests/array/indexAccess/inline_array_index_access_strings.sol diff --git a/test/libsolidity/semanticTests/array/memory_arrays_dynamic_index_access_write.sol b/test/libsolidity/semanticTests/array/indexAccess/memory_arrays_dynamic_index_access_write.sol similarity index 100% rename from test/libsolidity/semanticTests/array/memory_arrays_dynamic_index_access_write.sol rename to test/libsolidity/semanticTests/array/indexAccess/memory_arrays_dynamic_index_access_write.sol diff --git a/test/libsolidity/semanticTests/array/memory_arrays_index_access_write.sol b/test/libsolidity/semanticTests/array/indexAccess/memory_arrays_index_access_write.sol similarity index 100% rename from test/libsolidity/semanticTests/array/memory_arrays_index_access_write.sol rename to test/libsolidity/semanticTests/array/indexAccess/memory_arrays_index_access_write.sol diff --git a/test/libsolidity/semanticTests/array/array_pop.sol b/test/libsolidity/semanticTests/array/pop/array_pop.sol similarity index 100% rename from test/libsolidity/semanticTests/array/array_pop.sol rename to test/libsolidity/semanticTests/array/pop/array_pop.sol diff --git a/test/libsolidity/semanticTests/array/array_pop_array_transition.sol b/test/libsolidity/semanticTests/array/pop/array_pop_array_transition.sol similarity index 100% rename from test/libsolidity/semanticTests/array/array_pop_array_transition.sol rename to test/libsolidity/semanticTests/array/pop/array_pop_array_transition.sol diff --git a/test/libsolidity/semanticTests/array/array_pop_empty_exception.sol b/test/libsolidity/semanticTests/array/pop/array_pop_empty_exception.sol similarity index 100% rename from test/libsolidity/semanticTests/array/array_pop_empty_exception.sol rename to test/libsolidity/semanticTests/array/pop/array_pop_empty_exception.sol diff --git a/test/libsolidity/semanticTests/array/array_pop_isolated.sol b/test/libsolidity/semanticTests/array/pop/array_pop_isolated.sol similarity index 100% rename from test/libsolidity/semanticTests/array/array_pop_isolated.sol rename to test/libsolidity/semanticTests/array/pop/array_pop_isolated.sol diff --git a/test/libsolidity/semanticTests/array/array_pop_storage_empty.sol b/test/libsolidity/semanticTests/array/pop/array_pop_storage_empty.sol similarity index 100% rename from test/libsolidity/semanticTests/array/array_pop_storage_empty.sol rename to test/libsolidity/semanticTests/array/pop/array_pop_storage_empty.sol diff --git a/test/libsolidity/semanticTests/array/array_pop_uint16_transition.sol b/test/libsolidity/semanticTests/array/pop/array_pop_uint16_transition.sol similarity index 100% rename from test/libsolidity/semanticTests/array/array_pop_uint16_transition.sol rename to test/libsolidity/semanticTests/array/pop/array_pop_uint16_transition.sol diff --git a/test/libsolidity/semanticTests/array/array_pop_uint24_transition.sol b/test/libsolidity/semanticTests/array/pop/array_pop_uint24_transition.sol similarity index 100% rename from test/libsolidity/semanticTests/array/array_pop_uint24_transition.sol rename to test/libsolidity/semanticTests/array/pop/array_pop_uint24_transition.sol diff --git a/test/libsolidity/semanticTests/array/byte_array_pop.sol b/test/libsolidity/semanticTests/array/pop/byte_array_pop.sol similarity index 100% rename from test/libsolidity/semanticTests/array/byte_array_pop.sol rename to test/libsolidity/semanticTests/array/pop/byte_array_pop.sol diff --git a/test/libsolidity/semanticTests/array/byte_array_pop_copy_long.sol b/test/libsolidity/semanticTests/array/pop/byte_array_pop_copy_long.sol similarity index 100% rename from test/libsolidity/semanticTests/array/byte_array_pop_copy_long.sol rename to test/libsolidity/semanticTests/array/pop/byte_array_pop_copy_long.sol diff --git a/test/libsolidity/semanticTests/array/byte_array_pop_empty_exception.sol b/test/libsolidity/semanticTests/array/pop/byte_array_pop_empty_exception.sol similarity index 100% rename from test/libsolidity/semanticTests/array/byte_array_pop_empty_exception.sol rename to test/libsolidity/semanticTests/array/pop/byte_array_pop_empty_exception.sol diff --git a/test/libsolidity/semanticTests/array/byte_array_pop_isolated.sol b/test/libsolidity/semanticTests/array/pop/byte_array_pop_isolated.sol similarity index 100% rename from test/libsolidity/semanticTests/array/byte_array_pop_isolated.sol rename to test/libsolidity/semanticTests/array/pop/byte_array_pop_isolated.sol diff --git a/test/libsolidity/semanticTests/array/byte_array_pop_masking_long.sol b/test/libsolidity/semanticTests/array/pop/byte_array_pop_masking_long.sol similarity index 100% rename from test/libsolidity/semanticTests/array/byte_array_pop_masking_long.sol rename to test/libsolidity/semanticTests/array/pop/byte_array_pop_masking_long.sol diff --git a/test/libsolidity/semanticTests/array/array_push.sol b/test/libsolidity/semanticTests/array/push/array_push.sol similarity index 100% rename from test/libsolidity/semanticTests/array/array_push.sol rename to test/libsolidity/semanticTests/array/push/array_push.sol diff --git a/test/libsolidity/semanticTests/array/array_push_nested.sol b/test/libsolidity/semanticTests/array/push/array_push_nested.sol similarity index 100% rename from test/libsolidity/semanticTests/array/array_push_nested.sol rename to test/libsolidity/semanticTests/array/push/array_push_nested.sol diff --git a/test/libsolidity/semanticTests/array/array_push_packed_array.sol b/test/libsolidity/semanticTests/array/push/array_push_packed_array.sol similarity index 100% rename from test/libsolidity/semanticTests/array/array_push_packed_array.sol rename to test/libsolidity/semanticTests/array/push/array_push_packed_array.sol diff --git a/test/libsolidity/semanticTests/array/array_push_struct.sol b/test/libsolidity/semanticTests/array/push/array_push_struct.sol similarity index 100% rename from test/libsolidity/semanticTests/array/array_push_struct.sol rename to test/libsolidity/semanticTests/array/push/array_push_struct.sol diff --git a/test/libsolidity/semanticTests/array/byte_array_push.sol b/test/libsolidity/semanticTests/array/push/byte_array_push.sol similarity index 100% rename from test/libsolidity/semanticTests/array/byte_array_push.sol rename to test/libsolidity/semanticTests/array/push/byte_array_push.sol diff --git a/test/libsolidity/semanticTests/array/byte_array_push_transition.sol b/test/libsolidity/semanticTests/array/push/byte_array_push_transition.sol similarity index 100% rename from test/libsolidity/semanticTests/array/byte_array_push_transition.sol rename to test/libsolidity/semanticTests/array/push/byte_array_push_transition.sol diff --git a/test/libsolidity/semanticTests/array/push_no_args_1d.sol b/test/libsolidity/semanticTests/array/push/push_no_args_1d.sol similarity index 100% rename from test/libsolidity/semanticTests/array/push_no_args_1d.sol rename to test/libsolidity/semanticTests/array/push/push_no_args_1d.sol diff --git a/test/libsolidity/semanticTests/array/push_no_args_2d.sol b/test/libsolidity/semanticTests/array/push/push_no_args_2d.sol similarity index 100% rename from test/libsolidity/semanticTests/array/push_no_args_2d.sol rename to test/libsolidity/semanticTests/array/push/push_no_args_2d.sol diff --git a/test/libsolidity/semanticTests/array/push_no_args_bytes.sol b/test/libsolidity/semanticTests/array/push/push_no_args_bytes.sol similarity index 100% rename from test/libsolidity/semanticTests/array/push_no_args_bytes.sol rename to test/libsolidity/semanticTests/array/push/push_no_args_bytes.sol diff --git a/test/libsolidity/semanticTests/array/push_no_args_struct.sol b/test/libsolidity/semanticTests/array/push/push_no_args_struct.sol similarity index 100% rename from test/libsolidity/semanticTests/array/push_no_args_struct.sol rename to test/libsolidity/semanticTests/array/push/push_no_args_struct.sol From 29d480d0edbd5d9e3b7a63f310cb4b160cbae882 Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Fri, 13 Nov 2020 09:44:43 +0100 Subject: [PATCH 10/29] Moving some byte array pop tests to semanticTests. --- test/libsolidity/SolidityEndToEndTest.cpp | 75 ------------------- .../pop/byte_array_pop_long_storage_empty.sol | 21 ++++++ ...ray_pop_long_storage_empty_garbage_ref.sol | 20 +++++ .../pop/byte_array_pop_storage_empty.sol | 16 ++++ 4 files changed, 57 insertions(+), 75 deletions(-) create mode 100644 test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty.sol create mode 100644 test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty_garbage_ref.sol create mode 100644 test/libsolidity/semanticTests/array/pop/byte_array_pop_storage_empty.sol diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index f8472d191..e610f239f 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2854,81 +2854,6 @@ BOOST_AUTO_TEST_CASE(array_copy_storage_abi) ); } -BOOST_AUTO_TEST_CASE(byte_array_pop_storage_empty) -{ - char const* sourceCode = R"( - contract c { - bytes data; - function test() public { - data.push(0x07); - data.push(0x05); - data.push(0x03); - data.pop(); - data.pop(); - data.pop(); - } - } - )"; - ALSO_VIA_YUL( - DISABLE_EWASM_TESTRUN() - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs()); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ); -} - -BOOST_AUTO_TEST_CASE(byte_array_pop_long_storage_empty) -{ - char const* sourceCode = R"( - contract c { - uint256 a; - uint256 b; - uint256 c; - bytes data; - function test() public returns (bool) { - for (uint8 i = 0; i <= 40; i++) - data.push(byte(i+1)); - for (int8 j = 40; j >= 0; j--) { - require(data[uint8(j)] == byte(j+1)); - require(data.length == uint8(j+1)); - data.pop(); - } - return true; - } - } - )"; - ALSO_VIA_YUL( - DISABLE_EWASM_TESTRUN() - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(true)); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ); -} - -BOOST_AUTO_TEST_CASE(byte_array_pop_long_storage_empty_garbage_ref) -{ - char const* sourceCode = R"( - contract c { - uint256 a; - uint256 b; - bytes data; - function test() public { - for (uint8 i = 0; i <= 40; i++) - data.push(0x03); - for (uint8 j = 0; j <= 40; j++) { - assembly { - mstore(0, "garbage") - } - data.pop(); - } - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs()); - BOOST_CHECK(storageEmpty(m_contractAddress)); -} - BOOST_AUTO_TEST_CASE(external_array_args) { char const* sourceCode = R"( diff --git a/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty.sol b/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty.sol new file mode 100644 index 000000000..fb61f8c74 --- /dev/null +++ b/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty.sol @@ -0,0 +1,21 @@ +contract c { + uint256 a; + uint256 b; + uint256 c; + bytes data; + function test() public returns (bool) { + for (uint8 i = 0; i <= 40; i++) + data.push(byte(i+1)); + for (int8 j = 40; j >= 0; j--) { + require(data[uint8(j)] == byte(j+1)); + require(data.length == uint8(j+1)); + data.pop(); + } + return true; + } +} +// ==== +// compileViaYul: also +// ---- +// test() -> true +// storage: empty diff --git a/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty_garbage_ref.sol b/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty_garbage_ref.sol new file mode 100644 index 000000000..a230eb033 --- /dev/null +++ b/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty_garbage_ref.sol @@ -0,0 +1,20 @@ +contract c { + uint256 a; + uint256 b; + bytes data; + function test() public { + for (uint8 i = 0; i <= 40; i++) + data.push(0x03); + for (uint8 j = 0; j <= 40; j++) { + assembly { + mstore(0, "garbage") + } + data.pop(); + } + } +} +// ==== +// compileViaYul: also +// ---- +// test() -> +// storage: empty diff --git a/test/libsolidity/semanticTests/array/pop/byte_array_pop_storage_empty.sol b/test/libsolidity/semanticTests/array/pop/byte_array_pop_storage_empty.sol new file mode 100644 index 000000000..db8899388 --- /dev/null +++ b/test/libsolidity/semanticTests/array/pop/byte_array_pop_storage_empty.sol @@ -0,0 +1,16 @@ +contract c { + bytes data; + function test() public { + data.push(0x07); + data.push(0x05); + data.push(0x03); + data.pop(); + data.pop(); + data.pop(); + } +} +// ==== +// compileViaYul: also +// ---- +// test() -> +// storage: empty From 1428a939a667b7dd4cc1adc11f40939ee6ceb0ef Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Fri, 13 Nov 2020 12:14:30 +0100 Subject: [PATCH 11/29] Moving some more array tests to semanticTests. --- test/libsolidity/SolidityEndToEndTest.cpp | 401 ------------------ .../array/calldata_array_two_dimensional.sol | 38 ++ .../calldata_array_two_dimensional_1.sol | 38 ++ .../array/calldata_bytes_array_bounds.sol | 12 + .../copying/array_copy_calldata_storage.sol | 25 ++ .../copying/array_copy_including_array.sol | 40 ++ .../copying/arrays_from_and_to_storage.sol | 18 + .../array/delete/memory_arrays_delete.sol | 15 + .../array/external_array_args.sol | 12 + .../array/indexAccess/bytes_index_access.sol | 27 ++ .../indexAccess/bytes_index_access_memory.sol | 17 + .../constructor/arrays_in_constructors.sol | 28 ++ .../bytes_in_constructors_packer.sol | 28 ++ .../bytes_in_constructors_unpacker.sol | 12 + 14 files changed, 310 insertions(+), 401 deletions(-) create mode 100644 test/libsolidity/semanticTests/array/calldata_array_two_dimensional.sol create mode 100644 test/libsolidity/semanticTests/array/calldata_array_two_dimensional_1.sol create mode 100644 test/libsolidity/semanticTests/array/calldata_bytes_array_bounds.sol create mode 100644 test/libsolidity/semanticTests/array/copying/array_copy_calldata_storage.sol create mode 100644 test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol create mode 100644 test/libsolidity/semanticTests/array/copying/arrays_from_and_to_storage.sol create mode 100644 test/libsolidity/semanticTests/array/delete/memory_arrays_delete.sol create mode 100644 test/libsolidity/semanticTests/array/external_array_args.sol create mode 100644 test/libsolidity/semanticTests/array/indexAccess/bytes_index_access.sol create mode 100644 test/libsolidity/semanticTests/array/indexAccess/bytes_index_access_memory.sol create mode 100644 test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol create mode 100644 test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol create mode 100644 test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index e610f239f..b650db5b5 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2854,145 +2854,6 @@ BOOST_AUTO_TEST_CASE(array_copy_storage_abi) ); } -BOOST_AUTO_TEST_CASE(external_array_args) -{ - char const* sourceCode = R"( - contract c { - function test(uint[8] calldata a, uint[] calldata b, uint[5] calldata c, uint a_index, uint b_index, uint c_index) - external returns (uint av, uint bv, uint cv) { - av = a[a_index]; - bv = b[b_index]; - cv = c[c_index]; - } - } - )"; - compileAndRun(sourceCode); - bytes params = encodeArgs( - 1, 2, 3, 4, 5, 6, 7, 8, // a - 32 * (8 + 1 + 5 + 1 + 1 + 1), // offset to b - 21, 22, 23, 24, 25, // c - 0, 1, 2, // (a,b,c)_index - 3, // b.length - 11, 12, 13 // b - ); - ABI_CHECK(callContractFunction("test(uint256[8],uint256[],uint256[5],uint256,uint256,uint256)", params), encodeArgs(1, 12, 23)); -} - -BOOST_AUTO_TEST_CASE(bytes_index_access) -{ - char const* sourceCode = R"( - contract c { - bytes data; - function direct(bytes calldata arg, uint index) external returns (uint) { - return uint(uint8(arg[index])); - } - function storageCopyRead(bytes calldata arg, uint index) external returns (uint) { - data = arg; - return uint(uint8(data[index])); - } - function storageWrite() external returns (uint) { - data = new bytes(35); - data[31] = 0x77; - data[32] = 0x14; - - data[31] = 0x01; - data[31] |= 0x08; - data[30] = 0x01; - data[32] = 0x03; - return uint(uint8(data[30])) * 0x100 | uint(uint8(data[31])) * 0x10 | uint(uint8(data[32])); - } - } - )"; - string array{ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, 33}; - ALSO_VIA_YUL( - DISABLE_EWASM_TESTRUN() - - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("direct(bytes,uint256)", 64, 33, u256(array.length()), array), encodeArgs(33)); - ABI_CHECK(callContractFunction("storageCopyRead(bytes,uint256)", 64, 33, u256(array.length()), array), encodeArgs(33)); - ABI_CHECK(callContractFunction("storageWrite()"), encodeArgs(0x193)); - ); -} - -BOOST_AUTO_TEST_CASE(array_copy_calldata_storage) -{ - char const* sourceCode = R"( - contract c { - uint[9] m_data; - uint[] m_data_dyn; - uint8[][] m_byte_data; - function store(uint[9] calldata a, uint8[3][] calldata b) external returns (uint8) { - m_data = a; - m_data_dyn = a; - m_byte_data = b; - return b[3][1]; // note that access and declaration are reversed to each other - } - function retrieve() public returns (uint a, uint b, uint c, uint d, uint e, uint f, uint g) { - a = m_data.length; - b = m_data[7]; - c = m_data_dyn.length; - d = m_data_dyn[7]; - e = m_byte_data.length; - f = m_byte_data[3].length; - g = m_byte_data[3][1]; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("store(uint256[9],uint8[3][])", encodeArgs(21, 22, 23, 24, 25, 26, 27, 28, 29, u256(32 * (9 + 1)), 4, 1, 2, 3, 11, 12, 13, 21, 22, 23, 31, 32, 33 )), encodeArgs(32)); - ABI_CHECK(callContractFunction("retrieve()"), encodeArgs(9, 28, 9, 28, 4, 3, 32)); -} - -BOOST_AUTO_TEST_CASE(array_copy_including_array) -{ - char const* sourceCode = R"( - contract c { - uint[3][90][] large; - uint[3][3][] small; - function test() public returns (uint r) { - for (uint i = 0; i < 7; i++) { - large.push(); - small.push(); - } - large[3][2][0] = 2; - large[1] = large[3]; - small[3][2][0] = 2; - small[1] = small[2]; - r = (( - small[3][2][0] * 0x100 | - small[1][2][0]) * 0x100 | - large[3][2][0]) * 0x100 | - large[1][2][0]; - delete small; - delete large; - - } - function clear() public returns (uint, uint) { - for (uint i = 0; i < 7; i++) { - large.push(); - small.push(); - } - small[3][2][0] = 0; - large[3][2][0] = 0; - while (small.length > 0) - small.pop(); - while (large.length > 0) - large.pop(); - return (small.length, large.length); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(0x02000202)); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("clear()"), encodeArgs(0, 0)); - BOOST_CHECK(storageEmpty(m_contractAddress)); -} - //BOOST_AUTO_TEST_CASE(assignment_to_const_array_vars) //{ // char const* sourceCode = R"( @@ -3305,159 +3166,6 @@ BOOST_AUTO_TEST_CASE(return_bytes_internal) } } -BOOST_AUTO_TEST_CASE(bytes_index_access_memory) -{ - char const* sourceCode = R"( - contract Main { - function f(bytes memory _s1, uint i1, uint i2, uint i3) public returns (byte c1, byte c2, byte c3) { - c1 = _s1[i1]; - c2 = intern(_s1, i2); - c3 = internIndirect(_s1)[i3]; - } - function intern(bytes memory _s1, uint i) public returns (byte c) { - return _s1[i]; - } - function internIndirect(bytes memory _s1) public returns (bytes memory) { - return _s1; - } - } - )"; - compileAndRun(sourceCode, 0, "Main"); - string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"); - bytes dyn1 = encodeArgs(u256(s1.length()), s1); - bytes args1 = encodeArgs(u256(0x80), u256(3), u256(4), u256(5)) + dyn1; - BOOST_REQUIRE( - callContractFunction("f(bytes,uint256,uint256,uint256)", asString(args1)) == - encodeArgs(string{s1[3]}, string{s1[4]}, string{s1[5]}) - ); -} - -BOOST_AUTO_TEST_CASE(bytes_in_constructors_unpacker) -{ - char const* sourceCode = R"( - contract Test { - uint public m_x; - bytes public m_s; - constructor(uint x, bytes memory s) { - m_x = x; - m_s = s; - } - } - )"; - string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"); - bytes dyn1 = encodeArgs(u256(s1.length()), s1); - u256 x = 7; - bytes args1 = encodeArgs(x, u256(0x40)) + dyn1; - compileAndRun(sourceCode, 0, "Test", args1); - BOOST_REQUIRE(callContractFunction("m_x()") == encodeArgs(x)); - BOOST_REQUIRE(callContractFunction("m_s()") == encodeArgs(u256(0x20)) + dyn1); -} - -BOOST_AUTO_TEST_CASE(bytes_in_constructors_packer) -{ - char const* sourceCode = R"( - contract Base { - uint public m_x; - bytes m_s; - constructor(uint x, bytes memory s) { - m_x = x; - m_s = s; - } - function part(uint i) public returns (byte) { - return m_s[i]; - } - } - contract Main is Base { - constructor(bytes memory s, uint x) Base(x, f(s)) {} - function f(bytes memory s) public returns (bytes memory) { - return s; - } - } - contract Creator { - function f(uint x, bytes memory s) public returns (uint r, byte ch) { - Main c = new Main(s, x); - r = c.m_x(); - ch = c.part(x); - } - } - )"; - compileAndRun(sourceCode, 0, "Creator"); - string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"); - bytes dyn1 = encodeArgs(u256(s1.length()), s1); - u256 x = 7; - bytes args1 = encodeArgs(x, u256(0x40)) + dyn1; - BOOST_REQUIRE( - callContractFunction("f(uint256,bytes)", asString(args1)) == - encodeArgs(x, string{s1[unsigned(x)]}) - ); -} - -BOOST_AUTO_TEST_CASE(arrays_in_constructors) -{ - char const* sourceCode = R"( - contract Base { - uint public m_x; - address[] m_s; - constructor(uint x, address[] memory s) { - m_x = x; - m_s = s; - } - function part(uint i) public returns (address) { - return m_s[i]; - } - } - contract Main is Base { - constructor(address[] memory s, uint x) Base(x, f(s)) {} - function f(address[] memory s) public returns (address[] memory) { - return s; - } - } - contract Creator { - function f(uint x, address[] memory s) public returns (uint r, address ch) { - Main c = new Main(s, x); - r = c.m_x(); - ch = c.part(x); - } - } - )"; - compileAndRun(sourceCode, 0, "Creator"); - vector s1{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; - bytes dyn1 = encodeArgs(u256(s1.size()), s1); - u256 x = 7; - bytes args1 = encodeArgs(x, u256(0x40)) + dyn1; - BOOST_REQUIRE( - callContractFunction("f(uint256,address[])", asString(args1)) == - encodeArgs(x, s1[unsigned(x)]) - ); -} - -BOOST_AUTO_TEST_CASE(arrays_from_and_to_storage) -{ - char const* sourceCode = R"( - contract Test { - uint24[] public data; - function set(uint24[] memory _data) public returns (uint) { - data = _data; - return data.length; - } - function get() public returns (uint24[] memory) { - return data; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - vector data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}; - BOOST_REQUIRE( - callContractFunction("set(uint24[])", u256(0x20), u256(data.size()), data) == - encodeArgs(u256(data.size())) - ); - ABI_CHECK(callContractFunction("data(uint256)", u256(7)), encodeArgs(u256(8))); - ABI_CHECK(callContractFunction("data(uint256)", u256(15)), encodeArgs(u256(16))); - ABI_CHECK(callContractFunction("data(uint256)", u256(18)), encodeArgs()); - ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(0x20), u256(data.size()), data)); -} - BOOST_AUTO_TEST_CASE(memory_types_initialisation) { char const* sourceCode = R"( @@ -3480,35 +3188,6 @@ BOOST_AUTO_TEST_CASE(memory_types_initialisation) ABI_CHECK(callContractFunction("nestedStat()"), encodeArgs(vector(3 * 7))); } -BOOST_AUTO_TEST_CASE(memory_arrays_delete) -{ - char const* sourceCode = R"( - contract Test { - function del() public returns (uint24[3][4] memory) { - uint24[3][4] memory x; - for (uint24 i = 0; i < x.length; i ++) - for (uint24 j = 0; j < x[i].length; j ++) - x[i][j] = i * 0x10 + j; - delete x[1]; - delete x[3][2]; - return x; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - vector data(3 * 4); - for (unsigned i = 0; i < 4; i++) - for (unsigned j = 0; j < 3; j++) - { - u256 v = 0; - if (!(i == 1 || (i == 3 && j == 2))) - v = i * 0x10 + j; - data[i * 3 + j] = v; - } - ABI_CHECK(callContractFunction("del()"), encodeArgs(data)); -} - BOOST_AUTO_TEST_CASE(calldata_struct_short) { char const* sourceCode = R"( @@ -3557,86 +3236,6 @@ BOOST_AUTO_TEST_CASE(calldata_struct_function_type) ABI_CHECK(callContractFunctionNoEncoding("f((function))", fn_C_h), encodeArgs(23)); } -BOOST_AUTO_TEST_CASE(calldata_bytes_array_bounds) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - function f(bytes[] calldata a, uint256 i) external returns (uint) { - return uint8(a[0][i]); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK( - callContractFunction("f(bytes[],uint256)", 0x40, 0, 1, 0x20, 2, bytes{'a', 'b'} + bytes(30, 0)), - encodeArgs('a') - ); - ABI_CHECK( - callContractFunction("f(bytes[],uint256)", 0x40, 1, 1, 0x20, 2, bytes{'a', 'b'} + bytes(30, 0)), - encodeArgs('b') - ); - ABI_CHECK( - callContractFunction("f(bytes[],uint256)", 0x40, 2, 1, 0x20, 2, bytes{'a', 'b'} + bytes(30, 0)), - encodeArgs() - ); -} - -BOOST_AUTO_TEST_CASE(calldata_array_two_dimensional) -{ - vector> data { - { 0x0A01, 0x0A02, 0x0A03 }, - { 0x0B01, 0x0B02, 0x0B03, 0x0B04 } - }; - - for (bool outerDynamicallySized: { true, false }) - { - string arrayType = outerDynamicallySized ? "uint256[][]" : "uint256[][2]"; - string sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - function test()" + arrayType + R"( calldata a) external returns (uint256) { - return a.length; - } - function test()" + arrayType + R"( calldata a, uint256 i) external returns (uint256) { - return a[i].length; - } - function test()" + arrayType + R"( calldata a, uint256 i, uint256 j) external returns (uint256) { - return a[i][j]; - } - function reenc()" + arrayType + R"( calldata a, uint256 i, uint256 j) external returns (uint256) { - return this.test(a, i, j); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - bytes encoding = encodeArray( - outerDynamicallySized, - true, - data | boost::adaptors::transformed([&](vector const& _values) { - return encodeArray(true, false, _values); - }) - ); - - ABI_CHECK(callContractFunction("test(" + arrayType + ")", 0x20, encoding), encodeArgs(data.size())); - for (size_t i = 0; i < data.size(); i++) - { - ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256)", 0x40, i, encoding), encodeArgs(data[i].size())); - for (size_t j = 0; j < data[i].size(); j++) - { - ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256)", 0x60, i, j, encoding), encodeArgs(data[i][j])); - ABI_CHECK(callContractFunction("reenc(" + arrayType + ",uint256,uint256)", 0x60, i, j, encoding), encodeArgs(data[i][j])); - } - // out of bounds access - ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256)", 0x60, i, data[i].size(), encoding), encodeArgs()); - } - // out of bounds access - ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256)", 0x40, data.size(), encoding), encodeArgs()); - } -} - BOOST_AUTO_TEST_CASE(calldata_array_dynamic_three_dimensional) { vector>> data { diff --git a/test/libsolidity/semanticTests/array/calldata_array_two_dimensional.sol b/test/libsolidity/semanticTests/array/calldata_array_two_dimensional.sol new file mode 100644 index 000000000..4c20627e7 --- /dev/null +++ b/test/libsolidity/semanticTests/array/calldata_array_two_dimensional.sol @@ -0,0 +1,38 @@ +pragma experimental ABIEncoderV2; +contract C { + function test(uint256[][2] calldata a) external returns (uint256) { + return a.length; + } + function test(uint256[][2] calldata a, uint256 i) external returns (uint256) { + return a[i].length; + } + function test(uint256[][2] calldata a, uint256 i, uint256 j) external returns (uint256) { + return a[i][j]; + } + function reenc(uint256[][2] calldata a, uint256 i, uint256 j) external returns (uint256) { + return this.test(a, i, j); + } +} +// ==== +// compileViaYul: also +// ---- +// test(uint256[][2]): 0x20, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 2 +// test(uint256[][2],uint256): 0x40, 0, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 3 +// test(uint256[][2],uint256): 0x40, 1, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 4 +// test(uint256[][2],uint256,uint256): 0x60, 0, 0, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A01 +// reenc(uint256[][2],uint256,uint256): 0x60, 0, 0, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A01 +// test(uint256[][2],uint256,uint256): 0x60, 0, 1, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A02 +// reenc(uint256[][2],uint256,uint256): 0x60, 0, 1, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A02 +// test(uint256[][2],uint256,uint256): 0x60, 0, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A03 +// reenc(uint256[][2],uint256,uint256): 0x60, 0, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A03 +// test(uint256[][2],uint256,uint256): 0x60, 1, 0, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B01 +// reenc(uint256[][2],uint256,uint256): 0x60, 1, 0, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B01 +// test(uint256[][2],uint256,uint256): 0x60, 1, 1, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B02 +// reenc(uint256[][2],uint256,uint256): 0x60, 1, 1, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B02 +// test(uint256[][2],uint256,uint256): 0x60, 1, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B03 +// reenc(uint256[][2],uint256,uint256): 0x60, 1, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B03 +// test(uint256[][2],uint256,uint256): 0x60, 1, 3, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B04 +// reenc(uint256[][2],uint256,uint256): 0x60, 1, 3, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B04 +// test(uint256[][2],uint256,uint256): 0x60, 0, 3, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> FAILURE +// test(uint256[][2],uint256,uint256): 0x60, 1, 4, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> FAILURE +// test(uint256[][2],uint256): 0x40, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> FAILURE diff --git a/test/libsolidity/semanticTests/array/calldata_array_two_dimensional_1.sol b/test/libsolidity/semanticTests/array/calldata_array_two_dimensional_1.sol new file mode 100644 index 000000000..9d40a356d --- /dev/null +++ b/test/libsolidity/semanticTests/array/calldata_array_two_dimensional_1.sol @@ -0,0 +1,38 @@ +pragma experimental ABIEncoderV2; +contract C { + function test(uint256[][] calldata a) external returns (uint256) { + return a.length; + } + function test(uint256[][] calldata a, uint256 i) external returns (uint256) { + return a[i].length; + } + function test(uint256[][] calldata a, uint256 i, uint256 j) external returns (uint256) { + return a[i][j]; + } + function reenc(uint256[][] calldata a, uint256 i, uint256 j) external returns (uint256) { + return this.test(a, i, j); + } +} +// ==== +// compileViaYul: also +// ---- +// test(uint256[][]): 0x20, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 2 +// test(uint256[][],uint256): 0x40, 0, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 3 +// test(uint256[][],uint256): 0x40, 1, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 4 +// test(uint256[][],uint256,uint256): 0x60, 0, 0, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A01 +// reenc(uint256[][],uint256,uint256): 0x60, 0, 0, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A01 +// test(uint256[][],uint256,uint256): 0x60, 0, 1, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A02 +// reenc(uint256[][],uint256,uint256): 0x60, 0, 1, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A02 +// test(uint256[][],uint256,uint256): 0x60, 0, 2, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A03 +// reenc(uint256[][],uint256,uint256): 0x60, 0, 2, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A03 +// test(uint256[][],uint256,uint256): 0x60, 1, 0, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B01 +// reenc(uint256[][],uint256,uint256): 0x60, 1, 0, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B01 +// test(uint256[][],uint256,uint256): 0x60, 1, 1, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B02 +// reenc(uint256[][],uint256,uint256): 0x60, 1, 1, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B02 +// test(uint256[][],uint256,uint256): 0x60, 1, 2, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B03 +// reenc(uint256[][],uint256,uint256): 0x60, 1, 2, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B03 +// test(uint256[][],uint256,uint256): 0x60, 1, 3, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B04 +// reenc(uint256[][],uint256,uint256): 0x60, 1, 3, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B04 +// test(uint256[][],uint256,uint256): 0x60, 0, 3, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> FAILURE +// test(uint256[][],uint256,uint256): 0x60, 1, 4, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> FAILURE +// test(uint256[][],uint256): 0x40, 2, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> FAILURE diff --git a/test/libsolidity/semanticTests/array/calldata_bytes_array_bounds.sol b/test/libsolidity/semanticTests/array/calldata_bytes_array_bounds.sol new file mode 100644 index 000000000..ed120d501 --- /dev/null +++ b/test/libsolidity/semanticTests/array/calldata_bytes_array_bounds.sol @@ -0,0 +1,12 @@ +pragma experimental ABIEncoderV2; +contract C { + function f(bytes[] calldata a, uint256 i) external returns (uint) { + return uint8(a[0][i]); + } +} +// ==== +// compileViaYul: also +// ---- +// f(bytes[],uint256): 0x40, 0, 1, 0x20, 2, 0x6162000000000000000000000000000000000000000000000000000000000000 -> 0x61 +// f(bytes[],uint256): 0x40, 1, 1, 0x20, 2, 0x6162000000000000000000000000000000000000000000000000000000000000 -> 0x62 +// f(bytes[],uint256): 0x40, 2, 1, 0x20, 2, 0x6162000000000000000000000000000000000000000000000000000000000000 -> FAILURE diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_calldata_storage.sol b/test/libsolidity/semanticTests/array/copying/array_copy_calldata_storage.sol new file mode 100644 index 000000000..20c934bc4 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_copy_calldata_storage.sol @@ -0,0 +1,25 @@ +contract c { + uint[9] m_data; + uint[] m_data_dyn; + uint8[][] m_byte_data; + function store(uint[9] calldata a, uint8[3][] calldata b) external returns (uint8) { + m_data = a; + m_data_dyn = a; + m_byte_data = b; + return b[3][1]; // note that access and declaration are reversed to each other + } + function retrieve() public returns (uint a, uint b, uint c, uint d, uint e, uint f, uint g) { + a = m_data.length; + b = m_data[7]; + c = m_data_dyn.length; + d = m_data_dyn[7]; + e = m_byte_data.length; + f = m_byte_data[3].length; + g = m_byte_data[3][1]; + } +} +// ==== +// compileViaYul: also +// ---- +// store(uint256[9],uint8[3][]): 21, 22, 23, 24, 25, 26, 27, 28, 29, 0x140, 4, 1, 2, 3, 11, 12, 13, 21, 22, 23, 31, 32, 33 -> 32 +// retrieve() -> 9, 28, 9, 28, 4, 3, 32 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol b/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol new file mode 100644 index 000000000..04564b748 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol @@ -0,0 +1,40 @@ +contract c { + uint[3][90][] large; + uint[3][3][] small; + function test() public returns (uint r) { + for (uint i = 0; i < 7; i++) { + large.push(); + small.push(); + } + large[3][2][0] = 2; + large[1] = large[3]; + small[3][2][0] = 2; + small[1] = small[2]; + r = (( + small[3][2][0] * 0x100 | + small[1][2][0]) * 0x100 | + large[3][2][0]) * 0x100 | + large[1][2][0]; + delete small; + delete large; + + } + function clear() public returns (uint, uint) { + for (uint i = 0; i < 7; i++) { + large.push(); + small.push(); + } + small[3][2][0] = 0; + large[3][2][0] = 0; + while (small.length > 0) + small.pop(); + while (large.length > 0) + large.pop(); + return (small.length, large.length); + } +} +// ---- +// test() -> 0x02000202 +// storage: empty +// clear() -> 0, 0 +// storage: empty diff --git a/test/libsolidity/semanticTests/array/copying/arrays_from_and_to_storage.sol b/test/libsolidity/semanticTests/array/copying/arrays_from_and_to_storage.sol new file mode 100644 index 000000000..8b99bf14c --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/arrays_from_and_to_storage.sol @@ -0,0 +1,18 @@ +contract Test { + uint24[] public data; + function set(uint24[] memory _data) public returns (uint) { + data = _data; + return data.length; + } + function get() public returns (uint24[] memory) { + return data; + } +} +// ==== +// compileViaYul: also +// ---- +// set(uint24[]): 0x20, 18, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 -> 18 +// data(uint256): 7 -> 8 +// data(uint256): 15 -> 16 +// data(uint256): 18 -> FAILURE +// get() -> 0x20, 18, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 diff --git a/test/libsolidity/semanticTests/array/delete/memory_arrays_delete.sol b/test/libsolidity/semanticTests/array/delete/memory_arrays_delete.sol new file mode 100644 index 000000000..31c86b353 --- /dev/null +++ b/test/libsolidity/semanticTests/array/delete/memory_arrays_delete.sol @@ -0,0 +1,15 @@ +contract Test { + function del() public returns (uint24[3][4] memory) { + uint24[3][4] memory x; + for (uint24 i = 0; i < x.length; i ++) + for (uint24 j = 0; j < x[i].length; j ++) + x[i][j] = i * 0x10 + j; + delete x[1]; + delete x[3][2]; + return x; + } +} +// ==== +// compileViaYul: also +// ---- +// del() -> 0, 1, 2, 0, 0, 0, 0x20, 0x21, 0x22, 0x30, 0x31, 0 diff --git a/test/libsolidity/semanticTests/array/external_array_args.sol b/test/libsolidity/semanticTests/array/external_array_args.sol new file mode 100644 index 000000000..38205ea95 --- /dev/null +++ b/test/libsolidity/semanticTests/array/external_array_args.sol @@ -0,0 +1,12 @@ +contract c { + function test(uint[8] calldata a, uint[] calldata b, uint[5] calldata c, uint a_index, uint b_index, uint c_index) + external returns (uint av, uint bv, uint cv) { + av = a[a_index]; + bv = b[b_index]; + cv = c[c_index]; + } +} +// ==== +// compileViaYul: also +// ---- +// test(uint256[8],uint256[],uint256[5],uint256,uint256,uint256): 1, 2, 3, 4, 5, 6, 7, 8, 0x220, 21, 22, 23, 24, 25, 0, 1, 2, 3, 11, 12, 13 -> 1, 12, 23 diff --git a/test/libsolidity/semanticTests/array/indexAccess/bytes_index_access.sol b/test/libsolidity/semanticTests/array/indexAccess/bytes_index_access.sol new file mode 100644 index 000000000..9a0c85ac7 --- /dev/null +++ b/test/libsolidity/semanticTests/array/indexAccess/bytes_index_access.sol @@ -0,0 +1,27 @@ +contract c { + bytes data; + function direct(bytes calldata arg, uint index) external returns (uint) { + return uint(uint8(arg[index])); + } + function storageCopyRead(bytes calldata arg, uint index) external returns (uint) { + data = arg; + return uint(uint8(data[index])); + } + function storageWrite() external returns (uint) { + data = new bytes(35); + data[31] = 0x77; + data[32] = 0x14; + + data[31] = 0x01; + data[31] |= 0x08; + data[30] = 0x01; + data[32] = 0x03; + return uint(uint8(data[30])) * 0x100 | uint(uint8(data[31])) * 0x10 | uint(uint8(data[32])); + } +} +// ==== +// compileViaYul: also +// ---- +// direct(bytes,uint256): 0x40, 33, 34, 0x000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F, left(0x2021) -> 0x21 +// storageCopyRead(bytes,uint256): 0x40, 33, 34, 0x000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F, left(0x2021) -> 0x21 +// storageWrite() -> 0x193 diff --git a/test/libsolidity/semanticTests/array/indexAccess/bytes_index_access_memory.sol b/test/libsolidity/semanticTests/array/indexAccess/bytes_index_access_memory.sol new file mode 100644 index 000000000..1c2824d7c --- /dev/null +++ b/test/libsolidity/semanticTests/array/indexAccess/bytes_index_access_memory.sol @@ -0,0 +1,17 @@ +contract Main { + function f(bytes memory _s1, uint i1, uint i2, uint i3) public returns (byte c1, byte c2, byte c3) { + c1 = _s1[i1]; + c2 = intern(_s1, i2); + c3 = internIndirect(_s1)[i3]; + } + function intern(bytes memory _s1, uint i) public returns (byte c) { + return _s1[i]; + } + function internIndirect(bytes memory _s1) public returns (bytes memory) { + return _s1; + } +} +// ==== +// compileViaYul: also +// ---- +// f(bytes,uint256,uint256,uint256): 0x80, 3, 4, 5, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> "d", "e", "f" diff --git a/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol b/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol new file mode 100644 index 000000000..ae380af8e --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol @@ -0,0 +1,28 @@ +contract Base { + uint public m_x; + address[] m_s; + constructor(uint x, address[] memory s) { + m_x = x; + m_s = s; + } + function part(uint i) public returns (address) { + return m_s[i]; + } +} +contract Main is Base { + constructor(address[] memory s, uint x) Base(x, f(s)) {} + function f(address[] memory s) public returns (address[] memory) { + return s; + } +} +contract Creator { + function f(uint x, address[] memory s) public returns (uint r, address ch) { + Main c = new Main(s, x); + r = c.m_x(); + ch = c.part(x); + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256,address[]): 7, 0x40, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 -> 7, 8 diff --git a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol new file mode 100644 index 000000000..35e69bf4e --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol @@ -0,0 +1,28 @@ +contract Base { + uint public m_x; + bytes m_s; + constructor(uint x, bytes memory s) { + m_x = x; + m_s = s; + } + function part(uint i) public returns (byte) { + return m_s[i]; + } +} +contract Main is Base { + constructor(bytes memory s, uint x) Base(x, f(s)) {} + function f(bytes memory s) public returns (bytes memory) { + return s; + } +} +contract Creator { + function f(uint x, bytes memory s) public returns (uint r, byte ch) { + Main c = new Main(s, x); + r = c.m_x(); + ch = c.part(x); + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256,bytes): 7, 0x40, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> 7, "h" diff --git a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol new file mode 100644 index 000000000..1d163d82e --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol @@ -0,0 +1,12 @@ +contract Test { + uint public m_x; + bytes public m_s; + constructor(uint x, bytes memory s) { + m_x = x; + m_s = s; + } +} +// ---- +// constructor(): 7, 0x40, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> +// m_x() -> 7 +// m_s() -> 0x20, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" From 738769db4c79f82c2010f74b11563b6ecd71aaa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 13 Nov 2020 23:37:29 +0100 Subject: [PATCH 12/29] cmdlineTest.sh: Fix regex for bytecode removal removing only every other piece between two references - Matched fragments must not overlap for the regex to work correctly. --- test/cmdlineTests.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index aa70e6d47..90e2d6520 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -118,7 +118,7 @@ function test_solc_behaviour() # Remove bytecode (but not linker references). sed -i.bak -E -e 's/(\"object\":\")[0-9a-f]+([^"]*\")/\1\2/g' "$stdout_path" sed -i.bak -E -e 's/(\"object\":\"[^"]+\$__)[0-9a-f]+(\")/\1\2/g' "$stdout_path" - sed -i.bak -E -e 's/(__\$[0-9a-f]{34}\$__)[0-9a-f]+(__\$[0-9a-f]{34}\$__)/\1\2/g' "$stdout_path" + sed -i.bak -E -e 's/([0-9a-f]{34}\$__)[0-9a-f]+(__\$[0-9a-f]{17})/\1\2/g' "$stdout_path" # Replace escaped newlines by actual newlines for readability sed -i.bak -E -e 's/\\n/\'$'\n/g' "$stdout_path" @@ -137,7 +137,7 @@ function test_solc_behaviour() # 64697066735822 = hex encoding of 0x64 'i' 'p' 'f' 's' 0x58 0x22 # 64736f6c63 = hex encoding of 0x64 's' 'o' 'l' 'c' sed -i.bak -E -e 's/[0-9a-f]*64697066735822[0-9a-f]+64736f6c63[0-9a-f]+//g' "$stdout_path" - sed -i.bak -E -e 's/(__\$[0-9a-f]{34}\$__)[0-9a-f]+(__\$[0-9a-f]{34}\$__)/\1\2/g' "$stdout_path" + sed -i.bak -E -e 's/([0-9a-f]{17}\$__)[0-9a-f]+(__\$[0-9a-f]{17})/\1\2/g' "$stdout_path" sed -i.bak -E -e 's/[0-9a-f]+((__\$[0-9a-f]{34}\$__)*)/\1/g' "$stdout_path" # Remove trailing empty lines. Needs a line break to make OSX sed happy. From 9f67d26a1ae83e8681c628e0dc8475c2e3cc8519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sat, 14 Nov 2020 02:58:13 +0100 Subject: [PATCH 13/29] CommandLineInterface: Fix output directory creation for paths ending with a slash and equivalent but not equal to dot or double dot --- Changelog.md | 1 + solc/CommandLineInterface.cpp | 15 +++++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Changelog.md b/Changelog.md index 002276b6d..a3b765391 100644 --- a/Changelog.md +++ b/Changelog.md @@ -27,6 +27,7 @@ Bugfixes: * SMTChecker: Fix false negative in modifier applied multiple times. * SMTChecker: Fix internal error in the BMC engine when inherited contract from a different source unit has private state variables. * SMTChecker: Fix internal error when ``array.push()`` is used as the LHS of an assignment. + * Command Line Interface: Fix write error when the directory passed to ``--output-dir`` ends with a slash. * SMTChecker: Fix CHC false positives when branches are used inside modifiers. * Code generator: Fix missing creation dependency tracking for abstract contracts. diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index e1c98dcef..aabe0be53 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -730,12 +730,15 @@ map CommandLineInterface::parseAstFromInput() void CommandLineInterface::createFile(string const& _fileName, string const& _data) { namespace fs = boost::filesystem; - // create directory if not existent - fs::path p(m_args.at(g_argOutputDir).as()); - // Do not try creating the directory if the first item is . or .. - if (p.filename() != "." && p.filename() != "..") - fs::create_directories(p); - string pathName = (p / _fileName).string(); + + fs::path outputDir(m_args.at(g_argOutputDir).as()); + + // NOTE: create_directories() raises an exception if the path consists solely of '.' or '..' + // (or equivalent such as './././.'). Paths like 'a/b/.' and 'a/b/..' are fine though. + // The simplest workaround is to use an absolute path. + fs::create_directories(fs::absolute(outputDir)); + + string pathName = (outputDir / _fileName).string(); if (fs::exists(pathName) && !m_args.count(g_strOverwrite)) { serr() << "Refusing to overwrite existing file \"" << pathName << "\" (use --" << g_strOverwrite << " to force)." << endl; From 543f804226aee2189d9ab23bf2823cb4949d1e66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 13 Nov 2020 21:52:17 +0100 Subject: [PATCH 14/29] StandardCompiler: Add helpers to streamline comparing link references in tests --- test/libsolidity/StandardCompiler.cpp | 60 ++++++++++++++++++++------- 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 37539d712..c2dfcb45d 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -86,6 +86,48 @@ Json::Value getContractResult(Json::Value const& _compilerResult, string const& return _compilerResult["contracts"][_file][_name]; } +void checkLinkReferencesSchema(Json::Value const& _contractResult) +{ + BOOST_TEST_REQUIRE(_contractResult.isObject()); + BOOST_TEST_REQUIRE(_contractResult["evm"]["bytecode"].isObject()); + + Json::Value const& linkReferenceResult = _contractResult["evm"]["bytecode"]["linkReferences"]; + BOOST_TEST_REQUIRE(linkReferenceResult.isObject()); + + for (string const& fileName: linkReferenceResult.getMemberNames()) + { + BOOST_TEST_REQUIRE(linkReferenceResult[fileName].isObject()); + for (string const& libraryName: linkReferenceResult[fileName].getMemberNames()) + { + BOOST_TEST_REQUIRE(linkReferenceResult[fileName][libraryName].isArray()); + BOOST_TEST_REQUIRE(!linkReferenceResult[fileName][libraryName].empty()); + for (int i = 0; i < static_cast(linkReferenceResult.size()); ++i) + { + BOOST_TEST_REQUIRE(linkReferenceResult[fileName][libraryName][i].isObject()); + BOOST_TEST_REQUIRE(linkReferenceResult[fileName][libraryName][i].size() == 2); + BOOST_TEST_REQUIRE(linkReferenceResult[fileName][libraryName][i]["length"].isUInt()); + BOOST_TEST_REQUIRE(linkReferenceResult[fileName][libraryName][i]["start"].isUInt()); + } + } + } +} + +void expectLinkReferences(Json::Value const& _contractResult, map> const& _expectedLinkReferences) +{ + checkLinkReferencesSchema(_contractResult); + + Json::Value const& linkReferenceResult = _contractResult["evm"]["bytecode"]["linkReferences"]; + BOOST_TEST(linkReferenceResult.size() == _expectedLinkReferences.size()); + + for (auto const& [fileName, libraries]: _expectedLinkReferences) + { + BOOST_TEST(linkReferenceResult.isMember(fileName)); + BOOST_TEST(linkReferenceResult[fileName].size() == libraries.size()); + for (string const& libraryName: libraries) + BOOST_TEST(linkReferenceResult[fileName].isMember(libraryName)); + } +} + Json::Value compile(string _input) { StandardCompiler compiler; @@ -710,11 +752,7 @@ BOOST_AUTO_TEST_CASE(library_filename_with_colon) BOOST_CHECK(containsAtMostWarnings(result)); Json::Value contract = getContractResult(result, "fileA", "A"); BOOST_CHECK(contract.isObject()); - BOOST_CHECK(contract["evm"]["bytecode"].isObject()); - BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"].isObject()); - BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["git:library.sol"].isObject()); - BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["git:library.sol"]["L"].isArray()); - BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["git:library.sol"]["L"][0].isObject()); + expectLinkReferences(contract, {{"git:library.sol", {"L"}}}); } BOOST_AUTO_TEST_CASE(libraries_invalid_top_level) @@ -860,15 +898,9 @@ BOOST_AUTO_TEST_CASE(library_linking) } )"; Json::Value result = compile(input); - BOOST_CHECK(containsAtMostWarnings(result)); - Json::Value contract = getContractResult(result, "fileA", "A"); - BOOST_CHECK(contract.isObject()); - BOOST_CHECK(contract["evm"]["bytecode"].isObject()); - BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"].isObject()); - BOOST_CHECK(!contract["evm"]["bytecode"]["linkReferences"]["library.sol"].isObject()); - BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["library2.sol"].isObject()); - BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["library2.sol"]["L2"].isArray()); - BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["library2.sol"]["L2"][0].isObject()); + BOOST_TEST(containsAtMostWarnings(result)); + Json::Value contractResult = getContractResult(result, "fileA", "A"); + expectLinkReferences(contractResult, {{"library2.sol", {"L2"}}}); } BOOST_AUTO_TEST_CASE(evm_version) From b97c6c55adf6e86c59383c0fcad73bf04951e0ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 13 Nov 2020 14:15:59 +0100 Subject: [PATCH 15/29] StandardCompiler: Don't assume that link reference always contains a colon --- libsolidity/interface/StandardCompiler.cpp | 8 ++- .../args | 1 + .../err | 1 + .../input.yul | 5 ++ .../output | 16 +++++ test/libsolidity/StandardCompiler.cpp | 64 +++++++++++++++++++ 6 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/args create mode 100644 test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/err create mode 100644 test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/input.yul create mode 100644 test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/output diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 5758c4994..86587839d 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -323,10 +323,12 @@ Json::Value formatLinkReferences(std::map const& linkRefere for (auto const& ref: linkReferences) { string const& fullname = ref.second; + + // If the link reference does not contain a colon, assume that the file name is missing and + // the whole string represents the library name. size_t colon = fullname.rfind(':'); - solAssert(colon != string::npos, ""); - string file = fullname.substr(0, colon); - string name = fullname.substr(colon + 1); + string file = (colon != string::npos ? fullname.substr(0, colon) : ""); + string name = (colon != string::npos ? fullname.substr(colon + 1) : fullname); Json::Value fileObject = ret.get(file, Json::objectValue); Json::Value libraryArray = fileObject.get(name, Json::arrayValue); diff --git a/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/args b/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/args new file mode 100644 index 000000000..57ee5078b --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/args @@ -0,0 +1 @@ +--strict-assembly --libraries L:0x1234567890123456789012345678901234567890 diff --git a/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/err b/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/err new file mode 100644 index 000000000..014a1178f --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/err @@ -0,0 +1 @@ +Warning: Yul is still experimental. Please use the output with care. diff --git a/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/input.yul b/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/input.yul new file mode 100644 index 000000000..b4dbc3cbc --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/input.yul @@ -0,0 +1,5 @@ +object "a" { + code { + let addr := linkersymbol("L") + } +} diff --git a/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/output b/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/output new file mode 100644 index 000000000..c7a9339b6 --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/output @@ -0,0 +1,16 @@ + +======= linking_strict_assembly_no_file_name_in_link_reference/input.yul (EVM) ======= + +Pretty printed source: +object "a" { + code { let addr := linkersymbol("L") } +} + + +Binary representation: +73123456789012345678901234567890123456789050 + +Text representation: + linkerSymbol("8aa64f937099b65a4febc243a5ae0f2d6416bb9e473c30dd29c1ee498fb7c5a8") + /* "linking_strict_assembly_no_file_name_in_link_reference/input.yul":22:67 */ + pop diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index c2dfcb45d..270295c3c 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -903,6 +903,70 @@ BOOST_AUTO_TEST_CASE(library_linking) expectLinkReferences(contractResult, {{"library2.sol", {"L2"}}}); } +BOOST_AUTO_TEST_CASE(linking_yul_empty_link_reference) +{ + char const* input = R"( + { + "language": "Yul", + "settings": { + "libraries": { + "": { + "": "0x4200000000000000000000000000000000000001" + } + }, + "outputSelection": { + "fileA": { + "*": [ + "evm.bytecode.linkReferences" + ] + } + } + }, + "sources": { + "fileA": { + "content": "object \"a\" { code { let addr := linkersymbol(\"\") } }" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_TEST(containsAtMostWarnings(result)); + Json::Value contractResult = getContractResult(result, "fileA", "a"); + expectLinkReferences(contractResult, {}); +} + +BOOST_AUTO_TEST_CASE(linking_yul_no_filename_in_link_reference) +{ + char const* input = R"( + { + "language": "Yul", + "settings": { + "libraries": { + "": { + "L": "0x4200000000000000000000000000000000000001" + } + }, + "outputSelection": { + "fileA": { + "*": [ + "evm.bytecode.linkReferences" + ] + } + } + }, + "sources": { + "fileA": { + "content": "object \"a\" { code { let addr := linkersymbol(\"L\") } }" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_TEST(containsAtMostWarnings(result)); + Json::Value contractResult = getContractResult(result, "fileA", "a"); + expectLinkReferences(contractResult, {}); +} + BOOST_AUTO_TEST_CASE(evm_version) { auto inputForVersion = [](string const& _version) From 148c379ab9754e34c34e2cb9b5f3f0aae655205e Mon Sep 17 00:00:00 2001 From: a3d4 Date: Mon, 16 Nov 2020 17:42:51 +0100 Subject: [PATCH 16/29] Fix Visual Studio compilation error (add missing include ) --- libsmtutil/SolverInterface.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libsmtutil/SolverInterface.h b/libsmtutil/SolverInterface.h index 8dd73b035..4e9cf2258 100644 --- a/libsmtutil/SolverInterface.h +++ b/libsmtutil/SolverInterface.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include From 62893aa1a1088d83e051d416dc8fdd7d94cb498b Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 12 Nov 2020 11:54:37 +0100 Subject: [PATCH 17/29] Copy byte arrays from storage to storage. --- libsolidity/codegen/YulUtilFunctions.cpp | 235 +++++++++--------- test/cmdlineTests.sh | 2 +- test/cmdlineTests/yul_unimplemented/err | 6 +- test/cmdlineTests/yul_unimplemented/input.sol | 7 +- .../array/copying/bytes_inside_mappings.sol | 2 + .../array/copying/copy_removes_bytes_data.sol | 2 + .../array/string_bytes_conversion.sol | 2 + 7 files changed, 132 insertions(+), 124 deletions(-) diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 602d1723f..075f9ca32 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1571,12 +1571,13 @@ string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromTy ); solAssert(_fromType.isByteArray(), ""); solAssert(_toType.isByteArray(), ""); - solUnimplementedAssert(!_fromType.dataStoredIn(DataLocation::Storage), ""); string functionName = "copy_byte_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier(); return m_functionCollector.createFunction(functionName, [&](){ Whiskers templ(R"( function (slot, src, len) { + if eq(slot, src) { leave } + let newLen := (src, len) // Make sure array length is sane if gt(newLen, 0xffffffffffffffff) { () } @@ -1605,11 +1606,11 @@ string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromTy let dstPtr := dstDataArea let i := 0 for { } lt(i, loopEnd) { i := add(i, 32) } { - sstore(dstPtr, (add(src, i))) + sstore(dstPtr, (add(src, i))) dstPtr := add(dstPtr, 1) } if lt(loopEnd, newLen) { - let lastValue := (add(src, i)) + let lastValue := (add(src, i)) sstore(dstPtr, (lastValue, and(newLen, 0x1f))) } sstore(slot, add(mul(newLen, 2), 1)) @@ -1617,13 +1618,15 @@ string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromTy default { let value := 0 if newLen { - value := (src) + value := (src) } sstore(slot, (value, newLen)) } } )"); templ("functionName", functionName); + bool fromStorage = _fromType.dataStoredIn(DataLocation::Storage); + templ("fromStorage", fromStorage); bool fromCalldata = _fromType.dataStoredIn(DataLocation::CallData); templ("fromMemory", _fromType.dataStoredIn(DataLocation::Memory)); templ("fromCalldata", fromCalldata); @@ -1632,7 +1635,7 @@ string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromTy templ("byteArrayLength", extractByteArrayLengthFunction()); templ("dstDataLocation", arrayDataAreaFunction(_toType)); templ("clearStorageRange", clearStorageRangeFunction(*_toType.baseType())); - templ("readFromCalldataOrMemory", readFromMemoryOrCalldata(*TypeProvider::uint256(), fromCalldata)); + templ("read", fromStorage ? "sload" : fromCalldata ? "calldataload" : "mload"); templ("maskBytes", maskBytesFunctionDynamic()); templ("byteArrayCombineShort", shortByteArrayEncodeUsedAreaSetLengthFunction()); @@ -2200,132 +2203,130 @@ string YulUtilFunctions::updateStorageValueFunction( ("prepare", prepareStoreFunction(_toType)) .render(); } + + auto const* toReferenceType = dynamic_cast(&_toType); + auto const* fromReferenceType = dynamic_cast(&_fromType); + solAssert(fromReferenceType && toReferenceType, ""); + solAssert(*toReferenceType->copyForLocation( + fromReferenceType->location(), + fromReferenceType->isPointer() + ).get() == *fromReferenceType, ""); + solAssert(toReferenceType->category() == fromReferenceType->category(), ""); + + if (_toType.category() == Type::Category::Array) + { + solAssert(_offset.value_or(0) == 0, ""); + + Whiskers templ(R"( + function (slot, ) { + (slot, ) + } + )"); + templ("functionName", functionName); + templ("value", suffixedVariableNameList("value_", 0, _fromType.sizeOnStack())); + templ("copyArrayToStorage", copyArrayToStorageFunction( + dynamic_cast(_fromType), + dynamic_cast(_toType) + )); + + return templ.render(); + } else { - auto const* toReferenceType = dynamic_cast(&_toType); - auto const* fromReferenceType = dynamic_cast(&_fromType); - solAssert(fromReferenceType && toReferenceType, ""); - solAssert(*toReferenceType->copyForLocation( - fromReferenceType->location(), - fromReferenceType->isPointer() - ).get() == *fromReferenceType, ""); + solAssert(_toType.category() == Type::Category::Struct, ""); + solUnimplementedAssert( fromReferenceType->location() != DataLocation::Storage, "Copying from storage to storage is not yet implemented." ); - solAssert(toReferenceType->category() == fromReferenceType->category(), ""); + auto const& fromStructType = dynamic_cast(_fromType); + auto const& toStructType = dynamic_cast(_toType); + solAssert(fromStructType.structDefinition() == toStructType.structDefinition(), ""); + solAssert(_offset.value_or(0) == 0, ""); - if (_toType.category() == Type::Category::Array) - { - solAssert(_offset.value_or(0) == 0, ""); - - Whiskers templ(R"( - function (slot, ) { - (slot, ) - } - )"); - templ("functionName", functionName); - templ("value", suffixedVariableNameList("value_", 0, _fromType.sizeOnStack())); - templ("copyArrayToStorage", copyArrayToStorageFunction( - dynamic_cast(_fromType), - dynamic_cast(_toType) - )); - - return templ.render(); - } - else if (_toType.category() == Type::Category::Struct) - { - auto const& fromStructType = dynamic_cast(_fromType); - auto const& toStructType = dynamic_cast(_toType); - solAssert(fromStructType.structDefinition() == toStructType.structDefinition(), ""); - solAssert(_offset.value_or(0) == 0, ""); - - Whiskers templ(R"( - function (slot, value) { - <#member> - { - - } - - } - )"); - templ("functionName", functionName); - - MemberList::MemberMap structMembers = fromStructType.nativeMembers(nullptr); - MemberList::MemberMap toStructMembers = toStructType.nativeMembers(nullptr); - - vector> memberParams(structMembers.size()); - for (size_t i = 0; i < structMembers.size(); ++i) - { - solAssert(structMembers[i].type->memoryHeadSize() == 32, ""); - bool fromCalldata = fromStructType.location() == DataLocation::CallData; - auto const& [slotDiff, offset] = toStructType.storageOffsetsOfMember(structMembers[i].name); - - Whiskers t(R"( - let memberSlot := add(slot, ) - - - - let := (value, add(value, )) - - let := add(value, ) - - - - let := () - (memberSlot, , ) - - (memberSlot, ) - - - let memberMemoryOffset := add(value, ) - let := (memberMemoryOffset) - (memberSlot, , ) - - )"); - t("fromCalldata", fromCalldata); - if (fromCalldata) + Whiskers templ(R"( + function (slot, value) { + <#member> { - t("memberCalldataOffset", suffixedVariableNameList( - "memberCalldataOffset_", - 0, - structMembers[i].type->stackItems().size() - )); - t("dynamicallyEncodedMember", structMembers[i].type->isDynamicallyEncoded()); - if (structMembers[i].type->isDynamicallyEncoded()) - t("accessCalldataTail", accessCalldataTailFunction(*structMembers[i].type)); + } - t("isValueType", structMembers[i].type->isValueType()); - t("memberValues", suffixedVariableNameList( - "memberValue_", + + } + )"); + templ("functionName", functionName); + + MemberList::MemberMap structMembers = fromStructType.nativeMembers(nullptr); + MemberList::MemberMap toStructMembers = toStructType.nativeMembers(nullptr); + + vector> memberParams(structMembers.size()); + for (size_t i = 0; i < structMembers.size(); ++i) + { + solAssert(structMembers[i].type->memoryHeadSize() == 32, ""); + bool fromCalldata = fromStructType.location() == DataLocation::CallData; + auto const& [slotDiff, offset] = toStructType.storageOffsetsOfMember(structMembers[i].name); + + Whiskers t(R"( + let memberSlot := add(slot, ) + + + + let := (value, add(value, )) + + let := add(value, ) + + + + let := () + (memberSlot, , ) + + (memberSlot, ) + + + let memberMemoryOffset := add(value, ) + let := (memberMemoryOffset) + (memberSlot, , ) + + )"); + t("fromCalldata", fromCalldata); + if (fromCalldata) + { + t("memberCalldataOffset", suffixedVariableNameList( + "memberCalldataOffset_", 0, structMembers[i].type->stackItems().size() )); - t("hasOffset", structMembers[i].type->isValueType()); - t( - "updateMember", - structMembers[i].type->isValueType() ? - updateStorageValueFunction(*structMembers[i].type, *toStructMembers[i].type) : - updateStorageValueFunction(*structMembers[i].type, *toStructMembers[i].type, offset) - ); - t("memberStorageSlotDiff", slotDiff.str()); - t("memberStorageOffset", to_string(offset)); - t( - "memberOffset", - fromCalldata ? - to_string(fromStructType.calldataOffsetOfMember(structMembers[i].name)) : - fromStructType.memoryOffsetOfMember(structMembers[i].name).str() - ); - if (!fromCalldata || structMembers[i].type->isValueType()) - t("loadFromMemoryOrCalldata", readFromMemoryOrCalldata(*structMembers[i].type, fromCalldata)); - memberParams[i]["updateMemberCall"] = t.render(); + t("dynamicallyEncodedMember", structMembers[i].type->isDynamicallyEncoded()); + if (structMembers[i].type->isDynamicallyEncoded()) + t("accessCalldataTail", accessCalldataTailFunction(*structMembers[i].type)); } - templ("member", memberParams); - - return templ.render(); + t("isValueType", structMembers[i].type->isValueType()); + t("memberValues", suffixedVariableNameList( + "memberValue_", + 0, + structMembers[i].type->stackItems().size() + )); + t("hasOffset", structMembers[i].type->isValueType()); + t( + "updateMember", + structMembers[i].type->isValueType() ? + updateStorageValueFunction(*structMembers[i].type, *toStructMembers[i].type) : + updateStorageValueFunction(*structMembers[i].type, *toStructMembers[i].type, offset) + ); + t("memberStorageSlotDiff", slotDiff.str()); + t("memberStorageOffset", to_string(offset)); + t( + "memberOffset", + fromCalldata ? + to_string(fromStructType.calldataOffsetOfMember(structMembers[i].name)) : + fromStructType.memoryOffsetOfMember(structMembers[i].name).str() + ); + if (!fromCalldata || structMembers[i].type->isValueType()) + t("loadFromMemoryOrCalldata", readFromMemoryOrCalldata(*structMembers[i].type, fromCalldata)); + memberParams[i]["updateMemberCall"] = t.render(); } - else - solAssert(false, "Invalid non-value type for assignment."); + templ("member", memberParams); + + return templ.render(); } }); } diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index aa70e6d47..4e6051fc2 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -128,7 +128,7 @@ function test_solc_behaviour() sed -i.bak -e '/^Warning (3805): This is a pre-release compiler version, please do not use it in production./d' "$stderr_path" sed -i.bak -e 's/\(^[ ]*auxdata: \)0x[0-9a-f]*$/\1/' "$stdout_path" sed -i.bak -e 's/ Consider adding "pragma .*$//' "$stderr_path" - sed -i.bak -e 's/\(Unimplemented feature error: .* in \).*$/\1/' "$stderr_path" + sed -i.bak -e 's/\(Unimplemented feature error.* in \).*$/\1/' "$stderr_path" sed -i.bak -e 's/"version": "[^"]*"/"version": ""/' "$stdout_path" # Remove bytecode (but not linker references). Since non-JSON output is unstructured, diff --git a/test/cmdlineTests/yul_unimplemented/err b/test/cmdlineTests/yul_unimplemented/err index 908cf4411..dd3572497 100644 --- a/test/cmdlineTests/yul_unimplemented/err +++ b/test/cmdlineTests/yul_unimplemented/err @@ -1,5 +1,5 @@ -Error (1834): Unimplemented feature error: Copying from storage to storage is not yet implemented. in - --> yul_unimplemented/input.sol:7:9: +Error (1834): Unimplemented feature error in + --> yul_unimplemented/input.sol:8:9: | -7 | a = b; +8 | x.f(); | ^^^^^ diff --git a/test/cmdlineTests/yul_unimplemented/input.sol b/test/cmdlineTests/yul_unimplemented/input.sol index 1ed933ade..811f3e4a0 100644 --- a/test/cmdlineTests/yul_unimplemented/input.sol +++ b/test/cmdlineTests/yul_unimplemented/input.sol @@ -1,9 +1,10 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.0; +library L { function f(uint) public {} } contract test { - bytes a; - bytes b; + using L for uint; function f() public { - a = b; + uint x; + x.f(); } } \ No newline at end of file diff --git a/test/libsolidity/semanticTests/array/copying/bytes_inside_mappings.sol b/test/libsolidity/semanticTests/array/copying/bytes_inside_mappings.sol index 21b5e9c5d..2decb1562 100644 --- a/test/libsolidity/semanticTests/array/copying/bytes_inside_mappings.sol +++ b/test/libsolidity/semanticTests/array/copying/bytes_inside_mappings.sol @@ -3,6 +3,8 @@ contract c { function copy(uint from, uint to) public returns (bool) { data[to] = data[from]; return true; } mapping(uint => bytes) data; } +// ==== +// compileViaYul: also // ---- // set(uint256): 1, 2 -> true // set(uint256): 2, 2, 3, 4, 5 -> true diff --git a/test/libsolidity/semanticTests/array/copying/copy_removes_bytes_data.sol b/test/libsolidity/semanticTests/array/copying/copy_removes_bytes_data.sol index bb97ae2c1..ace34e872 100644 --- a/test/libsolidity/semanticTests/array/copying/copy_removes_bytes_data.sol +++ b/test/libsolidity/semanticTests/array/copying/copy_removes_bytes_data.sol @@ -5,6 +5,8 @@ contract c { bytes data1; bytes data2; } +// ==== +// compileViaYul: also // ---- // set(): 1, 2, 3, 4, 5 -> true // storage: nonempty diff --git a/test/libsolidity/semanticTests/array/string_bytes_conversion.sol b/test/libsolidity/semanticTests/array/string_bytes_conversion.sol index 9578fc4f7..5f6e73422 100644 --- a/test/libsolidity/semanticTests/array/string_bytes_conversion.sol +++ b/test/libsolidity/semanticTests/array/string_bytes_conversion.sol @@ -12,6 +12,8 @@ contract Test { return bytes(s).length; } } +// ==== +// compileViaYul: also // ---- // f(string,uint256): 0x40, 0x02, 0x06, "abcdef" -> "c" // l() -> 0x06 From 1a4d38c0ac49e867012047b3187ae3f152b2117d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 13 Nov 2020 22:31:04 +0100 Subject: [PATCH 18/29] CommandLineInterface: Report an error if library appears multiple times in the --libraries option --- Changelog.md | 1 + solc/CommandLineInterface.cpp | 9 +++++++- .../args | 1 + .../err | 1 + .../exit | 1 + .../input.yul | 5 +++++ .../args | 1 + .../err | 1 + .../input.yul | 6 +++++ .../output | 22 +++++++++++++++++++ 10 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 test/cmdlineTests/linking_strict_assembly_duplicate_library_name/args create mode 100644 test/cmdlineTests/linking_strict_assembly_duplicate_library_name/err create mode 100644 test/cmdlineTests/linking_strict_assembly_duplicate_library_name/exit create mode 100644 test/cmdlineTests/linking_strict_assembly_duplicate_library_name/input.yul create mode 100644 test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/args create mode 100644 test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/err create mode 100644 test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/input.yul create mode 100644 test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/output diff --git a/Changelog.md b/Changelog.md index 002276b6d..8ea4e635b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -19,6 +19,7 @@ Compiler Features: Bugfixes: + * Command Line Interface: Reject duplicate libraries in ``--libraries`` option instead of arbitrarily choosing one. * SMTChecker: Fix lack of reporting potential violations when using only the CHC engine. * SMTChecker: Fix internal error on conversion from string literal to byte. * SMTChecker: Fix internal error when using tuples of rational literals inside the conditional operator. diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index e1c98dcef..1c1061535 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -664,9 +664,16 @@ bool CommandLineInterface::parseLibraryOption(string const& _input) serr() << "Colon separator missing in library address specifier \"" << lib << "\"" << endl; return false; } + string libName(lib.begin(), lib.begin() + static_cast(colon)); - string addrString(lib.begin() + static_cast(colon) + 1, lib.end()); boost::trim(libName); + if (m_libraries.count(libName)) + { + serr() << "Address specified more than once for library \"" << libName << "\"." << endl; + return false; + } + + string addrString(lib.begin() + static_cast(colon) + 1, lib.end()); boost::trim(addrString); if (addrString.substr(0, 2) == "0x") addrString = addrString.substr(2); diff --git a/test/cmdlineTests/linking_strict_assembly_duplicate_library_name/args b/test/cmdlineTests/linking_strict_assembly_duplicate_library_name/args new file mode 100644 index 000000000..54be2478a --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_duplicate_library_name/args @@ -0,0 +1 @@ +--strict-assembly --libraries library.sol:L:0x1234567890123456789012345678901234567890,library.sol:L:0x0987654321098765432109876543210987654321 diff --git a/test/cmdlineTests/linking_strict_assembly_duplicate_library_name/err b/test/cmdlineTests/linking_strict_assembly_duplicate_library_name/err new file mode 100644 index 000000000..973e9e06e --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_duplicate_library_name/err @@ -0,0 +1 @@ +Address specified more than once for library "library.sol:L". diff --git a/test/cmdlineTests/linking_strict_assembly_duplicate_library_name/exit b/test/cmdlineTests/linking_strict_assembly_duplicate_library_name/exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_duplicate_library_name/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/linking_strict_assembly_duplicate_library_name/input.yul b/test/cmdlineTests/linking_strict_assembly_duplicate_library_name/input.yul new file mode 100644 index 000000000..c4dda1ceb --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_duplicate_library_name/input.yul @@ -0,0 +1,5 @@ +object "a" { + code { + let addr := linkersymbol("library.sol:L") + } +} diff --git a/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/args b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/args new file mode 100644 index 000000000..a424a337a --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/args @@ -0,0 +1 @@ +--strict-assembly --libraries library1.sol:L:0x1111111111111111111111111111111111111111,library2.sol:L:0x2222222222222222222222222222222222222222 diff --git a/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/err b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/err new file mode 100644 index 000000000..014a1178f --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/err @@ -0,0 +1 @@ +Warning: Yul is still experimental. Please use the output with care. diff --git a/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/input.yul b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/input.yul new file mode 100644 index 000000000..acb612857 --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/input.yul @@ -0,0 +1,6 @@ +object "a" { + code { + let addr1 := linkersymbol("library1.sol:L") + let addr2 := linkersymbol("library2.sol:L") + } +} diff --git a/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/output b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/output new file mode 100644 index 000000000..80696c594 --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/output @@ -0,0 +1,22 @@ + +======= linking_strict_assembly_same_library_name_different_files/input.yul (EVM) ======= + +Pretty printed source: +object "a" { + code { + let addr1 := linkersymbol("library1.sol:L") + let addr2 := linkersymbol("library2.sol:L") + } +} + + +Binary representation: +7311111111111111111111111111111111111111117322222222222222222222222222222222222222225050 + +Text representation: + linkerSymbol("f3ffc10c396a7cc41ae954b050792839d20947bf73497d30c49a9fda1ea477ec") + /* "linking_strict_assembly_same_library_name_different_files/input.yul":32:75 */ + linkerSymbol("c3523432985587641d17c68161d2f700c57aaf4ed21cda4f25d76193c831f97f") + /* "linking_strict_assembly_same_library_name_different_files/input.yul":22:133 */ + pop + pop From aba04c7afa231c05dc7a86227f5dbb4758f30e44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sat, 14 Nov 2020 00:48:40 +0100 Subject: [PATCH 19/29] docs: Remove the outdated information about linked library name length limitation --- docs/contracts/libraries.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/contracts/libraries.rst b/docs/contracts/libraries.rst index 5630c7212..035bb36e1 100644 --- a/docs/contracts/libraries.rst +++ b/docs/contracts/libraries.rst @@ -196,8 +196,6 @@ manually by replacing all those 40 symbols by the hex encoding of the address of the library contract. .. note:: - Manually linking libraries on the generated bytecode is discouraged, because - in this way, the library name is restricted to 36 characters. You should ask the compiler to link the libraries at the time a contract is compiled by either using the ``--libraries`` option of ``solc`` or the ``libraries`` key if you use From 1ff00488bb6102a39b2ef902622255e4997dd20c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sat, 14 Nov 2020 00:50:19 +0100 Subject: [PATCH 20/29] docs: Add information about metadata difference resulting from manual linking --- docs/contracts/libraries.rst | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/contracts/libraries.rst b/docs/contracts/libraries.rst index 035bb36e1..6da52776a 100644 --- a/docs/contracts/libraries.rst +++ b/docs/contracts/libraries.rst @@ -196,10 +196,14 @@ manually by replacing all those 40 symbols by the hex encoding of the address of the library contract. .. note:: - You should ask the compiler to link the libraries at the time - a contract is compiled by either using - the ``--libraries`` option of ``solc`` or the ``libraries`` key if you use - the standard-JSON interface to the compiler. + Manually linking libraries on the generated bytecode is discouraged because it does not update + contract metadata. Since metadata contains a list of libraries specified at the time of + compilation and bytecode contains a metadata hash, you will get different binaries, depending + on when linking is performed. + + You should ask the compiler to link the libraries at the time a contract is compiled by either + using the ``--libraries`` option of ``solc`` or the ``libraries`` key if you use the + standard-JSON interface to the compiler. In comparison to contracts, libraries are restricted in the following ways: From b7694b9a147900ee3bf1eb99679a8687305a5a18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sat, 14 Nov 2020 01:20:38 +0100 Subject: [PATCH 21/29] docs: Gather information about linker in one place and reorganize the section a bit --- docs/contracts/libraries.rst | 31 ++++++++++--------------------- docs/using-the-compiler.rst | 30 +++++++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 24 deletions(-) diff --git a/docs/contracts/libraries.rst b/docs/contracts/libraries.rst index 6da52776a..d3b9ef226 100644 --- a/docs/contracts/libraries.rst +++ b/docs/contracts/libraries.rst @@ -184,26 +184,15 @@ custom types without the overhead of external function calls: It is possible to obtain the address of a library by converting the library type to the ``address`` type, i.e. using ``address(LibraryName)``. -As the compiler cannot know where the library will be -deployed at, these addresses have to be filled into the -final bytecode by a linker -(see :ref:`commandline-compiler` for how to use the -commandline compiler for linking). If the addresses are not -given as arguments to the compiler, the compiled hex code -will contain placeholders of the form ``__Set______`` (where -``Set`` is the name of the library). The address can be filled -manually by replacing all those 40 symbols by the hex -encoding of the address of the library contract. - -.. note:: - Manually linking libraries on the generated bytecode is discouraged because it does not update - contract metadata. Since metadata contains a list of libraries specified at the time of - compilation and bytecode contains a metadata hash, you will get different binaries, depending - on when linking is performed. - - You should ask the compiler to link the libraries at the time a contract is compiled by either - using the ``--libraries`` option of ``solc`` or the ``libraries`` key if you use the - standard-JSON interface to the compiler. +As the compiler does not know the address where the library will be deployed, the compiled hex code +will contain placeholders of the form ``__$30bbc0abd4d6364515865950d3e0d10953$__``. The placeholder +is a 34 character prefix of the hex encoding of the keccak256 hash of the fully qualified library +name, which would be for example ``libraries/bigint.sol:BigInt`` if the library was stored in a file +called ``bigint.sol`` in a ``libraries/`` directory. Such bytecode is incomplete and should not be +deployed. Placeholders need to be replaced with actual addresses. You can do that by either passing +them to the compiler when the library is being compiled or by using the linker to update an already +compiled binary. See :ref:`library-linking` for information on how to use the commandline compiler +for linking. In comparison to contracts, libraries are restricted in the following ways: @@ -287,4 +276,4 @@ for any non-view and non-pure function. This means that the actual code stored on chain for a library is different from the code reported by the compiler as -``deployedBytecode``. \ No newline at end of file +``deployedBytecode``. diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 589bf1e29..7bf4cfd5c 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -12,10 +12,16 @@ Using the Commandline Compiler .. note:: This section does not apply to :ref:`solcjs `, not even if it is used in commandline mode. +Basic usage +----------- + One of the build targets of the Solidity repository is ``solc``, the solidity commandline compiler. Using ``solc --help`` provides you with an explanation of all options. The compiler can produce various outputs, ranging from simple binaries and assembly over an abstract syntax tree (parse tree) to estimations of gas usage. If you only want to compile a single file, you run it as ``solc --bin sourceFile.sol`` and it will print the binary. If you want to get some of the more advanced output variants of ``solc``, it is probably better to tell it to output everything to separate files using ``solc -o outputDirectory --bin --ast-json --asm sourceFile.sol``. +Optimizer options +----------------- + Before you deploy your contract, activate the optimizer when compiling using ``solc --optimize --bin sourceFile.sol``. By default, the optimizer will optimize the contract assuming it is called 200 times across its lifetime (more specifically, it assumes each opcode is executed around 200 times). @@ -27,6 +33,9 @@ This parameter has effects on the following (this might change in the future): - the size of the binary search in the function dispatch routine - the way constants like large numbers or strings are stored +Path remapping +-------------- + The commandline compiler will automatically read imported files from the filesystem, but it is also possible to provide path redirects using ``prefix=path`` in the following way: @@ -53,6 +62,11 @@ For security reasons the compiler has restrictions what directories it can acces Everything inside the path specified via ``--base-path`` is always allowed. +.. _library-linking: + +Library linking +--------------- + If your contracts use :ref:`libraries `, you will notice that the bytecode contains substrings of the form ``__$53aea86b7d70b31448b230b20ae141a537$__``. These are placeholders for the actual library addresses. The placeholder is a 34 character prefix of the hex encoding of the keccak256 hash of the fully qualified library name. The bytecode file will also contain lines of the form ``// -> `` at the end to help @@ -60,13 +74,23 @@ identify which libraries the placeholders represent. Note that the fully qualifi is the path of its source file and the library name separated by ``:``. You can use ``solc`` as a linker meaning that it will insert the library addresses for you at those points: -Either add ``--libraries "file.sol:Math:0x1234567890123456789012345678901234567890 file.sol:Heap:0xabCD567890123456789012345678901234567890"`` to your command to provide an address for each library or store the string in a file (one library per line) and run ``solc`` using ``--libraries fileName``. - -If ``solc`` is called with the option ``--link``, all input files are interpreted to be unlinked binaries (hex-encoded) in the ``__$53aea86b7d70b31448b230b20ae141a537$__``-format given above and are linked in-place (if the input is read from stdin, it is written to stdout). All options except ``--libraries`` are ignored (including ``-o``) in this case. +Either add ``--libraries "file.sol:Math:0x1234567890123456789012345678901234567890 file.sol:Heap:0xabCD567890123456789012345678901234567890"`` to your command to provide an address for each library (use commas or spaces as separators) or store the string in a file (one library per line) and run ``solc`` using ``--libraries fileName``. If ``solc`` is called with the option ``--standard-json``, it will expect a JSON input (as explained below) on the standard input, and return a JSON output on the standard output. This is the recommended interface for more complex and especially automated uses. The process will always terminate in a "success" state and report any errors via the JSON output. The option ``--base-path`` is also processed in standard-json mode. +If ``solc`` is called with the option ``--link``, all input files are interpreted to be unlinked binaries (hex-encoded) in the ``__$53aea86b7d70b31448b230b20ae141a537$__``-format given above and are linked in-place (if the input is read from stdin, it is written to stdout). All options except ``--libraries`` are ignored (including ``-o``) in this case. + +.. warning:: + Manually linking libraries on the generated bytecode is discouraged because it does not update + contract metadata. Since metadata contains a list of libraries specified at the time of + compilation and bytecode contains a metadata hash, you will get different binaries, depending + on when linking is performed. + + You should ask the compiler to link the libraries at the time a contract is compiled by either + using the ``--libraries`` option of ``solc`` or the ``libraries`` key if you use the + standard-JSON interface to the compiler. + .. note:: The library placeholder used to be the fully qualified name of the library itself instead of the hash of it. This format is still supported by ``solc --link`` but From 07427c798c48061c608b1fded06c12bd296fa563 Mon Sep 17 00:00:00 2001 From: Martin Blicha Date: Mon, 16 Nov 2020 17:52:06 +0100 Subject: [PATCH 22/29] [SMTChecker] Adding a dummy frame to the call stack for the implicit constructor --- libsolidity/formal/BMC.cpp | 43 +++++++++---------- .../bmc_coverage/assert_in_constructor.sol | 21 +++++++++ ...plicit_constructor_with_function_calls.sol | 19 ++++++++ .../smtCheckerTests/bmc_coverage/math.sol | 2 +- 4 files changed, 62 insertions(+), 23 deletions(-) create mode 100644 test/libsolidity/smtCheckerTests/bmc_coverage/assert_in_constructor.sol create mode 100644 test/libsolidity/smtCheckerTests/bmc_coverage/implicit_constructor_with_function_calls.sol diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index b6e05bdd0..ec10b44de 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -120,7 +120,10 @@ void BMC::endVisit(ContractDefinition const& _contract) constructor->accept(*this); else { + /// Visiting implicit constructor - we need a dummy callstack frame + pushCallStack({nullptr, nullptr}); inlineConstructorHierarchy(_contract); + popCallStack(); /// Check targets created by state variable initialization. smtutil::Expression constraints = m_context.assertions(); checkVerificationTargets(constraints); @@ -831,32 +834,28 @@ void BMC::checkCondition( { case smtutil::CheckResult::SATISFIABLE: { + solAssert(!_callStack.empty(), ""); std::ostringstream message; message << "BMC: " << _description << " happens here."; - if (_callStack.size()) - { - std::ostringstream modelMessage; - modelMessage << "Counterexample:\n"; - solAssert(values.size() == expressionNames.size(), ""); - map sortedModel; - for (size_t i = 0; i < values.size(); ++i) - if (expressionsToEvaluate.at(i).name != values.at(i)) - sortedModel[expressionNames.at(i)] = values.at(i); + std::ostringstream modelMessage; + modelMessage << "Counterexample:\n"; + solAssert(values.size() == expressionNames.size(), ""); + map sortedModel; + for (size_t i = 0; i < values.size(); ++i) + if (expressionsToEvaluate.at(i).name != values.at(i)) + sortedModel[expressionNames.at(i)] = values.at(i); - for (auto const& eval: sortedModel) - modelMessage << " " << eval.first << " = " << eval.second << "\n"; + for (auto const& eval: sortedModel) + modelMessage << " " << eval.first << " = " << eval.second << "\n"; - m_errorReporter.warning( - _errorHappens, - _location, - message.str(), - SecondarySourceLocation().append(modelMessage.str(), SourceLocation{}) - .append(SMTEncoder::callStackMessage(_callStack)) - .append(move(secondaryLocation)) - ); - } - else - m_errorReporter.warning(6084_error, _location, message.str(), secondaryLocation); + m_errorReporter.warning( + _errorHappens, + _location, + message.str(), + SecondarySourceLocation().append(modelMessage.str(), SourceLocation{}) + .append(SMTEncoder::callStackMessage(_callStack)) + .append(move(secondaryLocation)) + ); break; } case smtutil::CheckResult::UNSATISFIABLE: diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/assert_in_constructor.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/assert_in_constructor.sol new file mode 100644 index 000000000..2ceae7304 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/assert_in_constructor.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; + +contract C { + uint x = initX(); + + function initX() internal pure returns (uint) { + return 42; + } +} + +contract D is C { + uint y; + + constructor() { + assert(x == 42); + y = x; + } +} +// ==== +// SMTEngine: bmc +// ---- diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/implicit_constructor_with_function_calls.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/implicit_constructor_with_function_calls.sol new file mode 100644 index 000000000..2f09c6a9b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/implicit_constructor_with_function_calls.sol @@ -0,0 +1,19 @@ +pragma experimental SMTChecker; + +contract C { + uint x = initX(); + uint y = initY(); + + function initX() internal pure returns (uint) { + return 42; + } + + function initY() internal view returns (uint) { + assert(x == 42); + return x; + } +} +// ==== +// SMTEngine: bmc +// ---- +// Warning 4661: (205-220): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/math.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/math.sol index 3ec7754c3..ca63aceac 100644 --- a/test/libsolidity/smtCheckerTests/bmc_coverage/math.sol +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/math.sol @@ -22,4 +22,4 @@ contract C { // Warning 4144: (217-222): BMC: Underflow (resulting value less than 0) happens here. // Warning 2661: (293-298): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. // Warning 3046: (369-374): BMC: Division by zero happens here. -// Warning 6084: (68-73): BMC: Underflow (resulting value less than 0) happens here. +// Warning 4144: (68-73): BMC: Underflow (resulting value less than 0) happens here. From d9fb17a85ef0574e7da0de19068608e6674cae7b Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 12 Nov 2020 14:46:10 +0100 Subject: [PATCH 23/29] Copy value array from storage to storage. --- libsolidity/codegen/YulUtilFunctions.cpp | 113 ++++++++++++++++-- libsolidity/codegen/YulUtilFunctions.h | 17 ++- .../array_copy_storage_storage_dyn_dyn.sol | 2 + ...ay_copy_storage_storage_static_dynamic.sol | 2 + .../copying/copy_function_storage_array.sol | 2 + 5 files changed, 125 insertions(+), 11 deletions(-) diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 075f9ca32..cbdca8524 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -449,6 +449,36 @@ string YulUtilFunctions::maskBytesFunctionDynamic() }); } +string YulUtilFunctions::maskLowerOrderBytesFunction(size_t _bytes) +{ + string functionName = "mask_lower_order_bytes_" + to_string(_bytes); + solAssert(_bytes <= 32, ""); + return m_functionCollector.createFunction(functionName, [&]() { + return Whiskers(R"( + function (data) -> result { + result := and(data, ) + })") + ("functionName", functionName) + ("mask", formatNumber((~u256(0)) >> (256 - 8 * _bytes))) + .render(); + }); +} + +string YulUtilFunctions::maskLowerOrderBytesFunctionDynamic() +{ + string functionName = "mask_lower_order_bytes_dynamic"; + return m_functionCollector.createFunction(functionName, [&]() { + return Whiskers(R"( + function (data, bytes) -> result { + let mask := not((mul(8, bytes), not(0))) + result := and(data, mask) + })") + ("functionName", functionName) + ("shl", shiftLeftFunctionDynamic()) + .render(); + }); +} + string YulUtilFunctions::roundUpFunction() { string functionName = "round_up_to_mul_of_32"; @@ -1470,23 +1500,20 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType, ); if (_fromType.isByteArray()) return copyByteArrayToStorageFunction(_fromType, _toType); - solUnimplementedAssert(!_fromType.dataStoredIn(DataLocation::Storage), ""); + if (_fromType.dataStoredIn(DataLocation::Storage) && _toType.baseType()->isValueType()) + return copyValueArrayStorageToStorageFunction(_fromType, _toType); string functionName = "copy_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier(); return m_functionCollector.createFunction(functionName, [&](){ Whiskers templ(R"( function (slot, value, len) { + if eq(slot, value) { leave } let length := (value, len) (slot, length) - let srcPtr := - - add(value, 0x20) - - value - + let srcPtr := (value) let elementSlot := (slot) let elementOffset := 0 @@ -1509,6 +1536,10 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType, let := (srcPtr) + + let := srcPtr + + (elementSlot, elementOffset, ) srcPtr := add(srcPtr, ) @@ -1526,12 +1557,17 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType, } } )"); + if (_fromType.dataStoredIn(DataLocation::Storage)) + solAssert(!_fromType.isValueType(), ""); templ("functionName", functionName); bool fromCalldata = _fromType.dataStoredIn(DataLocation::CallData); templ("isFromDynamicCalldata", _fromType.isDynamicallySized() && fromCalldata); - templ("fromMemory", _fromType.dataStoredIn(DataLocation::Memory)); + templ("fromStorage", _fromType.dataStoredIn(DataLocation::Storage)); + bool fromMemory = _fromType.dataStoredIn(DataLocation::Memory); + templ("fromMemory", fromMemory); templ("fromCalldata", fromCalldata); templ("isToDynamic", _toType.isDynamicallySized()); + templ("srcDataLocation", arrayDataAreaFunction(_fromType)); templ("isFromMemoryDynamic", _fromType.isDynamicallySized() && _fromType.dataStoredIn(DataLocation::Memory)); if (fromCalldata) { @@ -1545,7 +1581,7 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType, templ("arrayLength",arrayLengthFunction(_fromType)); templ("isValueType", _fromType.baseType()->isValueType()); templ("dstDataLocation", arrayDataAreaFunction(_toType)); - if (!fromCalldata || _fromType.baseType()->isValueType()) + if (fromMemory || (fromCalldata && _fromType.baseType()->isValueType())) templ("readFromCalldataOrMemory", readFromMemoryOrCalldata(*_fromType.baseType(), fromCalldata)); templ("elementValues", suffixedVariableNameList( "elementValue_", @@ -1643,6 +1679,65 @@ string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromTy }); } + +string YulUtilFunctions::copyValueArrayStorageToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType) +{ + solAssert( + *_fromType.copyForLocation(_toType.location(), _toType.isPointer()) == dynamic_cast(_toType), + "" + ); + solAssert(!_fromType.isByteArray(), ""); + solAssert(_fromType.dataStoredIn(DataLocation::Storage) && _toType.baseType()->isValueType(), ""); + solAssert(_toType.dataStoredIn(DataLocation::Storage), ""); + + string functionName = "copy_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier(); + return m_functionCollector.createFunction(functionName, [&](){ + Whiskers templ(R"( + function (dst, src) { + if eq(dst, src) { leave } + let length := (src) + // Make sure array length is sane + if gt(length, 0xffffffffffffffff) { () } + + (dst, length) + + + let srcPtr := (src) + + let dstPtr := (dst) + + let fullSlots := div(length, ) + let i := 0 + for { } lt(i, fullSlots) { i := add(i, 1) } { + sstore(add(dstPtr, i), (sload(add(srcPtr, i)))) + } + let spill := sub(length, mul(i, )) + if gt(spill, 0) { + sstore(add(dstPtr, i), (sload(add(srcPtr, i)), mul(spill, ))) + } + } + )"); + if (_fromType.dataStoredIn(DataLocation::Storage)) + solAssert(!_fromType.isValueType(), ""); + templ("functionName", functionName); + templ("isToDynamic", _toType.isDynamicallySized()); + if (_toType.isDynamicallySized()) + templ("resizeArray", resizeDynamicArrayFunction(_toType)); + templ("arrayLength",arrayLengthFunction(_fromType)); + templ("panic", panicFunction()); + templ("srcDataLocation", arrayDataAreaFunction(_fromType)); + templ("dstDataLocation", arrayDataAreaFunction(_toType)); + unsigned itemsPerSlot = 32 / _toType.storageStride(); + templ("itemsPerSlot", to_string(itemsPerSlot)); + templ("bytesPerItem", to_string(_toType.storageStride())); + templ("maskFull", maskLowerOrderBytesFunction(itemsPerSlot * _toType.storageStride())); + templ("maskBytes", maskLowerOrderBytesFunctionDynamic()); + + return templ.render(); + }); +} + + string YulUtilFunctions::arrayConvertLengthToSize(ArrayType const& _type) { string functionName = "array_convert_length_to_size_" + _type.identifier(); diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index 161cdf64b..1a0cd9a83 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -104,6 +104,15 @@ public: /// signature: (value, bytes) -> result std::string maskBytesFunctionDynamic(); + /// Zeroes out all bytes above the first ``_bytes`` lower order bytes. + /// signature: (value) -> result + std::string maskLowerOrderBytesFunction(size_t _bytes); + + /// Zeroes out all bytes above the first ``bytes`` lower order bytes. + /// @note ``bytes`` has to be small enough not to overflow ``8 * bytes``. + /// signature: (value, bytes) -> result + std::string maskLowerOrderBytesFunctionDynamic(); + /// @returns the name of a function that rounds its input to the next multiple /// of 32 or the input if it is a multiple of 32. /// signature: (value) -> result @@ -186,14 +195,18 @@ public: /// signature: (slot) -> std::string clearStorageArrayFunction(ArrayType const& _type); - /// @returns the name of a function that will copy array from calldata or memory to storage + /// @returns the name of a function that will copy an array to storage /// signature (to_slot, from_ptr) -> std::string copyArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType); - /// @returns the name of a function that will copy a byte array from calldata or memory to storage + /// @returns the name of a function that will copy a byte array to storage /// signature (to_slot, from_ptr) -> std::string copyByteArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType); + /// @returns the name of a function that will copy an array of value types from storage to storage. + /// signature (to_slot, from_slot) -> + std::string copyValueArrayStorageToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType); + /// Returns the name of a function that will convert a given length to the /// size in memory (number of storage slots or calldata/memory bytes) it /// will require. diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dyn_dyn.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dyn_dyn.sol index c183d8a82..f2235bf0d 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dyn_dyn.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dyn_dyn.sol @@ -12,6 +12,8 @@ contract c { len = data2.length; if (index < len) val = data2[index]; } } +// ==== +// compileViaYul: also // ---- // setData1(uint256,uint256,uint256): 10, 5, 4 -> // copyStorageStorage() -> diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_dynamic.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_dynamic.sol index 4432776fb..9b3ba326e 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_dynamic.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_dynamic.sol @@ -10,5 +10,7 @@ contract c { } } +// ==== +// compileViaYul: also // ---- // test() -> 9, 4 diff --git a/test/libsolidity/semanticTests/array/copying/copy_function_storage_array.sol b/test/libsolidity/semanticTests/array/copying/copy_function_storage_array.sol index eba61ad1a..7b3022361 100644 --- a/test/libsolidity/semanticTests/array/copying/copy_function_storage_array.sol +++ b/test/libsolidity/semanticTests/array/copying/copy_function_storage_array.sol @@ -14,5 +14,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // test() -> 7 From 98cc7a1ea9bd845fce9add1a8dfd0156d8926141 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 12 Nov 2020 15:23:43 +0100 Subject: [PATCH 24/29] Copying structs from storage to storage. --- libsolidity/codegen/YulUtilFunctions.cpp | 110 ++++++++++-------- .../struct_assign_reference_to_struct.sol | 2 + ...truct_containing_bytes_copy_and_delete.sol | 2 + .../semanticTests/structs/struct_copy.sol | 2 + .../various/swap_in_storage_overwrite.sol | 2 + 5 files changed, 69 insertions(+), 49 deletions(-) diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index cbdca8524..32d96369c 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -2330,10 +2330,6 @@ string YulUtilFunctions::updateStorageValueFunction( { solAssert(_toType.category() == Type::Category::Struct, ""); - solUnimplementedAssert( - fromReferenceType->location() != DataLocation::Storage, - "Copying from storage to storage is not yet implemented." - ); auto const& fromStructType = dynamic_cast(_fromType); auto const& toStructType = dynamic_cast(_toType); solAssert(fromStructType.structDefinition() == toStructType.structDefinition(), ""); @@ -2341,6 +2337,7 @@ string YulUtilFunctions::updateStorageValueFunction( Whiskers templ(R"( function (slot, value) { + if eq(slot, value) { leave } <#member> { @@ -2348,6 +2345,7 @@ string YulUtilFunctions::updateStorageValueFunction( } )"); + templ("fromStorage", fromStructType.dataStoredIn(DataLocation::Storage)); templ("functionName", functionName); MemberList::MemberMap structMembers = fromStructType.nativeMembers(nullptr); @@ -2356,67 +2354,81 @@ string YulUtilFunctions::updateStorageValueFunction( vector> memberParams(structMembers.size()); for (size_t i = 0; i < structMembers.size(); ++i) { - solAssert(structMembers[i].type->memoryHeadSize() == 32, ""); - bool fromCalldata = fromStructType.location() == DataLocation::CallData; + Type const& memberType = *structMembers[i].type; + solAssert(memberType.memoryHeadSize() == 32, ""); auto const& [slotDiff, offset] = toStructType.storageOffsetsOfMember(structMembers[i].name); Whiskers t(R"( let memberSlot := add(slot, ) + let memberSrcPtr := add(value, ) - - let := (value, add(value, )) - - let := add(value, ) - + let := + + (value, memberSrcPtr) + + memberSrcPtr + - let := () - (memberSlot, , ) - - (memberSlot, ) + := () - - let memberMemoryOffset := add(value, ) - let := (memberMemoryOffset) - (memberSlot, , ) + + + let := (memberSrcPtr) + + + + let := + + (memberSrcPtr) + + memberSrcPtr + + + + (memberSlot, ) )"); + bool fromCalldata = fromStructType.location() == DataLocation::CallData; t("fromCalldata", fromCalldata); + bool fromMemory = fromStructType.location() == DataLocation::Memory; + t("fromMemory", fromMemory); + bool fromStorage = fromStructType.location() == DataLocation::Storage; + t("fromStorage", fromStorage); + t("isValueType", memberType.isValueType()); + t("memberValues", suffixedVariableNameList("memberValue_", 0, memberType.stackItems().size())); + + t("memberStorageSlotDiff", slotDiff.str()); if (fromCalldata) { - t("memberCalldataOffset", suffixedVariableNameList( - "memberCalldataOffset_", - 0, - structMembers[i].type->stackItems().size() - )); - t("dynamicallyEncodedMember", structMembers[i].type->isDynamicallyEncoded()); - if (structMembers[i].type->isDynamicallyEncoded()) - t("accessCalldataTail", accessCalldataTailFunction(*structMembers[i].type)); + t("memberOffset", to_string(fromStructType.calldataOffsetOfMember(structMembers[i].name))); + t("dynamicallyEncodedMember", memberType.isDynamicallyEncoded()); + if (memberType.isDynamicallyEncoded()) + t("accessCalldataTail", accessCalldataTailFunction(memberType)); + if (memberType.isValueType()) + t("read", readFromCalldata(memberType)); } - t("isValueType", structMembers[i].type->isValueType()); - t("memberValues", suffixedVariableNameList( - "memberValue_", - 0, - structMembers[i].type->stackItems().size() + else if (fromMemory) + { + t("memberOffset", fromStructType.memoryOffsetOfMember(structMembers[i].name).str()); + t("read", readFromMemory(memberType)); + } + else if (fromStorage) + { + auto [srcSlotOffset, srcOffset] = fromStructType.storageOffsetsOfMember(structMembers[i].name); + t("memberOffset", formatNumber(srcSlotOffset)); + if (memberType.isValueType()) + t("read", readFromStorageValueType(memberType, srcOffset, false)); + else + solAssert(srcOffset == 0, ""); + + } + t("updateStorageValue", updateStorageValueFunction( + memberType, + *toStructMembers[i].type, + memberType.isValueType() ? optional{offset} : std::nullopt )); - t("hasOffset", structMembers[i].type->isValueType()); - t( - "updateMember", - structMembers[i].type->isValueType() ? - updateStorageValueFunction(*structMembers[i].type, *toStructMembers[i].type) : - updateStorageValueFunction(*structMembers[i].type, *toStructMembers[i].type, offset) - ); - t("memberStorageSlotDiff", slotDiff.str()); - t("memberStorageOffset", to_string(offset)); - t( - "memberOffset", - fromCalldata ? - to_string(fromStructType.calldataOffsetOfMember(structMembers[i].name)) : - fromStructType.memoryOffsetOfMember(structMembers[i].name).str() - ); - if (!fromCalldata || structMembers[i].type->isValueType()) - t("loadFromMemoryOrCalldata", readFromMemoryOrCalldata(*structMembers[i].type, fromCalldata)); memberParams[i]["updateMemberCall"] = t.render(); } templ("member", memberParams); diff --git a/test/libsolidity/semanticTests/structs/struct_assign_reference_to_struct.sol b/test/libsolidity/semanticTests/structs/struct_assign_reference_to_struct.sol index ca6dffb9d..4b456ee6c 100644 --- a/test/libsolidity/semanticTests/structs/struct_assign_reference_to_struct.sol +++ b/test/libsolidity/semanticTests/structs/struct_assign_reference_to_struct.sol @@ -32,5 +32,7 @@ contract test { } } +// ==== +// compileViaYul: also // ---- // assign() -> 2, 2, 3, 3 diff --git a/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol b/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol index c6e25f9d5..2ab4a8f36 100644 --- a/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol +++ b/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol @@ -20,6 +20,8 @@ contract c { return data1.data[i]; } } +// ==== +// compileViaYul: also // ---- // storage: empty // set(uint256,bytes,uint256): 12, 0x60, 13, 33, "12345678901234567890123456789012", "3" -> true diff --git a/test/libsolidity/semanticTests/structs/struct_copy.sol b/test/libsolidity/semanticTests/structs/struct_copy.sol index 2849c16bc..7c9ef9751 100644 --- a/test/libsolidity/semanticTests/structs/struct_copy.sol +++ b/test/libsolidity/semanticTests/structs/struct_copy.sol @@ -34,6 +34,8 @@ contract c { } } +// ==== +// compileViaYul: also // ---- // set(uint256): 7 -> true // retrieve(uint256): 7 -> 1, 3, 4, 2 diff --git a/test/libsolidity/semanticTests/various/swap_in_storage_overwrite.sol b/test/libsolidity/semanticTests/various/swap_in_storage_overwrite.sol index 59907b476..cd3bf04da 100644 --- a/test/libsolidity/semanticTests/various/swap_in_storage_overwrite.sol +++ b/test/libsolidity/semanticTests/various/swap_in_storage_overwrite.sol @@ -23,6 +23,8 @@ contract c { } } +// ==== +// compileViaYul: also // ---- // x() -> 0, 0 // y() -> 0, 0 From 559b27aaadde0008433e33c87d0cf354d3811378 Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Thu, 8 Oct 2020 11:07:31 +0200 Subject: [PATCH 25/29] Natspec: Fix internal error when different return name was inherited --- Changelog.md | 1 + libsolidity/analysis/DocStringAnalyser.cpp | 51 +++- test/libsolidity/SolidityNatspecJSON.cpp | 225 ++++++++++++++++++ .../operators/slice_default_end.sol | 2 +- 4 files changed, 268 insertions(+), 11 deletions(-) diff --git a/Changelog.md b/Changelog.md index 3b801c1c9..fb06c2ea6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -20,6 +20,7 @@ Bugfixes: * SMTChecker: Fix internal error in the BMC engine when inherited contract from a different source unit has private state variables. * SMTChecker: Fix internal error when ``array.push()`` is used as the LHS of an assignment. * Code generator: Fix missing creation dependency tracking for abstract contracts. + * NatSpec: Fix internal error when inheriting return parameter documentation but the parameter names differ between base and inherited. ### 0.7.4 (2020-10-19) diff --git a/libsolidity/analysis/DocStringAnalyser.cpp b/libsolidity/analysis/DocStringAnalyser.cpp index 5eba7d175..ab445b93f 100644 --- a/libsolidity/analysis/DocStringAnalyser.cpp +++ b/libsolidity/analysis/DocStringAnalyser.cpp @@ -36,21 +36,52 @@ using namespace solidity::frontend; namespace { -void copyMissingTags(StructurallyDocumentedAnnotation& _target, set const& _baseFunctions) +void copyMissingTags(set const& _baseFunctions, StructurallyDocumentedAnnotation& _target, CallableDeclaration const* _declaration = nullptr) { + // Only copy if there is exactly one direct base function. if (_baseFunctions.size() != 1) return; auto& sourceDoc = dynamic_cast((*_baseFunctions.begin())->annotation()); - set existingTags; + for (auto it = sourceDoc.docTags.begin(); it != sourceDoc.docTags.end();) + { + string const& tag = it->first; + // Don't copy tag "inheritdoc" or already existing tags + if (tag == "inheritdoc" || _target.docTags.count(tag)) + { + it++; + continue; + } - for (auto const& iterator: _target.docTags) - existingTags.insert(iterator.first); + size_t n = 0; + // Iterate over all values of the current tag (it's a multimap) + for (auto next = sourceDoc.docTags.upper_bound(tag); it != next; it++, n++) + { + DocTag content = it->second; + + // Update the parameter name for @return tags + if (_declaration && tag == "return") + { + size_t docParaNameEndPos = content.content.find_first_of(" \t"); + string const docParameterName = content.content.substr(0, docParaNameEndPos); + + if (docParameterName != _declaration->returnParameters().at(n)->name()) + { + bool baseHasNoName = (*_baseFunctions.begin())->returnParameters().at(n)->name().empty(); + string paramName = _declaration->returnParameters().at(n)->name(); + content.content = + (paramName.empty() ? "" : std::move(paramName) + " ") + ( + string::npos == docParaNameEndPos || baseHasNoName ? + content.content : + content.content.substr(docParaNameEndPos + 1) + ); + } + } - for (auto const& [tag, content]: sourceDoc.docTags) - if (tag != "inheritdoc" && !existingTags.count(tag)) _target.docTags.emplace(tag, content); + } + } } CallableDeclaration const* findBaseCallable(set const& _baseFunctions, int64_t _contractId) @@ -91,9 +122,9 @@ bool DocStringAnalyser::visit(VariableDeclaration const& _variable) return false; if (CallableDeclaration const* baseFunction = resolveInheritDoc(_variable.annotation().baseFunctions, _variable, _variable.annotation())) - copyMissingTags(_variable.annotation(), {baseFunction}); + copyMissingTags({baseFunction}, _variable.annotation()); else if (_variable.annotation().docTags.empty()) - copyMissingTags(_variable.annotation(), _variable.annotation().baseFunctions); + copyMissingTags(_variable.annotation().baseFunctions, _variable.annotation()); return false; } @@ -119,13 +150,13 @@ void DocStringAnalyser::handleCallable( ) { if (CallableDeclaration const* baseFunction = resolveInheritDoc(_callable.annotation().baseFunctions, _node, _annotation)) - copyMissingTags(_annotation, {baseFunction}); + copyMissingTags({baseFunction}, _annotation, &_callable); else if ( _annotation.docTags.empty() && _callable.annotation().baseFunctions.size() == 1 && parameterNamesEqual(_callable, **_callable.annotation().baseFunctions.begin()) ) - copyMissingTags(_annotation, _callable.annotation().baseFunctions); + copyMissingTags(_callable.annotation().baseFunctions, _annotation, &_callable); } CallableDeclaration const* DocStringAnalyser::resolveInheritDoc( diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index fa1e7903d..0c8451750 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -1748,6 +1748,7 @@ BOOST_AUTO_TEST_CASE(user_explicit_inherit_partial2) checkNatspec(sourceCode, "ERC20", natspec, true); checkNatspec(sourceCode, "Token", natspec2, true); } + BOOST_AUTO_TEST_CASE(dev_explicit_inherit_partial) { char const *sourceCode = R"( @@ -2022,6 +2023,230 @@ BOOST_AUTO_TEST_CASE(dev_explicit_inehrit_complex) ); } +BOOST_AUTO_TEST_CASE(dev_different_return_name) +{ + char const *sourceCode = R"( + contract A { + /// @return y value + function g(int x) public pure virtual returns (int y) { return x; } + } + + contract B is A { + function g(int x) public pure override returns (int z) { return x; } + } + )"; + + char const *natspec = R"ABCDEF({ + "methods": + { + "g(int256)": + { + "returns": + { + "y": "value" + } + } + } + })ABCDEF"; + + char const *natspec2 = R"ABCDEF({ + "methods": + { + "g(int256)": + { + "returns": + { + "z": "value" + } + } + } + })ABCDEF"; + + checkNatspec(sourceCode, "A", natspec, false); + checkNatspec(sourceCode, "B", natspec2, false); +} + +BOOST_AUTO_TEST_CASE(dev_different_return_name_multiple) +{ + char const *sourceCode = R"( + contract A { + /// @return a value A + /// @return b value B + function g(int x) public pure virtual returns (int a, int b) { return (1, 2); } + } + + contract B is A { + function g(int x) public pure override returns (int z, int y) { return (1, 2); } + } + )"; + + char const *natspec = R"ABCDEF({ + "methods": + { + "g(int256)": + { + "returns": + { + "a": "value A", + "b": "value B" + } + } + } + })ABCDEF"; + + char const *natspec2 = R"ABCDEF({ + "methods": + { + "g(int256)": + { + "returns": + { + "z": "value A", + "y": "value B" + } + } + } + })ABCDEF"; + + checkNatspec(sourceCode, "A", natspec, false); + checkNatspec(sourceCode, "B", natspec2, false); +} + +BOOST_AUTO_TEST_CASE(dev_different_return_name_multiple_partly_unnamed) +{ + char const *sourceCode = R"( + contract A { + /// @return value A + /// @return b value B + function g(int x) public pure virtual returns (int, int b) { return (1, 2); } + } + + contract B is A { + function g(int x) public pure override returns (int z, int) { return (1, 2); } + } + )"; + + char const *natspec = R"ABCDEF({ + "methods": + { + "g(int256)": + { + "returns": + { + "_0": "value A", + "b": "value B" + } + } + } + })ABCDEF"; + + char const *natspec2 = R"ABCDEF({ + "methods": + { + "g(int256)": + { + "returns": + { + "z": "value A", + "_1": "value B" + } + } + } + })ABCDEF"; + + checkNatspec(sourceCode, "A", natspec, false); + checkNatspec(sourceCode, "B", natspec2, false); +} + +BOOST_AUTO_TEST_CASE(dev_different_return_name_multiple_unnamed) +{ + char const *sourceCode = R"( + contract A { + /// @return value A + /// @return value B + function g(int x) public pure virtual returns (int, int) { return (1, 2); } + } + + contract B is A { + function g(int x) public pure override returns (int z, int y) { return (1, 2); } + } + )"; + + char const *natspec = R"ABCDEF({ + "methods": + { + "g(int256)": + { + "returns": + { + "_0": "value A", + "_1": "value B" + } + } + } + })ABCDEF"; + + char const *natspec2 = R"ABCDEF({ + "methods": + { + "g(int256)": + { + "returns": + { + "z": "value A", + "y": "value B" + } + } + } + })ABCDEF"; + + checkNatspec(sourceCode, "A", natspec, false); + checkNatspec(sourceCode, "B", natspec2, false); +} + +BOOST_AUTO_TEST_CASE(dev_return_name_no_description) +{ + char const *sourceCode = R"( + contract A { + /// @return a + function g(int x) public pure virtual returns (int a) { return 2; } + } + + contract B is A { + function g(int x) public pure override returns (int b) { return 2; } + } + )"; + + char const *natspec = R"ABCDEF({ + "methods": + { + "g(int256)": + { + "returns": + { + "a": "a", + } + } + } + })ABCDEF"; + + char const *natspec2 = R"ABCDEF({ + "methods": + { + "g(int256)": + { + "returns": + { + "b": "a", + } + } + } + })ABCDEF"; + + checkNatspec(sourceCode, "A", natspec, false); + checkNatspec(sourceCode, "B", natspec2, false); +} + } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/smtCheckerTests/operators/slice_default_end.sol b/test/libsolidity/smtCheckerTests/operators/slice_default_end.sol index b35f6f2bb..9bf06e68e 100644 --- a/test/libsolidity/smtCheckerTests/operators/slice_default_end.sol +++ b/test/libsolidity/smtCheckerTests/operators/slice_default_end.sol @@ -8,7 +8,7 @@ contract C { assert(bytes(b[10:]).length == 20); assert(bytes(b[10:])[0] == 0xff); //assert(bytes(b[10:])[5] == 0xff); // Removed because of Spacer's nondeterminism - assert(bytes(b[10:])[19] == 0xaa); + //assert(bytes(b[10:])[19] == 0xaa); // Removed because of Spacer nondeterminism } } // ---- From fffd0306f57d39dc8765d3d139f4989d1396b02f Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 17 Nov 2020 14:51:43 +0100 Subject: [PATCH 26/29] Fix bug in array to storage copy routine. --- libsolidity/codegen/YulUtilFunctions.cpp | 12 ++++++++---- .../array/copying/array_copy_nested_array.sol | 2 ++ .../copying/array_copy_storage_storage_struct.sol | 2 ++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 32d96369c..ad4ad2198 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1542,7 +1542,7 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType, (elementSlot, elementOffset, ) - srcPtr := add(srcPtr, ) + srcPtr := add(srcPtr, ) elementOffset := add(elementOffset, ) @@ -1552,7 +1552,6 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType, } elementSlot := add(elementSlot, ) - elementOffset := 0 } } @@ -1568,7 +1567,6 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType, templ("fromCalldata", fromCalldata); templ("isToDynamic", _toType.isDynamicallySized()); templ("srcDataLocation", arrayDataAreaFunction(_fromType)); - templ("isFromMemoryDynamic", _fromType.isDynamicallySized() && _fromType.dataStoredIn(DataLocation::Memory)); if (fromCalldata) { templ("dynamicallySizedBase", _fromType.baseType()->isDynamicallySized()); @@ -1589,7 +1587,13 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType, _fromType.baseType()->stackItems().size() )); templ("updateStorageValue", updateStorageValueFunction(*_fromType.baseType(), *_toType.baseType())); - templ("stride", to_string(fromCalldata ? _fromType.calldataStride() : _fromType.memoryStride())); + templ("srcStride", + fromCalldata ? + to_string(_fromType.calldataStride()) : + fromMemory ? + to_string(_fromType.memoryStride()) : + formatNumber(_fromType.baseType()->storageSize()) + ); templ("multipleItemsPerSlot", _toType.storageStride() <= 16); templ("storageStride", to_string(_toType.storageStride())); templ("storageSize", _toType.baseType()->storageSize().str()); diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_nested_array.sol b/test/libsolidity/semanticTests/array/copying/array_copy_nested_array.sol index 9330d3162..bd4b1ea92 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_nested_array.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_nested_array.sol @@ -11,5 +11,7 @@ contract c { } } +// ==== +// compileViaYul: also // ---- // test(uint256[2][]): 32, 3, 7, 8, 9, 10, 11, 12 -> 10 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_struct.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_struct.sol index 5c53eaa77..097c86dfe 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_struct.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_struct.sol @@ -15,6 +15,8 @@ contract c { data2 = data1; } } +// ==== +// compileViaYul: also // ---- // test() -> 4, 5 // storage: empty From 74ce8d697959de1dfa5dbb5b374892616a0aa0fd Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 17 Nov 2020 12:58:54 +0100 Subject: [PATCH 27/29] Fix assignment to non-lvalue storage values. --- libsolidity/codegen/YulUtilFunctions.cpp | 18 ++++++++++++------ libsolidity/codegen/YulUtilFunctions.h | 1 + .../codegen/ir/IRGeneratorForStatements.cpp | 18 +++++++++--------- .../copying/array_copy_including_array.sol | 2 ++ .../array/pop/array_pop_array_transition.sol | 2 ++ 5 files changed, 26 insertions(+), 15 deletions(-) diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index ad4ad2198..5ccd8e285 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1274,7 +1274,6 @@ string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type) { solAssert(_type.location() == DataLocation::Storage, ""); solAssert(_type.isDynamicallySized(), ""); - solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "Base type is not yet implemented."); string functionName = "array_push_" + _type.identifier(); return m_functionCollector.createFunction(functionName, [&]() { @@ -1540,7 +1539,7 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType, let := srcPtr - (elementSlot, elementOffset, ) + (elementSlot, elementOffset, ) srcPtr := add(srcPtr, ) @@ -2317,11 +2316,14 @@ string YulUtilFunctions::updateStorageValueFunction( solAssert(_offset.value_or(0) == 0, ""); Whiskers templ(R"( - function (slot, ) { + function (slot, offset, ) { + if offset { () } (slot, ) } )"); templ("functionName", functionName); + templ("dynamicOffset", !_offset.has_value()); + templ("panic", panicFunction()); templ("value", suffixedVariableNameList("value_", 0, _fromType.sizeOnStack())); templ("copyArrayToStorage", copyArrayToStorageFunction( dynamic_cast(_fromType), @@ -2340,7 +2342,8 @@ string YulUtilFunctions::updateStorageValueFunction( solAssert(_offset.value_or(0) == 0, ""); Whiskers templ(R"( - function (slot, value) { + function (slot, offset, value) { + if offset { () } if eq(slot, value) { leave } <#member> { @@ -2349,8 +2352,10 @@ string YulUtilFunctions::updateStorageValueFunction( } )"); - templ("fromStorage", fromStructType.dataStoredIn(DataLocation::Storage)); templ("functionName", functionName); + templ("dynamicOffset", !_offset.has_value()); + templ("panic", panicFunction()); + templ("fromStorage", fromStructType.dataStoredIn(DataLocation::Storage)); MemberList::MemberMap structMembers = fromStructType.nativeMembers(nullptr); MemberList::MemberMap toStructMembers = toStructType.nativeMembers(nullptr); @@ -2428,10 +2433,11 @@ string YulUtilFunctions::updateStorageValueFunction( solAssert(srcOffset == 0, ""); } + t("memberStorageSlotOffset", to_string(offset)); t("updateStorageValue", updateStorageValueFunction( memberType, *toStructMembers[i].type, - memberType.isValueType() ? optional{offset} : std::nullopt + optional{offset} )); memberParams[i]["updateMemberCall"] = t.render(); } diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index 1a0cd9a83..af34c3535 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -287,6 +287,7 @@ public: /// Returns the name of a function will write the given value to /// the specified slot and offset. If offset is not given, it is expected as /// runtime parameter. + /// For reference types, offset is checked to be zero at runtime. /// signature: (slot, [offset,] value) std::string updateStorageValueFunction( Type const& _fromType, diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 9d4d9ad54..142660749 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -2660,22 +2660,22 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable std::visit( util::GenericVisitor{ [&](IRLValue::Storage const& _storage) { - std::optional offset; + string offsetArgument; + optional offsetStatic; - if (std::holds_alternative(_storage.offset)) - offset = std::get(_storage.offset); + std::visit(GenericVisitor{ + [&](unsigned _offset) { offsetStatic = _offset; }, + [&](string const& _offset) { offsetArgument = ", " + _offset; } + }, _storage.offset); m_code << - m_utils.updateStorageValueFunction(_value.type(), _lvalue.type, offset) << + m_utils.updateStorageValueFunction(_value.type(), _lvalue.type, offsetStatic) << "(" << _storage.slot << - ( - std::holds_alternative(_storage.offset) ? - (", " + std::get(_storage.offset)) : - "" - ) << + offsetArgument << _value.commaSeparatedListPrefixed() << ")\n"; + }, [&](IRLValue::Memory const& _memory) { if (_lvalue.type.isValueType()) diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol b/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol index 04564b748..a600bffdd 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol @@ -33,6 +33,8 @@ contract c { return (small.length, large.length); } } +// ==== +// compileViaYul: also // ---- // test() -> 0x02000202 // storage: empty diff --git a/test/libsolidity/semanticTests/array/pop/array_pop_array_transition.sol b/test/libsolidity/semanticTests/array/pop/array_pop_array_transition.sol index d826dea30..ece182803 100644 --- a/test/libsolidity/semanticTests/array/pop/array_pop_array_transition.sol +++ b/test/libsolidity/semanticTests/array/pop/array_pop_array_transition.sol @@ -21,6 +21,8 @@ contract c { delete inner; } } +// ==== +// compileViaYul: also // ---- // test() -> 1, 2, 3 // storage: empty From 9f0283df5cf04572d6974743702c8d626c9c2aa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 13 Nov 2020 23:19:56 +0100 Subject: [PATCH 28/29] Fix command-line tests using wrong file names for libraries --- test/cmdlineTests/linking_standard_solidity/input.json | 2 +- .../linking_standard_solidity_quote_in_file_name/input.json | 4 ++-- .../linking_standard_solidity_quote_in_file_name/output.json | 2 +- .../input.json | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/cmdlineTests/linking_standard_solidity/input.json b/test/cmdlineTests/linking_standard_solidity/input.json index b70481304..921aa5018 100644 --- a/test/cmdlineTests/linking_standard_solidity/input.json +++ b/test/cmdlineTests/linking_standard_solidity/input.json @@ -20,7 +20,7 @@ }, "settings": { "libraries": { - "contract/test.sol": { + "A": { "L": "0x1234567890123456789012345678901234567890" } }, diff --git a/test/cmdlineTests/linking_standard_solidity_quote_in_file_name/input.json b/test/cmdlineTests/linking_standard_solidity_quote_in_file_name/input.json index fc54cdabc..d96e65c5f 100644 --- a/test/cmdlineTests/linking_standard_solidity_quote_in_file_name/input.json +++ b/test/cmdlineTests/linking_standard_solidity_quote_in_file_name/input.json @@ -1,7 +1,7 @@ { "language": "Solidity", "sources": { - "A": { + "A\"B": { "content": " // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.0; @@ -20,7 +20,7 @@ }, "settings": { "libraries": { - "contract/test\"test.sol": { + "A\"B": { "L": "0x1234567890123456789012345678901234567890" } }, diff --git a/test/cmdlineTests/linking_standard_solidity_quote_in_file_name/output.json b/test/cmdlineTests/linking_standard_solidity_quote_in_file_name/output.json index 1cfa37405..f04e00b12 100644 --- a/test/cmdlineTests/linking_standard_solidity_quote_in_file_name/output.json +++ b/test/cmdlineTests/linking_standard_solidity_quote_in_file_name/output.json @@ -1 +1 @@ -{"contracts":{"A":{"C":{"evm":{"bytecode":{"generatedSources":[],"linkReferences":{},"object":"","opcodes":"","sourceMap":""}}}}},"sources":{"A":{"id":0}}} +{"contracts":{"A\"B":{"C":{"evm":{"bytecode":{"generatedSources":[],"linkReferences":{},"object":"","opcodes":"","sourceMap":""}}}}},"sources":{"A\"B":{"id":0}}} diff --git a/test/cmdlineTests/linking_standard_solidity_unresolved_references/input.json b/test/cmdlineTests/linking_standard_solidity_unresolved_references/input.json index 597e7f96e..9a6ce25a7 100644 --- a/test/cmdlineTests/linking_standard_solidity_unresolved_references/input.json +++ b/test/cmdlineTests/linking_standard_solidity_unresolved_references/input.json @@ -28,7 +28,7 @@ }, "settings": { "libraries": { - "contract/test.sol": { + "A": { "L1": "0x1234567890123456789012345678901234567890" } }, From 4174f38b02fde0c30f9b0e04d3f34238f59a63ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 13 Nov 2020 22:30:50 +0100 Subject: [PATCH 29/29] StandardCompiler: Include file names in link references --- Changelog.md | 1 + libsolidity/interface/StandardCompiler.cpp | 3 +- .../args | 1 + .../err | 1 + .../input.yul | 6 ++ .../output | 22 ++++++ test/libsolidity/StandardCompiler.cpp | 68 ++++++++++++++++++- 7 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/args create mode 100644 test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/err create mode 100644 test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/input.yul create mode 100644 test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/output diff --git a/Changelog.md b/Changelog.md index 7dcf3313a..5c5d9d205 100644 --- a/Changelog.md +++ b/Changelog.md @@ -32,6 +32,7 @@ Bugfixes: * SMTChecker: Fix CHC false positives when branches are used inside modifiers. * Code generator: Fix missing creation dependency tracking for abstract contracts. * NatSpec: Fix internal error when inheriting return parameter documentation but the parameter names differ between base and inherited. + * Standard JSON: Fix library addresses specified in ``libraries`` being used for linking even if the file names do not match. ### 0.7.4 (2020-10-19) diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 86587839d..f3ba7db35 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -839,8 +839,7 @@ std::variant StandardCompiler: try { - // @TODO use libraries only for the given source - ret.libraries[library] = util::h160(address); + ret.libraries[sourceName + ":" + library] = util::h160(address); } catch (util::BadHexCharacter const&) { diff --git a/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/args b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/args new file mode 100644 index 000000000..cce3dedbf --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/args @@ -0,0 +1 @@ +--strict-assembly --libraries library1.sol:L:0x1234567890123456789012345678901234567890 diff --git a/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/err b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/err new file mode 100644 index 000000000..014a1178f --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/err @@ -0,0 +1 @@ +Warning: Yul is still experimental. Please use the output with care. diff --git a/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/input.yul b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/input.yul new file mode 100644 index 000000000..acb612857 --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/input.yul @@ -0,0 +1,6 @@ +object "a" { + code { + let addr1 := linkersymbol("library1.sol:L") + let addr2 := linkersymbol("library2.sol:L") + } +} diff --git a/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/output b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/output new file mode 100644 index 000000000..150355ba1 --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/output @@ -0,0 +1,22 @@ + +======= linking_strict_assembly_same_library_name_different_files_in_link_references/input.yul (EVM) ======= + +Pretty printed source: +object "a" { + code { + let addr1 := linkersymbol("library1.sol:L") + let addr2 := linkersymbol("library2.sol:L") + } +} + + +Binary representation: +73123456789012345678901234567890123456789073__$c3523432985587641d17c68161d2f700c5$__5050 + +Text representation: + linkerSymbol("f3ffc10c396a7cc41ae954b050792839d20947bf73497d30c49a9fda1ea477ec") + /* "linking_strict_assembly_same_library_name_different_files_in_link_references/input.yul":32:75 */ + linkerSymbol("c3523432985587641d17c68161d2f700c57aaf4ed21cda4f25d76193c831f97f") + /* "linking_strict_assembly_same_library_name_different_files_in_link_references/input.yul":22:133 */ + pop + pop diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 270295c3c..887e0ac32 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -903,6 +903,38 @@ BOOST_AUTO_TEST_CASE(library_linking) expectLinkReferences(contractResult, {{"library2.sol", {"L2"}}}); } +BOOST_AUTO_TEST_CASE(linking_yul) +{ + char const* input = R"( + { + "language": "Yul", + "settings": { + "libraries": { + "fileB": { + "L": "0x4200000000000000000000000000000000000001" + } + }, + "outputSelection": { + "fileA": { + "*": [ + "evm.bytecode.linkReferences" + ] + } + } + }, + "sources": { + "fileA": { + "content": "object \"a\" { code { let addr := linkersymbol(\"fileB:L\") } }" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_TEST(containsAtMostWarnings(result)); + Json::Value contractResult = getContractResult(result, "fileA", "a"); + expectLinkReferences(contractResult, {}); +} + BOOST_AUTO_TEST_CASE(linking_yul_empty_link_reference) { char const* input = R"( @@ -932,7 +964,7 @@ BOOST_AUTO_TEST_CASE(linking_yul_empty_link_reference) Json::Value result = compile(input); BOOST_TEST(containsAtMostWarnings(result)); Json::Value contractResult = getContractResult(result, "fileA", "a"); - expectLinkReferences(contractResult, {}); + expectLinkReferences(contractResult, {{"", {""}}}); } BOOST_AUTO_TEST_CASE(linking_yul_no_filename_in_link_reference) @@ -964,7 +996,39 @@ BOOST_AUTO_TEST_CASE(linking_yul_no_filename_in_link_reference) Json::Value result = compile(input); BOOST_TEST(containsAtMostWarnings(result)); Json::Value contractResult = getContractResult(result, "fileA", "a"); - expectLinkReferences(contractResult, {}); + expectLinkReferences(contractResult, {{"", {"L"}}}); +} + +BOOST_AUTO_TEST_CASE(linking_yul_same_library_name_different_files) +{ + char const* input = R"( + { + "language": "Yul", + "settings": { + "libraries": { + "fileB": { + "L": "0x4200000000000000000000000000000000000001" + } + }, + "outputSelection": { + "fileA": { + "*": [ + "evm.bytecode.linkReferences" + ] + } + } + }, + "sources": { + "fileA": { + "content": "object \"a\" { code { let addr := linkersymbol(\"fileC:L\") } }" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_TEST(containsAtMostWarnings(result)); + Json::Value contractResult = getContractResult(result, "fileA", "a"); + expectLinkReferences(contractResult, {{"fileC", {"L"}}}); } BOOST_AUTO_TEST_CASE(evm_version)