From 7f65f1cc02861077f84465d92b561dd1c0f1f758 Mon Sep 17 00:00:00 2001 From: Marenz Date: Thu, 1 Jul 2021 15:17:42 +0200 Subject: [PATCH 01/98] Fix code-start headers in documentation --- docs/contracts/events.rst | 2 +- docs/natspec-format.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/contracts/events.rst b/docs/contracts/events.rst index aae16fb8b..d282499dc 100644 --- a/docs/contracts/events.rst +++ b/docs/contracts/events.rst @@ -73,7 +73,7 @@ four indexed arguments rather than three. In particular, it is possible to "fake" the signature of another event using an anonymous event. -:: +.. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.4.21 <0.9.0; diff --git a/docs/natspec-format.rst b/docs/natspec-format.rst index 7007fe4a1..a49ddd895 100644 --- a/docs/natspec-format.rst +++ b/docs/natspec-format.rst @@ -58,7 +58,7 @@ The following example shows a contract and a function using all available tags. This may change in the future. -.. code:: Solidity +.. code-block:: Solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.2 < 0.9.0; From f62b80530bf2b5f1df053ea61fb1ac1067ef783c Mon Sep 17 00:00:00 2001 From: Marenz Date: Thu, 1 Jul 2021 15:18:32 +0200 Subject: [PATCH 02/98] Fix isolate_tests when run with single files and add usage information --- scripts/isolate_tests.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/isolate_tests.py b/scripts/isolate_tests.py index 494c2a7aa..57d1c991a 100755 --- a/scripts/isolate_tests.py +++ b/scripts/isolate_tests.py @@ -106,13 +106,18 @@ def extract_and_write(f, path): write_cases(f, cases) if __name__ == '__main__': + if len(sys.argv) == 1: + print("Usage: " + sys.argv[0] + " path-to-file-or-folder-to-extract-code-from [docs]") + exit(1) + path = sys.argv[1] docs = False if len(sys.argv) > 2 and sys.argv[2] == 'docs': docs = True if isfile(path): - extract_and_write(path, path) + _, tail = split(path) + extract_and_write(tail, path) else: for root, subdirs, files in os.walk(path): if '_build' in subdirs: From 8d5e82b40609393c88e16a4e60e3b7446e26535f Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 6 Jul 2021 12:23:12 +0200 Subject: [PATCH 03/98] Fix source location of builtin function calls. --- Changelog.md | 1 + libyul/backends/evm/EVMCodeTransform.cpp | 10 +++--- libyul/backends/evm/EVMDialect.cpp | 6 ++++ .../linking_strict_assembly/output | 1 + .../output | 1 + .../output | 3 +- .../output | 3 +- .../output | 3 +- test/cmdlineTests/object_compiler/output | 3 +- .../output | 2 +- .../output | 2 +- .../standard_yul_object/output.json | 3 +- .../standard_yul_object_name/output.json | 3 +- .../strict_asm_optimizer_steps/output | 5 +-- test/cmdlineTests/yul_verbatim/output | 2 ++ test/libyul/objectCompiler/datacopy.yul | 11 +++--- .../libyul/objectCompiler/dataoffset_code.yul | 4 +-- .../libyul/objectCompiler/dataoffset_data.yul | 4 +-- .../libyul/objectCompiler/dataoffset_self.yul | 4 +-- test/libyul/objectCompiler/datasize_code.yul | 4 +-- test/libyul/objectCompiler/datasize_data.yul | 4 +-- test/libyul/objectCompiler/datasize_self.yul | 4 +-- .../libyul/objectCompiler/function_series.yul | 4 +-- test/libyul/objectCompiler/jump_tags.yul | 6 ++-- test/libyul/objectCompiler/linkersymbol.yul | 3 +- .../objectCompiler/long_object_name.yul | 4 +-- test/libyul/objectCompiler/metadata.yul | 6 ++-- .../libyul/objectCompiler/subObjectAccess.yul | 35 ++++++++++--------- 28 files changed, 81 insertions(+), 60 deletions(-) diff --git a/Changelog.md b/Changelog.md index 6fab20f5e..45ef90635 100644 --- a/Changelog.md +++ b/Changelog.md @@ -16,6 +16,7 @@ Bugfixes: * Code Generator: Fix internal compiler error when passing a 32-byte hex literal or a zero literal to ``bytes.concat()`` by disallowing such literals. * Type Checker: Fix internal error and prevent static calls to unimplemented modifiers. * Yul Code Generator: Fix internal compiler error when using a long literal with bitwise negation. + * Yul Code Generator: Fix source location references for calls to builtin functions. ### 0.8.6 (2021-06-22) diff --git a/libyul/backends/evm/EVMCodeTransform.cpp b/libyul/backends/evm/EVMCodeTransform.cpp index d5530c52a..bc41eb9b2 100644 --- a/libyul/backends/evm/EVMCodeTransform.cpp +++ b/libyul/backends/evm/EVMCodeTransform.cpp @@ -237,16 +237,14 @@ void CodeTransform::operator()(FunctionCall const& _call) { yulAssert(m_scope, ""); + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_call.debugData)); if (BuiltinFunctionForEVM const* builtin = m_dialect.builtin(_call.functionName.name)) builtin->generateCode(_call, m_assembly, m_builtinContext, [&](Expression const& _expression) { visitExpression(_expression); }); else { - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_call.debugData)); - AbstractAssembly::LabelID returnLabel(numeric_limits::max()); // only used for evm 1.0 - - returnLabel = m_assembly.newLabelId(); + AbstractAssembly::LabelID returnLabel = m_assembly.newLabelId(); m_assembly.appendLabelReference(returnLabel); Scope::Function* function = nullptr; @@ -320,8 +318,6 @@ void CodeTransform::operator()(If const& _if) void CodeTransform::operator()(Switch const& _switch) { - //@TODO use JUMPV in EVM1.5? - visitExpression(*_switch.expression); int expressionHeight = m_assembly.stackHeight(); map caseBodies; @@ -416,6 +412,8 @@ void CodeTransform::operator()(FunctionDefinition const& _function) subTransform.setupReturnVariablesAndFunctionExit(); subTransform(_function.body); + + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_function.debugData)); if (!subTransform.m_stackErrors.empty()) { m_assembly.markAsInvalid(); diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index 90e5b2815..f4176c1b2 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -163,6 +163,7 @@ map createBuiltins(langutil::EVMVersion _evmVe ) { yulAssert(_call.arguments.size() == 1, ""); Expression const& arg = _call.arguments.front(); + _assembly.setSourceLocation(_call.debugData->location); _assembly.appendLinkerSymbol(std::get(arg).value.str()); })); @@ -192,6 +193,7 @@ map createBuiltins(langutil::EVMVersion _evmVe yulAssert(_call.arguments.size() == 1, ""); Expression const& arg = _call.arguments.front(); YulString dataName = std::get(arg).value; + _assembly.setSourceLocation(_call.debugData->location); if (_context.currentObject->name == dataName) _assembly.appendAssemblySize(); else @@ -214,6 +216,7 @@ map createBuiltins(langutil::EVMVersion _evmVe yulAssert(_call.arguments.size() == 1, ""); Expression const& arg = _call.arguments.front(); YulString dataName = std::get(arg).value; + _assembly.setSourceLocation(_call.debugData->location); if (_context.currentObject->name == dataName) _assembly.appendConstant(0); else @@ -276,6 +279,7 @@ map createBuiltins(langutil::EVMVersion _evmVe std::function ) { yulAssert(_call.arguments.size() == 1, ""); + _assembly.setSourceLocation(_call.debugData->location); _assembly.appendImmutable(std::get(_call.arguments.front()).value.str()); } )); @@ -382,6 +386,8 @@ BuiltinFunctionForEVM const* EVMDialect::verbatimFunction(size_t _arguments, siz for (Expression const& arg: _call.arguments | ranges::views::tail | ranges::views::reverse) _visitExpression(arg); Expression const& bytecode = _call.arguments.front(); + + _assembly.setSourceLocation(_call.debugData->location); _assembly.appendVerbatim( asBytes(std::get(bytecode).value.str()), _arguments, diff --git a/test/cmdlineTests/linking_strict_assembly/output b/test/cmdlineTests/linking_strict_assembly/output index 8a58b94e4..cb5e7bf96 100644 --- a/test/cmdlineTests/linking_strict_assembly/output +++ b/test/cmdlineTests/linking_strict_assembly/output @@ -14,6 +14,7 @@ Binary representation: 7312345678901234567890123456789012345678908060005550 Text representation: + /* "linking_strict_assembly/input.yul":44:79 */ linkerSymbol("f919ba91ac99f96129544b80b9516b27a80e376b9dc693819d0b18b7e0395612") /* "linking_strict_assembly/input.yul":98:102 */ dup1 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 index 871bacf2e..5d5b87c67 100644 --- 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 @@ -14,6 +14,7 @@ Binary representation: 7312345678901234567890123456789012345678908060005550 Text representation: + /* "linking_strict_assembly_no_file_name_in_link_reference/input.yul":44:61 */ linkerSymbol("8aa64f937099b65a4febc243a5ae0f2d6416bb9e473c30dd29c1ee498fb7c5a8") /* "linking_strict_assembly_no_file_name_in_link_reference/input.yul":80:84 */ dup1 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 index eb74885ee..521d26bfd 100644 --- a/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/output +++ b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/output @@ -16,8 +16,9 @@ Binary representation: 73111111111111111111111111111111111111111173222222222222222222222222222222222222222281600055806001555050 Text representation: + /* "linking_strict_assembly_same_library_name_different_files/input.yul":45:75 */ linkerSymbol("f3ffc10c396a7cc41ae954b050792839d20947bf73497d30c49a9fda1ea477ec") - /* "linking_strict_assembly_same_library_name_different_files/input.yul":32:75 */ + /* "linking_strict_assembly_same_library_name_different_files/input.yul":97:127 */ linkerSymbol("c3523432985587641d17c68161d2f700c57aaf4ed21cda4f25d76193c831f97f") /* "linking_strict_assembly_same_library_name_different_files/input.yul":146:151 */ dup2 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 index b5a44af48..b161a93f8 100644 --- 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 @@ -16,8 +16,9 @@ Binary representation: 73123456789012345678901234567890123456789073__$c3523432985587641d17c68161d2f700c5$__81600055806001555050 Text representation: + /* "linking_strict_assembly_same_library_name_different_files_in_link_references/input.yul":45:75 */ linkerSymbol("f3ffc10c396a7cc41ae954b050792839d20947bf73497d30c49a9fda1ea477ec") - /* "linking_strict_assembly_same_library_name_different_files_in_link_references/input.yul":32:75 */ + /* "linking_strict_assembly_same_library_name_different_files_in_link_references/input.yul":97:127 */ linkerSymbol("c3523432985587641d17c68161d2f700c57aaf4ed21cda4f25d76193c831f97f") /* "linking_strict_assembly_same_library_name_different_files_in_link_references/input.yul":146:151 */ dup2 diff --git a/test/cmdlineTests/linking_strict_assembly_unresolved_references/output b/test/cmdlineTests/linking_strict_assembly_unresolved_references/output index f927894ce..59acd6ae2 100644 --- a/test/cmdlineTests/linking_strict_assembly_unresolved_references/output +++ b/test/cmdlineTests/linking_strict_assembly_unresolved_references/output @@ -16,8 +16,9 @@ Binary representation: 73123456789012345678901234567890123456789073__$fb58009a6b1ecea3b9d99bedd645df4ec3$__81600055806001555050 Text representation: + /* "linking_strict_assembly_unresolved_references/input.yul":45:81 */ linkerSymbol("05b0326038374a21e0895480a58bda0768cdcc04c8d18f154362d1ca5223d245") - /* "linking_strict_assembly_unresolved_references/input.yul":32:81 */ + /* "linking_strict_assembly_unresolved_references/input.yul":103:139 */ linkerSymbol("fb58009a6b1ecea3b9d99bedd645df4ec308f17bc0087e5f39d078f77f809177") /* "linking_strict_assembly_unresolved_references/input.yul":158:163 */ dup2 diff --git a/test/cmdlineTests/object_compiler/output b/test/cmdlineTests/object_compiler/output index 53ccbe424..eb8a3286e 100644 --- a/test/cmdlineTests/object_compiler/output +++ b/test/cmdlineTests/object_compiler/output @@ -32,9 +32,10 @@ Text representation: 0x00 /* "object_compiler/input.yul":118:137 */ sstore - dataSize(sub_0) /* "object_compiler/input.yul":240:259 */ + dataSize(sub_0) dup1 + /* "object_compiler/input.yul":217:238 */ dataOffset(sub_0) /* "object_compiler/input.yul":125:126 */ 0x00 diff --git a/test/cmdlineTests/optimizer_inliner_dynamic_reference/output b/test/cmdlineTests/optimizer_inliner_dynamic_reference/output index 1f5597478..91000a7b7 100644 --- a/test/cmdlineTests/optimizer_inliner_dynamic_reference/output +++ b/test/cmdlineTests/optimizer_inliner_dynamic_reference/output @@ -194,7 +194,7 @@ sub_0: assembly { /* "#utility.yul":406:415 */ add swap1 - /* "#utility.yul":244:421 */ + /* "#utility.yul":196:421 */ jump // out /* "#utility.yul":426:553 */ tag_26: diff --git a/test/cmdlineTests/optimizer_inliner_dynamic_reference_constructor/output b/test/cmdlineTests/optimizer_inliner_dynamic_reference_constructor/output index f17c19176..e82ba9f72 100644 --- a/test/cmdlineTests/optimizer_inliner_dynamic_reference_constructor/output +++ b/test/cmdlineTests/optimizer_inliner_dynamic_reference_constructor/output @@ -199,7 +199,7 @@ sub_0: assembly { /* "#utility.yul":406:415 */ add swap1 - /* "#utility.yul":244:421 */ + /* "#utility.yul":196:421 */ jump // out /* "#utility.yul":426:553 */ tag_22: diff --git a/test/cmdlineTests/standard_yul_object/output.json b/test/cmdlineTests/standard_yul_object/output.json index c0a3434a9..49aaa6a74 100644 --- a/test/cmdlineTests/standard_yul_object/output.json +++ b/test/cmdlineTests/standard_yul_object/output.json @@ -1,4 +1,5 @@ -{"contracts":{"A":{"NamedObject":{"evm":{"assembly":" data_4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45 +{"contracts":{"A":{"NamedObject":{"evm":{"assembly":" /* \"A\":39:61 */ + data_4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45 /* \"A\":80:81 */ 0x00 /* \"A\":76:77 */ diff --git a/test/cmdlineTests/standard_yul_object_name/output.json b/test/cmdlineTests/standard_yul_object_name/output.json index 1a686914d..f2e85ed7c 100644 --- a/test/cmdlineTests/standard_yul_object_name/output.json +++ b/test/cmdlineTests/standard_yul_object_name/output.json @@ -1,4 +1,5 @@ -{"contracts":{"A":{"NamedObject":{"evm":{"assembly":" data_4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45 +{"contracts":{"A":{"NamedObject":{"evm":{"assembly":" /* \"A\":39:61 */ + data_4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45 /* \"A\":80:81 */ 0x00 /* \"A\":76:77 */ diff --git a/test/cmdlineTests/strict_asm_optimizer_steps/output b/test/cmdlineTests/strict_asm_optimizer_steps/output index 595199408..1ed63ba66 100644 --- a/test/cmdlineTests/strict_asm_optimizer_steps/output +++ b/test/cmdlineTests/strict_asm_optimizer_steps/output @@ -48,14 +48,15 @@ Text representation: revert /* "strict_asm_optimizer_steps/input.yul":58:60 */ tag_1: - /* "strict_asm_optimizer_steps/input.yul":98:163 */ + /* "strict_asm_optimizer_steps/input.yul":138:162 */ dataSize(sub_0) + /* "strict_asm_optimizer_steps/input.yul":110:136 */ dataOffset(sub_0) /* "strict_asm_optimizer_steps/input.yul":107:108 */ 0x00 /* "strict_asm_optimizer_steps/input.yul":98:163 */ codecopy - /* "strict_asm_optimizer_steps/input.yul":172:207 */ + /* "strict_asm_optimizer_steps/input.yul":182:206 */ dataSize(sub_0) /* "strict_asm_optimizer_steps/input.yul":179:180 */ 0x00 diff --git a/test/cmdlineTests/yul_verbatim/output b/test/cmdlineTests/yul_verbatim/output index 821e7461a..60fc7e6d1 100644 --- a/test/cmdlineTests/yul_verbatim/output +++ b/test/cmdlineTests/yul_verbatim/output @@ -32,6 +32,7 @@ Text representation: dup1 /* "yul_verbatim/input.yul":75:76 */ dup3 + /* "yul_verbatim/input.yul":53:80 */ verbatimbytecode_616263 /* "yul_verbatim/input.yul":95:96 */ dup3 @@ -39,6 +40,7 @@ Text representation: dup2 /* "yul_verbatim/input.yul":85:97 */ sstore + /* "yul_verbatim/input.yul":111:132 */ verbatimbytecode_646566 /* "yul_verbatim/input.yul":137:158 */ verbatimbytecode_78797a diff --git a/test/libyul/objectCompiler/datacopy.yul b/test/libyul/objectCompiler/datacopy.yul index 4499a3771..b6ec642f0 100644 --- a/test/libyul/objectCompiler/datacopy.yul +++ b/test/libyul/objectCompiler/datacopy.yul @@ -13,14 +13,15 @@ object "a" { } // ---- // Assembly: -// /* "source":26:73 */ +// /* "source":57:72 */ // dataSize(sub_0) +// /* "source":38:55 */ // dataOffset(sub_0) // /* "source":35:36 */ // 0x00 // /* "source":26:73 */ // codecopy -// /* "source":78:104 */ +// /* "source":88:103 */ // dataSize(sub_0) // /* "source":85:86 */ // 0x00 @@ -29,13 +30,13 @@ object "a" { // stop // // sub_0: assembly { -// /* "source":143:171 */ +// /* "source":153:170 */ // 0x00 // /* "source":150:151 */ // dup1 // /* "source":143:171 */ // sstore -// /* "source":178:206 */ +// /* "source":188:205 */ // 0x0d // /* "source":185:186 */ // 0x00 @@ -46,4 +47,4 @@ object "a" { // } // Bytecode: 600a600d600039600a6000f3fe60008055600d600052fe // Opcodes: PUSH1 0xA PUSH1 0xD PUSH1 0x0 CODECOPY PUSH1 0xA PUSH1 0x0 RETURN INVALID PUSH1 0x0 DUP1 SSTORE PUSH1 0xD PUSH1 0x0 MSTORE INVALID -// SourceMappings: 26:47:0:-:0;;35:1;26:47;78:26;85:1;78:26 +// SourceMappings: 57:15:0:-:0;38:17;35:1;26:47;88:15;85:1;78:26 diff --git a/test/libyul/objectCompiler/dataoffset_code.yul b/test/libyul/objectCompiler/dataoffset_code.yul index ed6242090..00184feab 100644 --- a/test/libyul/objectCompiler/dataoffset_code.yul +++ b/test/libyul/objectCompiler/dataoffset_code.yul @@ -7,7 +7,7 @@ object "a" { } // ---- // Assembly: -// /* "source":22:50 */ +// /* "source":32:49 */ // dataOffset(sub_0) // /* "source":29:30 */ // 0x00 @@ -27,4 +27,4 @@ object "a" { // } // Bytecode: 6006600055fe6008600055fe // Opcodes: PUSH1 0x6 PUSH1 0x0 SSTORE INVALID PUSH1 0x8 PUSH1 0x0 SSTORE INVALID -// SourceMappings: 22:28:0:-:0;29:1;22:28 +// SourceMappings: 32:17:0:-:0;29:1;22:28 diff --git a/test/libyul/objectCompiler/dataoffset_data.yul b/test/libyul/objectCompiler/dataoffset_data.yul index 6848ce9b9..2a029785b 100644 --- a/test/libyul/objectCompiler/dataoffset_data.yul +++ b/test/libyul/objectCompiler/dataoffset_data.yul @@ -4,7 +4,7 @@ object "a" { } // ---- // Assembly: -// /* "source":22:52 */ +// /* "source":32:51 */ // data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f // /* "source":29:30 */ // 0x00 @@ -14,4 +14,4 @@ object "a" { // data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421 // Bytecode: 6006600055fe48656c6c6f2c20576f726c6421 // Opcodes: PUSH1 0x6 PUSH1 0x0 SSTORE INVALID 0x48 PUSH6 0x6C6C6F2C2057 PUSH16 0x726C6421000000000000000000000000 -// SourceMappings: 22:30:0:-:0;29:1;22:30 +// SourceMappings: 32:19:0:-:0;29:1;22:30 diff --git a/test/libyul/objectCompiler/dataoffset_self.yul b/test/libyul/objectCompiler/dataoffset_self.yul index 4ee790d94..163cd7574 100644 --- a/test/libyul/objectCompiler/dataoffset_self.yul +++ b/test/libyul/objectCompiler/dataoffset_self.yul @@ -4,7 +4,7 @@ object "a" { } // ---- // Assembly: -// /* "source":22:48 */ +// /* "source":32:47 */ // 0x00 // /* "source":29:30 */ // dup1 @@ -14,4 +14,4 @@ object "a" { // data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421 // Bytecode: 60008055fe // Opcodes: PUSH1 0x0 DUP1 SSTORE INVALID -// SourceMappings: 22:26:0:-:0;29:1;22:26 +// SourceMappings: 32:15:0:-:0;29:1;22:26 diff --git a/test/libyul/objectCompiler/datasize_code.yul b/test/libyul/objectCompiler/datasize_code.yul index c48b1eba9..fc26e4eb9 100644 --- a/test/libyul/objectCompiler/datasize_code.yul +++ b/test/libyul/objectCompiler/datasize_code.yul @@ -7,7 +7,7 @@ object "a" { } // ---- // Assembly: -// /* "source":22:48 */ +// /* "source":32:47 */ // dataSize(sub_0) // /* "source":29:30 */ // 0x00 @@ -27,4 +27,4 @@ object "a" { // } // Bytecode: 6006600055fe // Opcodes: PUSH1 0x6 PUSH1 0x0 SSTORE INVALID -// SourceMappings: 22:26:0:-:0;29:1;22:26 +// SourceMappings: 32:15:0:-:0;29:1;22:26 diff --git a/test/libyul/objectCompiler/datasize_data.yul b/test/libyul/objectCompiler/datasize_data.yul index 191d162df..a9ba7b027 100644 --- a/test/libyul/objectCompiler/datasize_data.yul +++ b/test/libyul/objectCompiler/datasize_data.yul @@ -4,7 +4,7 @@ object "a" { } // ---- // Assembly: -// /* "source":22:50 */ +// /* "source":32:49 */ // 0x0d // /* "source":29:30 */ // 0x00 @@ -14,4 +14,4 @@ object "a" { // data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421 // Bytecode: 600d600055fe // Opcodes: PUSH1 0xD PUSH1 0x0 SSTORE INVALID -// SourceMappings: 22:28:0:-:0;29:1;22:28 +// SourceMappings: 32:17:0:-:0;29:1;22:28 diff --git a/test/libyul/objectCompiler/datasize_self.yul b/test/libyul/objectCompiler/datasize_self.yul index cfd96555a..d4a3c8d06 100644 --- a/test/libyul/objectCompiler/datasize_self.yul +++ b/test/libyul/objectCompiler/datasize_self.yul @@ -4,7 +4,7 @@ object "a" { } // ---- // Assembly: -// /* "source":22:46 */ +// /* "source":32:45 */ // bytecodeSize // /* "source":29:30 */ // 0x00 @@ -14,4 +14,4 @@ object "a" { // data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421 // Bytecode: 6006600055fe // Opcodes: PUSH1 0x6 PUSH1 0x0 SSTORE INVALID -// SourceMappings: 22:24:0:-:0;29:1;22:24 +// SourceMappings: 32:13:0:-:0;29:1;22:24 diff --git a/test/libyul/objectCompiler/function_series.yul b/test/libyul/objectCompiler/function_series.yul index 9efcf77af..cf4911997 100644 --- a/test/libyul/objectCompiler/function_series.yul +++ b/test/libyul/objectCompiler/function_series.yul @@ -13,12 +13,10 @@ object "Contract" { // /* "source":33:48 */ // jump(tag_1) // tag_2: -// /* "source":46:48 */ // tag_3: // jump // out // /* "source":53:68 */ // tag_4: -// /* "source":66:68 */ // tag_5: // jump // out // tag_1: @@ -30,4 +28,4 @@ object "Contract" { // sstore // Bytecode: 6009565b5b565b5b565b6001600055 // Opcodes: PUSH1 0x9 JUMP JUMPDEST JUMPDEST JUMP JUMPDEST JUMPDEST JUMP JUMPDEST PUSH1 0x1 PUSH1 0x0 SSTORE -// SourceMappings: 33:15:0:-:0;;;46:2;:::o;53:15::-;66:2;:::o;:::-;83:1;80;73:12 +// SourceMappings: 33:15:0:-:0;;;;:::o;53:::-;;:::o;:::-;83:1;80;73:12 diff --git a/test/libyul/objectCompiler/jump_tags.yul b/test/libyul/objectCompiler/jump_tags.yul index bd3920900..93aea8ea0 100644 --- a/test/libyul/objectCompiler/jump_tags.yul +++ b/test/libyul/objectCompiler/jump_tags.yul @@ -21,7 +21,7 @@ object "Contract" { // tag_5 // jump // in // tag_4: -// /* "source":46:54 */ +// /* "source":33:54 */ // tag_3: // jump // out // /* "source":59:104 */ @@ -49,7 +49,7 @@ object "Contract" { // tag_5 // jump // in // tag_8: -// /* "source":73:104 */ +// /* "source":59:104 */ // pop // tag_6: // jump // out @@ -64,4 +64,4 @@ object "Contract" { // tag_9: // Bytecode: 6026565b600b6001600e565b5b565b8015601857506024565b602260028201600e565b505b565b602e6001600e565b // Opcodes: PUSH1 0x26 JUMP JUMPDEST PUSH1 0xB PUSH1 0x1 PUSH1 0xE JUMP JUMPDEST JUMPDEST JUMP JUMPDEST DUP1 ISZERO PUSH1 0x18 JUMPI POP PUSH1 0x24 JUMP JUMPDEST PUSH1 0x22 PUSH1 0x2 DUP3 ADD PUSH1 0xE JUMP JUMPDEST POP JUMPDEST JUMP JUMPDEST PUSH1 0x2E PUSH1 0x1 PUSH1 0xE JUMP JUMPDEST -// SourceMappings: 33:21:0:-:0;;;48:4;50:1;48:4;:::i;:::-;46:8;:::o;59:45::-;78:1;75:2;;;82:5;;;75:2;90:12;99:1;96;92:9;90:12;:::i;:::-;73:31;;:::o;:::-;109:4;111:1;109:4;:::i;:::- +// SourceMappings: 33:21:0:-:0;;;48:4;50:1;48:4;:::i;:::-;33:21;:::o;59:45::-;78:1;75:2;;;82:5;;;75:2;90:12;99:1;96;92:9;90:12;:::i;:::-;59:45;;:::o;:::-;109:4;111:1;109:4;:::i;:::- diff --git a/test/libyul/objectCompiler/linkersymbol.yul b/test/libyul/objectCompiler/linkersymbol.yul index 65bf004ef..9dac37448 100644 --- a/test/libyul/objectCompiler/linkersymbol.yul +++ b/test/libyul/objectCompiler/linkersymbol.yul @@ -7,6 +7,7 @@ object "a" { } // ---- // Assembly: +// /* "source":44:79 */ // linkerSymbol("f919ba91ac99f96129544b80b9516b27a80e376b9dc693819d0b18b7e0395612") // /* "source":109:119 */ // 0x18530aaf @@ -39,4 +40,4 @@ object "a" { // pop // Bytecode: 7300000000000000000000000000000000000000006318530aaf60e31b60805260006080600460806000855af15050 // Opcodes: PUSH20 0x0 PUSH4 0x18530AAF PUSH1 0xE3 SHL PUSH1 0x80 MSTORE PUSH1 0x0 PUSH1 0x80 PUSH1 0x4 PUSH1 0x80 PUSH1 0x0 DUP6 GAS CALL POP POP -// SourceMappings: :::-:0;109:10:0;104:3;100:20;95:3;88:33;179:1;174:3;171:1;166:3;163:1;157:4;150:5;145:36;22:165; +// SourceMappings: 44:35:0:-:0;109:10;104:3;100:20;95:3;88:33;179:1;174:3;171:1;166:3;163:1;157:4;150:5;145:36;22:165; diff --git a/test/libyul/objectCompiler/long_object_name.yul b/test/libyul/objectCompiler/long_object_name.yul index 0aa8c4405..ad8266d1e 100644 --- a/test/libyul/objectCompiler/long_object_name.yul +++ b/test/libyul/objectCompiler/long_object_name.yul @@ -10,7 +10,7 @@ object "t" { // optimizationPreset: full // ---- // Assembly: -// /* "source":23:147 */ +// /* "source":33:146 */ // dataSize(sub_0) // /* "source":30:31 */ // 0x00 @@ -22,4 +22,4 @@ object "t" { // } // Bytecode: 6000600055fe // Opcodes: PUSH1 0x0 PUSH1 0x0 SSTORE INVALID -// SourceMappings: 23:124:0:-:0;30:1;23:124 +// SourceMappings: 33:113:0:-:0;30:1;23:124 diff --git a/test/libyul/objectCompiler/metadata.yul b/test/libyul/objectCompiler/metadata.yul index 9facd54e4..ac66e4bda 100644 --- a/test/libyul/objectCompiler/metadata.yul +++ b/test/libyul/objectCompiler/metadata.yul @@ -21,8 +21,9 @@ object "A" { } // ---- // Assembly: +// /* "source":35:48 */ // 0x0e -// /* "source":26:48 */ +// /* "source":62:75 */ // 0x03 // /* "source":90:91 */ // dup2 @@ -44,6 +45,7 @@ object "A" { // data_e1629b9dda060bb30c7908346f6af189c16773fa148d3366701fbaa35d54f3c8 414243 // // sub_0: assembly { +// /* "source":157:176 */ // data_211450822d7f8c345093893187e7e1fbebc4ec67af72601920194be14104e336 // /* "source":193:194 */ // dup1 @@ -62,4 +64,4 @@ object "A" { // auxdata: 0x4d32 // Bytecode: 600e600381600055806020555050fe4d32 // Opcodes: PUSH1 0xE PUSH1 0x3 DUP2 PUSH1 0x0 SSTORE DUP1 PUSH1 0x20 SSTORE POP POP INVALID 0x4D ORIGIN -// SourceMappings: :::-:0;26:22:0;90:1;87;80:12;108:1;104:2;97:13;20:94; +// SourceMappings: 35:13:0:-:0;62;90:1;87;80:12;108:1;104:2;97:13;20:94; diff --git a/test/libyul/objectCompiler/subObjectAccess.yul b/test/libyul/objectCompiler/subObjectAccess.yul index cadb6cc41..1e28ed5d5 100644 --- a/test/libyul/objectCompiler/subObjectAccess.yul +++ b/test/libyul/objectCompiler/subObjectAccess.yul @@ -67,24 +67,25 @@ object "A" { } // ---- // Assembly: +// /* "source":37:52 */ // 0x00 -// /* "source":26:52 */ +// /* "source":68:81 */ // bytecodeSize -// /* "source":57:81 */ +// /* "source":97:112 */ // dataOffset(sub_0) -// /* "source":86:112 */ +// /* "source":128:141 */ // dataSize(sub_0) -// /* "source":117:141 */ +// /* "source":158:175 */ // dataOffset(sub_0.sub_0) -// /* "source":146:175 */ +// /* "source":192:207 */ // dataSize(sub_0.sub_0) -// /* "source":180:207 */ +// /* "source":224:241 */ // dataOffset(sub_0.sub_1) -// /* "source":212:241 */ +// /* "source":258:273 */ // dataSize(sub_0.sub_1) -// /* "source":246:273 */ +// /* "source":291:310 */ // dataOffset(sub_0.sub_0.sub_0) -// /* "source":278:310 */ +// /* "source":328:345 */ // dataSize(sub_0.sub_0.sub_0) // /* "source":361:364 */ // dup10 @@ -156,16 +157,17 @@ object "A" { // data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421 // // sub_0: assembly { +// /* "source":659:674 */ // dataOffset(sub_0) -// /* "source":648:674 */ +// /* "source":692:705 */ // dataSize(sub_0) -// /* "source":681:705 */ +// /* "source":723:738 */ // dataOffset(sub_1) -// /* "source":712:738 */ +// /* "source":756:769 */ // dataSize(sub_1) -// /* "source":745:769 */ +// /* "source":788:805 */ // dataOffset(sub_0.sub_0) -// /* "source":776:805 */ +// /* "source":824:839 */ // dataSize(sub_0.sub_0) // /* "source":857:860 */ // dup6 @@ -212,8 +214,9 @@ object "A" { // stop // // sub_0: assembly { +// /* "source":1052:1067 */ // dataOffset(sub_0) -// /* "source":1041:1067 */ +// /* "source":1087:1100 */ // dataSize(sub_0) // /* "source":1120:1123 */ // dup2 @@ -248,4 +251,4 @@ object "A" { // } // Bytecode: 600060996045603f60866013608560016084600189600055886020558760405586606055856080558460a0558360c0558260e055816101005580610120556101406000f3fe602a6013603d6001603e600185600055846020558360405582606055816080558060a05560c06000f3fe60126001816000558060205560406000f3fefefefefefe60126001816000558060205560406000f3fefe // Opcodes: PUSH1 0x0 PUSH1 0x99 PUSH1 0x45 PUSH1 0x3F PUSH1 0x86 PUSH1 0x13 PUSH1 0x85 PUSH1 0x1 PUSH1 0x84 PUSH1 0x1 DUP10 PUSH1 0x0 SSTORE DUP9 PUSH1 0x20 SSTORE DUP8 PUSH1 0x40 SSTORE DUP7 PUSH1 0x60 SSTORE DUP6 PUSH1 0x80 SSTORE DUP5 PUSH1 0xA0 SSTORE DUP4 PUSH1 0xC0 SSTORE DUP3 PUSH1 0xE0 SSTORE DUP2 PUSH2 0x100 SSTORE DUP1 PUSH2 0x120 SSTORE PUSH2 0x140 PUSH1 0x0 RETURN INVALID PUSH1 0x2A PUSH1 0x13 PUSH1 0x3D PUSH1 0x1 PUSH1 0x3E PUSH1 0x1 DUP6 PUSH1 0x0 SSTORE DUP5 PUSH1 0x20 SSTORE DUP4 PUSH1 0x40 SSTORE DUP3 PUSH1 0x60 SSTORE DUP2 PUSH1 0x80 SSTORE DUP1 PUSH1 0xA0 SSTORE PUSH1 0xC0 PUSH1 0x0 RETURN INVALID PUSH1 0x12 PUSH1 0x1 DUP2 PUSH1 0x0 SSTORE DUP1 PUSH1 0x20 SSTORE PUSH1 0x40 PUSH1 0x0 RETURN INVALID INVALID INVALID INVALID INVALID INVALID PUSH1 0x12 PUSH1 0x1 DUP2 PUSH1 0x0 SSTORE DUP1 PUSH1 0x20 SSTORE PUSH1 0x40 PUSH1 0x0 RETURN INVALID INVALID -// SourceMappings: :::-:0;26:26:0;57:24;86:26;117:24;146:29;180:27;212:29;246:27;278:32;361:3;358:1;351:14;381:3;377:2;370:15;401:3;397:2;390:15;421:3;417:2;410:15;442:4;437:3;430:17;464:4;459:3;452:17;486:4;481:3;474:17;508:4;503:3;496:17;530:5;525:3;518:18;553:5;548:3;541:18;574:3;571:1;564:14 +// SourceMappings: 37:15:0:-:0;68:13;97:15;128:13;158:17;192:15;224:17;258:15;291:19;328:17;361:3;358:1;351:14;381:3;377:2;370:15;401:3;397:2;390:15;421:3;417:2;410:15;442:4;437:3;430:17;464:4;459:3;452:17;486:4;481:3;474:17;508:4;503:3;496:17;530:5;525:3;518:18;553:5;548:3;541:18;574:3;571:1;564:14 From 5291ca2dd4ee6b9ce9ebef54a44610df848ec8b1 Mon Sep 17 00:00:00 2001 From: Marenz Date: Thu, 1 Jul 2021 15:19:38 +0200 Subject: [PATCH 04/98] isolate_tests: Extract code blocks from documentation using code block header --- docs/contributing.rst | 2 +- scripts/docs_version_pragma_check.sh | 2 +- scripts/isolate_tests.py | 106 +++++++++--------- .../docker-scripts/isolate_tests.py | 3 + test/cmdlineTests.sh | 4 +- test/docsCodeStyle.sh | 2 +- 6 files changed, 59 insertions(+), 60 deletions(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index eaae6db3e..62d5bad1f 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -324,7 +324,7 @@ from the documentation or the other tests: # extract from tests: path/to/solidity/scripts/isolate_tests.py path/to/solidity/test/libsolidity/SolidityEndToEndTest.cpp # extract from documentation: - path/to/solidity/scripts/isolate_tests.py path/to/solidity/docs docs + path/to/solidity/scripts/isolate_tests.py path/to/solidity/docs The AFL documentation states that the corpus (the initial input files) should not be too large. The files themselves should not be larger than 1 kB and there should be diff --git a/scripts/docs_version_pragma_check.sh b/scripts/docs_version_pragma_check.sh index 8ffd0afb6..d7ca3f2ad 100755 --- a/scripts/docs_version_pragma_check.sh +++ b/scripts/docs_version_pragma_check.sh @@ -136,7 +136,7 @@ SOLTMPDIR=$(mktemp -d) ( set -e cd "$SOLTMPDIR" - "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ docs + "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ getAllAvailableVersions diff --git a/scripts/isolate_tests.py b/scripts/isolate_tests.py index 57d1c991a..fdb398c4b 100755 --- a/scripts/isolate_tests.py +++ b/scripts/isolate_tests.py @@ -10,7 +10,8 @@ import sys import re import os import hashlib -from os.path import join, isfile, split +from os.path import join, isfile, split, basename +from argparse import ArgumentParser def extract_test_cases(path): with open(path, encoding="utf8", errors='ignore', mode='r', newline='') as file: @@ -35,54 +36,50 @@ def extract_test_cases(path): return tests -# Contract sources are indented by 4 spaces. -# Look for `pragma solidity`, `contract`, `library` or `interface` -# and abort a line not indented properly. +# Extract code examples based on a start marker +# up until we reach EOF or a line that is not empty and doesn't start with 4 +# spaces. def extract_docs_cases(path): + beginMarkers = ['.. code-block:: solidity', '::'] + immediatelyAfterMarker = False insideBlock = False - insideBlockParameters = False - pastBlockParameters = False - extractedLines = [] tests = [] # Collect all snippets of indented blocks - with open(path, mode='r', errors='ignore', encoding='utf8', newline='') as f: lines = f.read().splitlines() - for l in lines: - if l != '': - if not insideBlock and l.startswith(' '): - # start new test - extractedLines += [''] - insideBlockParameters = False - pastBlockParameters = False - insideBlock = l.startswith(' ') - if insideBlock: - if not pastBlockParameters: - # NOTE: For simplicity this allows blank lines between block parameters even - # though Sphinx does not. This does not matter since the first non-empty line in - # a Solidity file cannot start with a colon anyway. - if not l.strip().startswith(':') and (l != '' or not insideBlockParameters): - insideBlockParameters = False - pastBlockParameters = True - else: - insideBlockParameters = True - if not insideBlockParameters: - extractedLines[-1] += l + '\n' + for line in lines: + if insideBlock: + if immediatelyAfterMarker: + # Skip Sphinx instructions and empty lines between them + if line == '' or line.lstrip().startswith(":"): + continue + + if line == '' or line.startswith(" "): + tests[-1] += line + "\n" + immediatelyAfterMarker = False + else: + insideBlock = False + elif any(map(line.lower().startswith, beginMarkers)): + insideBlock = True + immediatelyAfterMarker = True + tests += [''] codeStart = "(// SPDX-License-Identifier:|pragma solidity|contract.*{|library.*{|interface.*{)" - # Filter all tests that do not contain Solidity or are indented incorrectly. - for lines in extractedLines: - if re.search(r'^\s{0,3}' + codeStart, lines, re.MULTILINE): + for test in tests: + if re.search(r'^\s{0,3}' + codeStart, test, re.MULTILINE): print("Indentation error in " + path + ":") - print(lines) + print(test) exit(1) - if re.search(r'^\s{4}' + codeStart, lines, re.MULTILINE): - tests.append(lines) - return tests + # Filter out tests that are not supposed to be compilable. + return [ + test.lstrip("\n") + for test in tests + if re.search(r'^\s{4}' + codeStart, test, re.MULTILINE) is not None + ] def write_cases(f, tests): cleaned_filename = f.replace(".","_").replace("-","_").replace(" ","_").lower() @@ -94,30 +91,30 @@ def write_cases(f, tests): with open(sol_filename, mode='w', encoding='utf8', newline='') as fi: fi.write(remainder) -def extract_and_write(f, path): - if docs: +def extract_and_write(path): + if path.lower().endswith('.rst'): cases = extract_docs_cases(path) + elif path.endswith('.sol'): + with open(path, mode='r', encoding='utf8', newline='') as f: + cases = [f.read()] else: - if f.endswith('.sol'): - with open(path, mode='r', encoding='utf8', newline='') as _f: - cases = [_f.read()] - else: - cases = extract_test_cases(path) - write_cases(f, cases) + cases = extract_test_cases(path) + + write_cases(basename(path), cases) if __name__ == '__main__': - if len(sys.argv) == 1: - print("Usage: " + sys.argv[0] + " path-to-file-or-folder-to-extract-code-from [docs]") - exit(1) + script_description = ( + "Reads Solidity, C++ or RST source files and extracts compilable solidity and yul code blocks from them. " + "Can be used to generate test cases to validade code examples. " + ) - path = sys.argv[1] - docs = False - if len(sys.argv) > 2 and sys.argv[2] == 'docs': - docs = True + parser = ArgumentParser(description=script_description) + parser.add_argument(dest='path', help='Path to file or directory to look for code in.') + options = parser.parse_args() + path = options.path if isfile(path): - _, tail = split(path) - extract_and_write(tail, path) + extract_and_write(path) else: for root, subdirs, files in os.walk(path): if '_build' in subdirs: @@ -125,8 +122,7 @@ if __name__ == '__main__': if 'compilationTests' in subdirs: subdirs.remove('compilationTests') for f in files: - _, tail = split(f) - if tail == "invalid_utf8_sequence.sol": + if basename(f) == "invalid_utf8_sequence.sol": continue # ignore the test with broken utf-8 encoding path = join(root, f) - extract_and_write(f, path) + extract_and_write(path) diff --git a/scripts/wasm-rebuild/docker-scripts/isolate_tests.py b/scripts/wasm-rebuild/docker-scripts/isolate_tests.py index 973140e51..568c61423 100755 --- a/scripts/wasm-rebuild/docker-scripts/isolate_tests.py +++ b/scripts/wasm-rebuild/docker-scripts/isolate_tests.py @@ -1,4 +1,7 @@ #!/usr/bin/env python2 +# +# Not actively tested or maintained. Exists in case we want to rebuild an +# ancient release. import sys import re diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index 4d99961ca..7a14fe6c8 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -361,7 +361,7 @@ SOLTMPDIR=$(mktemp -d) ( set -e cd "$SOLTMPDIR" - "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ docs + "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ developmentVersion=$("$REPO_ROOT/scripts/get_version.sh") for f in *.sol @@ -510,7 +510,7 @@ SOLTMPDIR=$(mktemp -d) set -e cd "$SOLTMPDIR" "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/test/ - "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ docs + "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ echo ./*.sol | xargs -P 4 -n 50 "${SOLIDITY_BUILD_DIR}/test/tools/solfuzzer" --quiet --input-files echo ./*.sol | xargs -P 4 -n 50 "${SOLIDITY_BUILD_DIR}/test/tools/solfuzzer" --without-optimizer --quiet --input-files diff --git a/test/docsCodeStyle.sh b/test/docsCodeStyle.sh index 9e082b7fb..33b787e25 100755 --- a/test/docsCodeStyle.sh +++ b/test/docsCodeStyle.sh @@ -22,7 +22,7 @@ SOLTMPDIR=$(mktemp -d) ( set -e cd "$SOLTMPDIR" - "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ docs + "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ if npm -v >/dev/null 2>&1; then if npm list -g | grep solhint >/dev/null 2>&1; then From b0c70883f7e0234757ffd6d6f8dc385b1d38e2b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 7 Jun 2021 12:31:05 +0200 Subject: [PATCH 05/98] Copy CommandLineInterface.cpp/.h to CommandLineParser.cpp/.h --- solc/CommandLineParser.cpp | 2106 ++++++++++++++++++++++++++++++++++++ solc/CommandLineParser.h | 142 +++ 2 files changed, 2248 insertions(+) create mode 100644 solc/CommandLineParser.cpp create mode 100644 solc/CommandLineParser.h diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp new file mode 100644 index 000000000..7c2cad4ef --- /dev/null +++ b/solc/CommandLineParser.cpp @@ -0,0 +1,2106 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +/** + * @author Lefteris + * @author Gav Wood + * @date 2014 + * Solidity command line interface. + */ +#include + +#include "solidity/BuildInfo.h" +#include "license.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#ifdef _WIN32 // windows + #include + #define isatty _isatty + #define fileno _fileno +#else // unix + #include +#endif + +#include +#include +#include + +#if !defined(STDERR_FILENO) + #define STDERR_FILENO 2 +#endif + +using namespace std; +using namespace solidity; +using namespace solidity::util; +using namespace solidity::langutil; + +namespace po = boost::program_options; + +namespace solidity::frontend +{ + +bool g_hasOutput = false; + +namespace +{ + +std::ostream& sout() +{ + g_hasOutput = true; + return cout; +} + +std::ostream& serr(bool _used = true) +{ + if (_used) + g_hasOutput = true; + return cerr; +} + +} + +#define cout +#define cerr + +static string const g_stdinFileNameStr = ""; +static string const g_strAbi = "abi"; +static string const g_strAllowPaths = "allow-paths"; +static string const g_strBasePath = "base-path"; +static string const g_strAsm = "asm"; +static string const g_strAsmJson = "asm-json"; +static string const g_strAssemble = "assemble"; +static string const g_strAst = "ast"; +static string const g_strAstCompactJson = "ast-compact-json"; +static string const g_strBinary = "bin"; +static string const g_strBinaryRuntime = "bin-runtime"; +static string const g_strCombinedJson = "combined-json"; +static string const g_strCompactJSON = "compact-format"; +static string const g_strContracts = "contracts"; +static string const g_strErrorRecovery = "error-recovery"; +static string const g_strEVM = "evm"; +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"; +static string const g_strHelp = "help"; +static string const g_strImportAst = "import-ast"; +static string const g_strInputFile = "input-file"; +static string const g_strInterface = "interface"; +static string const g_strYul = "yul"; +static string const g_strYulDialect = "yul-dialect"; +static string const g_strIR = "ir"; +static string const g_strIROptimized = "ir-optimized"; +static string const g_strIPFS = "ipfs"; +static string const g_strLicense = "license"; +static string const g_strLibraries = "libraries"; +static string const g_strLink = "link"; +static string const g_strMachine = "machine"; +static string const g_strMetadata = "metadata"; +static string const g_strMetadataHash = "metadata-hash"; +static string const g_strMetadataLiteral = "metadata-literal"; +static string const g_strModelCheckerContracts = "model-checker-contracts"; +static string const g_strModelCheckerEngine = "model-checker-engine"; +static string const g_strModelCheckerTargets = "model-checker-targets"; +static string const g_strModelCheckerTimeout = "model-checker-timeout"; +static string const g_strNatspecDev = "devdoc"; +static string const g_strNatspecUser = "userdoc"; +static string const g_strNone = "none"; +static string const g_strNoOptimizeYul = "no-optimize-yul"; +static string const g_strOpcodes = "opcodes"; +static string const g_strOptimize = "optimize"; +static string const g_strOptimizeRuns = "optimize-runs"; +static string const g_strOptimizeYul = "optimize-yul"; +static string const g_strYulOptimizations = "yul-optimizations"; +static string const g_strOutputDir = "output-dir"; +static string const g_strOverwrite = "overwrite"; +static string const g_strRevertStrings = "revert-strings"; +static string const g_strStorageLayout = "storage-layout"; +static string const g_strStopAfter = "stop-after"; +static string const g_strParsing = "parsing"; + +/// Possible arguments to for --revert-strings +static set const g_revertStringsArgs +{ + revertStringsToString(RevertStrings::Default), + revertStringsToString(RevertStrings::Strip), + revertStringsToString(RevertStrings::Debug), + revertStringsToString(RevertStrings::VerboseDebug) +}; + +static string const g_strSignatureHashes = "hashes"; +static string const g_strSources = "sources"; +static string const g_strSourceList = "sourceList"; +static string const g_strSrcMap = "srcmap"; +static string const g_strSrcMapRuntime = "srcmap-runtime"; +static string const g_strFunDebug = "function-debug"; +static string const g_strFunDebugRuntime = "function-debug-runtime"; +static string const g_strStandardJSON = "standard-json"; +static string const g_strStrictAssembly = "strict-assembly"; +static string const g_strSwarm = "swarm"; +static string const g_strPrettyJson = "pretty-json"; +static string const g_strVersion = "version"; +static string const g_strIgnoreMissingFiles = "ignore-missing"; +static string const g_strColor = "color"; +static string const g_strNoColor = "no-color"; +static string const g_strErrorIds = "error-codes"; + +static string const g_argAbi = g_strAbi; +static string const g_argPrettyJson = g_strPrettyJson; +static string const g_argAllowPaths = g_strAllowPaths; +static string const g_argBasePath = g_strBasePath; +static string const g_argAsm = g_strAsm; +static string const g_argAsmJson = g_strAsmJson; +static string const g_argAssemble = g_strAssemble; +static string const g_argAstCompactJson = g_strAstCompactJson; +static string const g_argBinary = g_strBinary; +static string const g_argBinaryRuntime = g_strBinaryRuntime; +static string const g_argCombinedJson = g_strCombinedJson; +static string const g_argCompactJSON = g_strCompactJSON; +static string const g_argErrorRecovery = g_strErrorRecovery; +static string const g_argGas = g_strGas; +static string const g_argHelp = g_strHelp; +static string const g_argImportAst = g_strImportAst; +static string const g_argInputFile = g_strInputFile; +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; +static string const g_argMetadata = g_strMetadata; +static string const g_argMetadataHash = g_strMetadataHash; +static string const g_argMetadataLiteral = g_strMetadataLiteral; +static string const g_argModelCheckerContracts = g_strModelCheckerContracts; +static string const g_argModelCheckerEngine = g_strModelCheckerEngine; +static string const g_argModelCheckerTargets = g_strModelCheckerTargets; +static string const g_argModelCheckerTimeout = g_strModelCheckerTimeout; +static string const g_argNatspecDev = g_strNatspecDev; +static string const g_argNatspecUser = g_strNatspecUser; +static string const g_argOpcodes = g_strOpcodes; +static string const g_argOptimize = g_strOptimize; +static string const g_argOptimizeRuns = g_strOptimizeRuns; +static string const g_argOutputDir = g_strOutputDir; +static string const g_argSignatureHashes = g_strSignatureHashes; +static string const g_argStandardJSON = g_strStandardJSON; +static string const g_argStorageLayout = g_strStorageLayout; +static string const g_argStrictAssembly = g_strStrictAssembly; +static string const g_argVersion = g_strVersion; +static string const g_stdinFileName = g_stdinFileNameStr; +static string const g_argIgnoreMissingFiles = g_strIgnoreMissingFiles; +static string const g_argColor = g_strColor; +static string const g_argNoColor = g_strNoColor; +static string const g_argErrorIds = g_strErrorIds; + +/// Possible arguments to for --combined-json +static set const g_combinedJsonArgs +{ + g_strAbi, + g_strAsm, + g_strAst, + g_strBinary, + g_strBinaryRuntime, + g_strCompactJSON, + g_strFunDebug, + g_strFunDebugRuntime, + g_strGeneratedSources, + g_strGeneratedSourcesRuntime, + g_strInterface, + g_strMetadata, + g_strNatspecUser, + g_strNatspecDev, + g_strOpcodes, + g_strSignatureHashes, + g_strSrcMap, + g_strSrcMapRuntime, + g_strStorageLayout +}; + +/// Possible arguments to for --machine +static set const g_machineArgs +{ + g_strEVM, + g_strEwasm +}; + +/// Possible arguments to for --yul-dialect +static set const g_yulDialectArgs +{ + g_strEVM, + g_strEwasm +}; + +/// Possible arguments to for --metadata-hash +static set const g_metadataHashArgs +{ + g_strIPFS, + g_strSwarm, + g_strNone +}; + +static void version() +{ + sout() << + "solc, the solidity compiler commandline interface" << + endl << + "Version: " << + solidity::frontend::VersionString << + endl; + exit(0); +} + +static void license() +{ + sout() << otherLicenses << endl; + // This is a static variable generated by cmake from LICENSE.txt + sout() << licenseText << endl; + exit(0); +} + +static bool needsHumanTargetedStdout(po::variables_map const& _args) +{ + if (_args.count(g_argGas)) + return true; + if (_args.count(g_argOutputDir)) + return false; + for (string const& arg: { + g_argAbi, + g_argAsm, + g_argAsmJson, + g_argBinary, + g_argBinaryRuntime, + g_argMetadata, + g_argNatspecUser, + g_argNatspecDev, + g_argOpcodes, + g_argSignatureHashes, + g_argStorageLayout + }) + if (_args.count(arg)) + return true; + return false; +} + +namespace +{ + +bool checkMutuallyExclusive(boost::program_options::variables_map const& args, std::string const& _optionA, std::string const& _optionB) +{ + if (args.count(_optionA) && args.count(_optionB)) + { + serr() << "Option " << _optionA << " and " << _optionB << " are mutually exclusive." << endl; + return false; + } + + return true; +} + +} + +void CommandLineInterface::handleBinary(string const& _contract) +{ + if (m_args.count(g_argBinary)) + { + if (m_args.count(g_argOutputDir)) + createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin", objectWithLinkRefsHex(m_compiler->object(_contract))); + else + { + sout() << "Binary:" << endl; + sout() << objectWithLinkRefsHex(m_compiler->object(_contract)) << endl; + } + } + if (m_args.count(g_argBinaryRuntime)) + { + if (m_args.count(g_argOutputDir)) + createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin-runtime", objectWithLinkRefsHex(m_compiler->runtimeObject(_contract))); + else + { + sout() << "Binary of the runtime part:" << endl; + sout() << objectWithLinkRefsHex(m_compiler->runtimeObject(_contract)) << endl; + } + } +} + +void CommandLineInterface::handleOpcode(string const& _contract) +{ + if (m_args.count(g_argOutputDir)) + createFile(m_compiler->filesystemFriendlyName(_contract) + ".opcode", evmasm::disassemble(m_compiler->object(_contract).bytecode)); + else + { + sout() << "Opcodes:" << endl; + sout() << std::uppercase << evmasm::disassemble(m_compiler->object(_contract).bytecode); + sout() << endl; + } +} + +void CommandLineInterface::handleIR(string const& _contractName) +{ + if (!m_args.count(g_argIR)) + return; + + if (m_args.count(g_argOutputDir)) + createFile(m_compiler->filesystemFriendlyName(_contractName) + ".yul", m_compiler->yulIR(_contractName)); + else + { + sout() << "IR:" << endl; + sout() << m_compiler->yulIR(_contractName) << endl; + } +} + +void CommandLineInterface::handleIROptimized(string const& _contractName) +{ + if (!m_args.count(g_argIROptimized)) + return; + + if (m_args.count(g_argOutputDir)) + createFile(m_compiler->filesystemFriendlyName(_contractName) + "_opt.yul", m_compiler->yulIROptimized(_contractName)); + else + { + sout() << "Optimized IR:" << endl; + sout() << m_compiler->yulIROptimized(_contractName) << endl; + } +} + +void CommandLineInterface::handleEwasm(string const& _contractName) +{ + if (!m_args.count(g_argEwasm)) + return; + + if (m_args.count(g_argOutputDir)) + { + createFile(m_compiler->filesystemFriendlyName(_contractName) + ".wast", m_compiler->ewasm(_contractName)); + createFile( + m_compiler->filesystemFriendlyName(_contractName) + ".wasm", + asString(m_compiler->ewasmObject(_contractName).bytecode) + ); + } + else + { + sout() << "Ewasm text:" << endl; + sout() << m_compiler->ewasm(_contractName) << endl; + sout() << "Ewasm binary (hex): " << m_compiler->ewasmObject(_contractName).toHex() << endl; + } +} + +void CommandLineInterface::handleBytecode(string const& _contract) +{ + if (m_args.count(g_argOpcodes)) + handleOpcode(_contract); + if (m_args.count(g_argBinary) || m_args.count(g_argBinaryRuntime)) + handleBinary(_contract); +} + +void CommandLineInterface::handleSignatureHashes(string const& _contract) +{ + if (!m_args.count(g_argSignatureHashes)) + return; + + Json::Value methodIdentifiers = m_compiler->methodIdentifiers(_contract); + string out; + for (auto const& name: methodIdentifiers.getMemberNames()) + out += methodIdentifiers[name].asString() + ": " + name + "\n"; + + if (m_args.count(g_argOutputDir)) + createFile(m_compiler->filesystemFriendlyName(_contract) + ".signatures", out); + else + sout() << "Function signatures:" << endl << out; +} + +void CommandLineInterface::handleMetadata(string const& _contract) +{ + if (!m_args.count(g_argMetadata)) + return; + + string data = m_compiler->metadata(_contract); + if (m_args.count(g_argOutputDir)) + createFile(m_compiler->filesystemFriendlyName(_contract) + "_meta.json", data); + else + sout() << "Metadata:" << endl << data << endl; +} + +void CommandLineInterface::handleABI(string const& _contract) +{ + if (!m_args.count(g_argAbi)) + return; + + string data = jsonCompactPrint(removeNullMembers(m_compiler->contractABI(_contract))); + if (m_args.count(g_argOutputDir)) + createFile(m_compiler->filesystemFriendlyName(_contract) + ".abi", data); + else + sout() << "Contract JSON ABI" << endl << data << endl; +} + +void CommandLineInterface::handleStorageLayout(string const& _contract) +{ + if (!m_args.count(g_argStorageLayout)) + return; + + string data = jsonCompactPrint(removeNullMembers(m_compiler->storageLayout(_contract))); + if (m_args.count(g_argOutputDir)) + createFile(m_compiler->filesystemFriendlyName(_contract) + "_storage.json", data); + else + sout() << "Contract Storage Layout:" << endl << data << endl; +} + +void CommandLineInterface::handleNatspec(bool _natspecDev, string const& _contract) +{ + std::string argName; + std::string suffix; + std::string title; + + if (_natspecDev) + { + argName = g_argNatspecDev; + suffix = ".docdev"; + title = "Developer Documentation"; + } + else + { + argName = g_argNatspecUser; + suffix = ".docuser"; + title = "User Documentation"; + } + + if (m_args.count(argName)) + { + std::string output = jsonPrettyPrint( + removeNullMembers( + _natspecDev ? + m_compiler->natspecDev(_contract) : + m_compiler->natspecUser(_contract) + ) + ); + + if (m_args.count(g_argOutputDir)) + createFile(m_compiler->filesystemFriendlyName(_contract) + suffix, output); + else + { + sout() << title << endl; + sout() << output << endl; + } + + } +} + +void CommandLineInterface::handleGasEstimation(string const& _contract) +{ + Json::Value estimates = m_compiler->gasEstimates(_contract); + sout() << "Gas estimation:" << endl; + + if (estimates["creation"].isObject()) + { + Json::Value creation = estimates["creation"]; + sout() << "construction:" << endl; + sout() << " " << creation["executionCost"].asString(); + sout() << " + " << creation["codeDepositCost"].asString(); + sout() << " = " << creation["totalCost"].asString() << endl; + } + + if (estimates["external"].isObject()) + { + Json::Value externalFunctions = estimates["external"]; + sout() << "external:" << endl; + for (auto const& name: externalFunctions.getMemberNames()) + { + if (name.empty()) + sout() << " fallback:\t"; + else + sout() << " " << name << ":\t"; + sout() << externalFunctions[name].asString() << endl; + } + } + + if (estimates["internal"].isObject()) + { + Json::Value internalFunctions = estimates["internal"]; + sout() << "internal:" << endl; + for (auto const& name: internalFunctions.getMemberNames()) + { + sout() << " " << name << ":\t"; + sout() << internalFunctions[name].asString() << endl; + } + } +} + +bool CommandLineInterface::readInputFilesAndConfigureRemappings() +{ + bool ignoreMissing = m_args.count(g_argIgnoreMissingFiles); + bool addStdin = false; + if (m_args.count(g_argInputFile)) + for (string path: m_args[g_argInputFile].as>()) + { + auto eq = find(path.begin(), path.end(), '='); + if (eq != path.end()) + { + if (auto r = ImportRemapper::parseRemapping(path)) + m_remappings.emplace_back(std::move(*r)); + else + { + serr() << "Invalid remapping: \"" << path << "\"." << endl; + return false; + } + + string remappingTarget(eq + 1, path.end()); + m_fileReader.allowDirectory(boost::filesystem::path(remappingTarget).remove_filename()); + } + else if (path == "-") + addStdin = true; + else + { + auto infile = boost::filesystem::path(path); + if (!boost::filesystem::exists(infile)) + { + if (!ignoreMissing) + { + serr() << infile << " is not found." << endl; + return false; + } + else + serr() << infile << " is not found. Skipping." << endl; + + continue; + } + + if (!boost::filesystem::is_regular_file(infile)) + { + if (!ignoreMissing) + { + serr() << infile << " is not a valid file." << endl; + return false; + } + else + serr() << infile << " is not a valid file. Skipping." << endl; + + continue; + } + + // NOTE: we ignore the FileNotFound exception as we manually check above + m_fileReader.setSource(infile, readFileAsString(infile.string())); + m_fileReader.allowDirectory(boost::filesystem::path(boost::filesystem::canonical(infile).string()).remove_filename()); + } + } + + if (addStdin) + m_fileReader.setSource(g_stdinFileName, readUntilEnd(cin)); + + if (m_fileReader.sourceCodes().size() == 0) + { + serr() << "No input files given. If you wish to use the standard input please specify \"-\" explicitly." << endl; + return false; + } + + return true; +} + +bool CommandLineInterface::parseLibraryOption(string const& _input) +{ + namespace fs = boost::filesystem; + string data = _input; + try + { + if (fs::is_regular_file(_input)) + data = readFileAsString(_input); + } + catch (fs::filesystem_error const&) + { + // Thrown e.g. if path is too long. + } + catch (FileNotFound const&) + { + // Should not happen if `fs::is_regular_file` is correct. + } + catch (NotAFile const&) + { + // Should not happen if `fs::is_regular_file` is correct. + } + + vector libraries; + boost::split(libraries, data, boost::is_space() || boost::is_any_of(","), boost::token_compress_on); + for (string const& lib: libraries) + if (!lib.empty()) + { + //search for equal sign or last colon in string as our binaries output placeholders in the form of file=Name or file:Name + //so we need to search for `=` or `:` in the string + auto separator = lib.rfind('='); + bool isSeparatorEqualSign = true; + if (separator == string::npos) + { + separator = lib.rfind(':'); + if (separator == string::npos) + { + serr() << "Equal sign separator missing in library address specifier \"" << lib << "\"" << endl; + return false; + } + else + isSeparatorEqualSign = false; // separator is colon + } + else + if (lib.rfind('=') != lib.find('=')) + { + serr() << "Only one equal sign \"=\" is allowed in the address string \"" << lib << "\"." << endl; + return false; + } + + string libName(lib.begin(), lib.begin() + static_cast(separator)); + 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(separator) + 1, lib.end()); + boost::trim(addrString); + if (addrString.empty()) + { + serr() << "Empty address provided for library \"" << libName << "\"." << endl; + serr() << "Note that there should not be any whitespace after the " << (isSeparatorEqualSign ? "equal sign" : "colon") << "." << endl; + return false; + } + + if (addrString.substr(0, 2) == "0x") + addrString = addrString.substr(2); + else + { + serr() << "The address " << addrString << " is not prefixed with \"0x\"." << endl; + serr() << "Note that the address must be prefixed with \"0x\"." << endl; + return false; + } + + if (addrString.length() != 40) + { + serr() << "Invalid length for address for library \"" << libName << "\": " << addrString.length() << " instead of 40 characters." << endl; + return false; + } + if (!passesAddressChecksum(addrString, false)) + { + serr() << "Invalid checksum on address for library \"" << libName << "\": " << addrString << endl; + serr() << "The correct checksum is " << getChecksummedAddress(addrString) << endl; + return false; + } + bytes binAddr = fromHex(addrString); + h160 address(binAddr, h160::AlignRight); + if (binAddr.size() > 20 || address == h160()) + { + serr() << "Invalid address for library \"" << libName << "\": " << addrString << endl; + return false; + } + m_libraries[libName] = address; + } + + return true; +} + +map CommandLineInterface::parseAstFromInput() +{ + map sourceJsons; + map tmpSources; + + for (SourceCode const& sourceCode: m_fileReader.sourceCodes() | ranges::views::values) + { + Json::Value ast; + astAssert(jsonParseStrict(sourceCode, ast), "Input file could not be parsed to JSON"); + astAssert(ast.isMember("sources"), "Invalid Format for import-JSON: Must have 'sources'-object"); + + for (auto& src: ast["sources"].getMemberNames()) + { + std::string astKey = ast["sources"][src].isMember("ast") ? "ast" : "AST"; + + astAssert(ast["sources"][src].isMember(astKey), "astkey is not member"); + astAssert(ast["sources"][src][astKey]["nodeType"].asString() == "SourceUnit", "Top-level node should be a 'SourceUnit'"); + astAssert(sourceJsons.count(src) == 0, "All sources must have unique names"); + sourceJsons.emplace(src, move(ast["sources"][src][astKey])); + tmpSources[src] = util::jsonCompactPrint(ast); + } + } + + m_fileReader.setSources(tmpSources); + + return sourceJsons; +} + +void CommandLineInterface::createFile(string const& _fileName, string const& _data) +{ + namespace fs = boost::filesystem; + + 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; + m_error = true; + return; + } + ofstream outFile(pathName); + outFile << _data; + if (!outFile) + { + serr() << "Could not write to file \"" << pathName << "\"." << endl; + m_error = true; + return; + } +} + +void CommandLineInterface::createJson(string const& _fileName, string const& _json) +{ + createFile(boost::filesystem::basename(_fileName) + string(".json"), _json); +} + +bool CommandLineInterface::parseArguments(int _argc, char** _argv) +{ + g_hasOutput = false; + + // Declare the supported options. + po::options_description desc((R"(solc, the Solidity commandline compiler. + +This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you +are welcome to redistribute it under certain conditions. See 'solc --)" + g_strLicense + R"(' +for details. + +Usage: solc [options] [input_file...] +Compiles the given Solidity input files (or the standard input if none given or +"-" is used as a file name) and outputs the components specified in the options +at standard output or in files in the output directory, if specified. +Imports are automatically read from the filesystem, but it is also possible to +remap paths using the context:prefix=path syntax. +Example: +solc --)" + g_argBinary + R"( -o /tmp/solcoutput dapp-bin=/usr/local/lib/dapp-bin contract.sol + +General Information)").c_str(), + po::options_description::m_default_line_length, + po::options_description::m_default_line_length - 23 + ); + desc.add_options() + (g_argHelp.c_str(), "Show help message and exit.") + (g_argVersion.c_str(), "Show version and exit.") + (g_strLicense.c_str(), "Show licensing information and exit.") + ; + + po::options_description inputOptions("Input Options"); + inputOptions.add_options() + ( + g_argBasePath.c_str(), + po::value()->value_name("path"), + "Use the given path as the root of the source tree instead of the root of the filesystem." + ) + ( + g_argAllowPaths.c_str(), + po::value()->value_name("path(s)"), + "Allow a given path for imports. A list of paths can be supplied by separating them with a comma." + ) + ( + g_argIgnoreMissingFiles.c_str(), + "Ignore missing files." + ) + ( + g_argErrorRecovery.c_str(), + "Enables additional parser error recovery." + ) + ; + desc.add(inputOptions); + + po::options_description outputOptions("Output Options"); + outputOptions.add_options() + ( + (g_argOutputDir + ",o").c_str(), + po::value()->value_name("path"), + "If given, creates one file per component and contract/file at the specified directory." + ) + ( + g_strOverwrite.c_str(), + "Overwrite existing files (used together with -o)." + ) + ( + g_strEVMVersion.c_str(), + po::value()->value_name("version")->default_value(EVMVersion{}.name()), + "Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon, " + "byzantium, constantinople, petersburg, istanbul 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, ",")), + "Strip revert (and require) reason strings or add additional debugging information." + ) + ( + g_strStopAfter.c_str(), + po::value()->value_name("stage"), + "Stop execution after the given compiler stage. Valid options: \"parsing\"." + ) + ; + desc.add(outputOptions); + + po::options_description alternativeInputModes("Alternative Input Modes"); + alternativeInputModes.add_options() + ( + g_argStandardJSON.c_str(), + "Switch to Standard JSON input / output mode, ignoring all options. " + "It reads from standard input, if no input file was given, otherwise it reads from the provided input file. The result will be written to standard output." + ) + ( + g_argLink.c_str(), + ("Switch to linker mode, ignoring all options apart from --" + g_argLibraries + " " + "and modify binaries in place.").c_str() + ) + ( + g_argAssemble.c_str(), + ("Switch to assembly mode, ignoring all options except " + "--" + g_argMachine + ", --" + g_strYulDialect + ", --" + g_argOptimize + " and --" + g_strYulOptimizations + " " + "and assumes input is assembly.").c_str() + ) + ( + g_argYul.c_str(), + ("Switch to Yul mode, ignoring all options except " + "--" + g_argMachine + ", --" + g_strYulDialect + ", --" + g_argOptimize + " and --" + g_strYulOptimizations + " " + "and assumes input is Yul.").c_str() + ) + ( + g_argStrictAssembly.c_str(), + ("Switch to strict assembly mode, ignoring all options except " + "--" + g_argMachine + ", --" + g_strYulDialect + ", --" + g_argOptimize + " and --" + g_strYulOptimizations + " " + "and assumes input is strict assembly.").c_str() + ) + ( + g_argImportAst.c_str(), + ("Import ASTs to be compiled, assumes input holds the AST in compact JSON format. " + "Supported Inputs is the output of the --" + g_argStandardJSON + " or the one produced by " + "--" + g_argCombinedJson + " " + g_strAst + "," + g_strCompactJSON).c_str() + ) + ; + desc.add(alternativeInputModes); + + po::options_description assemblyModeOptions("Assembly Mode Options"); + assemblyModeOptions.add_options() + ( + g_argMachine.c_str(), + po::value()->value_name(boost::join(g_machineArgs, ",")), + "Target machine in assembly or Yul mode." + ) + ( + g_strYulDialect.c_str(), + po::value()->value_name(boost::join(g_yulDialectArgs, ",")), + "Input dialect to use in assembly or yul mode." + ) + ; + desc.add(assemblyModeOptions); + + po::options_description linkerModeOptions("Linker Mode Options"); + linkerModeOptions.add_options() + ( + g_argLibraries.c_str(), + po::value>()->value_name("libs"), + "Direct string or file containing library addresses. Syntax: " + "=
[, or whitespace] ...\n" + "Address is interpreted as a hex string prefixed by 0x." + ) + ; + desc.add(linkerModeOptions); + + po::options_description outputFormatting("Output Formatting"); + outputFormatting.add_options() + ( + g_argPrettyJson.c_str(), + "Output JSON in pretty format. Currently it only works with the combined JSON output." + ) + ( + g_argColor.c_str(), + "Force colored output." + ) + ( + g_argNoColor.c_str(), + "Explicitly disable colored output, disabling terminal auto-detection." + ) + ( + g_argErrorIds.c_str(), + "Output error codes." + ) + ; + desc.add(outputFormatting); + + po::options_description outputComponents("Output Components"); + outputComponents.add_options() + (g_argAstCompactJson.c_str(), "AST of all source files in a compact JSON format.") + (g_argAsm.c_str(), "EVM assembly of the contracts.") + (g_argAsmJson.c_str(), "EVM assembly of the contracts in JSON format.") + (g_argOpcodes.c_str(), "Opcodes of the contracts.") + (g_argBinary.c_str(), "Binary of the contracts in hex.") + (g_argBinaryRuntime.c_str(), "Binary of the runtime part of the contracts in hex.") + (g_argAbi.c_str(), "ABI specification of the contracts.") + (g_argIR.c_str(), "Intermediate Representation (IR) of all contracts (EXPERIMENTAL).") + (g_argIROptimized.c_str(), "Optimized intermediate Representation (IR) of all contracts (EXPERIMENTAL).") + (g_argEwasm.c_str(), "Ewasm text representation of all contracts (EXPERIMENTAL).") + (g_argSignatureHashes.c_str(), "Function signature hashes of the contracts.") + (g_argNatspecUser.c_str(), "Natspec user documentation of all contracts.") + (g_argNatspecDev.c_str(), "Natspec developer documentation of all contracts.") + (g_argMetadata.c_str(), "Combined Metadata JSON whose Swarm hash is stored on-chain.") + (g_argStorageLayout.c_str(), "Slots, offsets and types of the contract's state variables.") + ; + desc.add(outputComponents); + + po::options_description extraOutput("Extra Output"); + extraOutput.add_options() + ( + g_argGas.c_str(), + "Print an estimate of the maximal gas usage for each function." + ) + ( + g_argCombinedJson.c_str(), + po::value()->value_name(boost::join(g_combinedJsonArgs, ",")), + "Output a single json document containing the specified information." + ) + ; + desc.add(extraOutput); + + po::options_description metadataOptions("Metadata Options"); + metadataOptions.add_options() + ( + g_argMetadataHash.c_str(), + po::value()->value_name(boost::join(g_metadataHashArgs, ",")), + "Choose hash method for the bytecode metadata or disable it." + ) + ( + g_argMetadataLiteral.c_str(), + "Store referenced sources as literal data in the metadata output." + ) + ; + desc.add(metadataOptions); + + po::options_description optimizerOptions("Optimizer Options"); + optimizerOptions.add_options() + ( + g_argOptimize.c_str(), + "Enable bytecode optimizer." + ) + ( + g_argOptimizeRuns.c_str(), + // TODO: The type in OptimiserSettings is size_t but we only accept values up to 2**32-1 + // on the CLI and in Standard JSON. We should just switch to uint32_t everywhere. + po::value()->value_name("n")->default_value(static_cast(OptimiserSettings{}.expectedExecutionsPerDeployment)), + "Set for how many contract runs to optimize. " + "Lower values will optimize more for initial deployment cost, higher values will optimize more for high-frequency usage." + ) + ( + g_strOptimizeYul.c_str(), + ("Legacy option, ignored. Use the general --" + g_argOptimize + " to enable Yul optimizer.").c_str() + ) + ( + g_strNoOptimizeYul.c_str(), + "Disable Yul optimizer in Solidity." + ) + ( + g_strYulOptimizations.c_str(), + po::value()->value_name("steps"), + "Forces yul optimizer to use the specified sequence of optimization steps instead of the built-in one." + ) + ; + desc.add(optimizerOptions); + + po::options_description smtCheckerOptions("Model Checker Options"); + smtCheckerOptions.add_options() + ( + g_strModelCheckerContracts.c_str(), + po::value()->value_name("default,:")->default_value("default"), + "Select which contracts should be analyzed using the form :." + "Multiple pairs : can be selected at the same time, separated by a comma " + "and no spaces." + ) + ( + g_strModelCheckerEngine.c_str(), + po::value()->value_name("all,bmc,chc,none")->default_value("none"), + "Select model checker engine." + ) + ( + g_strModelCheckerTargets.c_str(), + po::value()->value_name("default,constantCondition,underflow,overflow,divByZero,balance,assert,popEmptyArray,outOfBounds")->default_value("default"), + "Select model checker verification targets. " + "Multiple targets can be selected at the same time, separated by a comma " + "and no spaces." + ) + ( + g_strModelCheckerTimeout.c_str(), + po::value()->value_name("ms"), + "Set model checker timeout per query in milliseconds. " + "The default is a deterministic resource limit. " + "A timeout of 0 means no resource/time restrictions for any query." + ) + ; + desc.add(smtCheckerOptions); + + po::options_description allOptions = desc; + allOptions.add_options()(g_argInputFile.c_str(), po::value>(), "input file"); + + // All positional options should be interpreted as input files + po::positional_options_description filesPositions; + filesPositions.add(g_argInputFile.c_str(), -1); + + // parse the compiler arguments + try + { + po::command_line_parser cmdLineParser(_argc, _argv); + cmdLineParser.style(po::command_line_style::default_style & (~po::command_line_style::allow_guessing)); + cmdLineParser.options(allOptions).positional(filesPositions); + po::store(cmdLineParser.run(), m_args); + } + catch (po::error const& _exception) + { + serr() << _exception.what() << endl; + return false; + } + + if (!checkMutuallyExclusive(m_args, g_argColor, g_argNoColor)) + return false; + + static vector const conflictingWithStopAfter{ + g_argBinary, + g_argIR, + g_argIROptimized, + g_argEwasm, + g_argGas, + g_argAsm, + g_argAsmJson, + g_argOpcodes + }; + + for (auto& option: conflictingWithStopAfter) + if (!checkMutuallyExclusive(m_args, g_strStopAfter, option)) + return false; + + m_coloredOutput = !m_args.count(g_argNoColor) && (isatty(STDERR_FILENO) || m_args.count(g_argColor)); + + m_withErrorIds = m_args.count(g_argErrorIds); + + if (m_args.count(g_argHelp) || (isatty(fileno(stdin)) && _argc == 1)) + { + sout() << desc; + return false; + } + + if (m_args.count(g_argVersion)) + { + version(); + return false; + } + + if (m_args.count(g_strLicense)) + { + license(); + return false; + } + + if (m_args.count(g_strRevertStrings)) + { + string revertStringsString = m_args[g_strRevertStrings].as(); + std::optional revertStrings = revertStringsFromString(revertStringsString); + if (!revertStrings) + { + serr() << "Invalid option for --" << g_strRevertStrings << ": " << revertStringsString << endl; + return false; + } + if (*revertStrings == RevertStrings::VerboseDebug) + { + serr() << "Only \"default\", \"strip\" and \"debug\" are implemented for --" << g_strRevertStrings << " for now." << endl; + return false; + } + m_revertStrings = *revertStrings; + } + + if (m_args.count(g_argCombinedJson)) + { + vector requests; + for (string const& item: boost::split(requests, m_args[g_argCombinedJson].as(), boost::is_any_of(","))) + if (!g_combinedJsonArgs.count(item)) + { + serr() << "Invalid option to --" << g_argCombinedJson << ": " << item << endl; + return false; + } + } + po::notify(m_args); + + return true; +} + +bool CommandLineInterface::processInput() +{ + if (m_args.count(g_argBasePath)) + { + boost::filesystem::path const fspath{m_args[g_argBasePath].as()}; + if (!boost::filesystem::is_directory(fspath)) + { + serr() << "Base path must be a directory: \"" << fspath << "\"\n"; + return false; + } + m_fileReader.setBasePath(fspath); + } + + if (m_args.count(g_argAllowPaths)) + { + vector paths; + for (string const& path: boost::split(paths, m_args[g_argAllowPaths].as(), boost::is_any_of(","))) + { + auto filesystem_path = boost::filesystem::path(path); + // If the given path had a trailing slash, the Boost filesystem + // path will have it's last component set to '.'. This breaks + // path comparison in later parts of the code, so we need to strip + // it. + if (filesystem_path.filename() == ".") + filesystem_path.remove_filename(); + m_fileReader.allowDirectory(filesystem_path); + } + } + + if (m_args.count(g_strStopAfter)) + { + if (m_args[g_strStopAfter].as() != "parsing") + { + serr() << "Valid options for --" << g_strStopAfter << " are: \"parsing\".\n"; + return false; + } + else + m_stopAfter = CompilerStack::State::Parsed; + } + + vector const exclusiveModes = { + g_argStandardJSON, + g_argLink, + g_argAssemble, + g_argStrictAssembly, + g_argYul, + g_argImportAst, + }; + if (countEnabledOptions(exclusiveModes) > 1) + { + serr() << "The following options are mutually exclusive: " << joinOptionNames(exclusiveModes) << ". "; + serr() << "Select at most one." << endl; + return false; + } + + if (m_args.count(g_argStandardJSON)) + { + vector inputFiles; + string jsonFile; + if (m_args.count(g_argInputFile)) + inputFiles = m_args[g_argInputFile].as>(); + if (inputFiles.size() == 1) + jsonFile = inputFiles[0]; + else if (inputFiles.size() > 1) + { + serr() << "If --" << g_argStandardJSON << " is used, only zero or one input files are supported." << endl; + return false; + } + string input; + if (jsonFile.empty()) + input = readUntilEnd(cin); + else + { + try + { + input = readFileAsString(jsonFile); + } + catch (FileNotFound const&) + { + serr() << "File not found: " << jsonFile << endl; + return false; + } + catch (NotAFile const&) + { + serr() << "Not a regular file: " << jsonFile << endl; + return false; + } + } + StandardCompiler compiler(m_fileReader.reader()); + sout() << compiler.compile(std::move(input)) << endl; + return true; + } + + if (!readInputFilesAndConfigureRemappings()) + return false; + + if (m_args.count(g_argLibraries)) + for (string const& library: m_args[g_argLibraries].as>()) + if (!parseLibraryOption(library)) + return false; + + if (m_args.count(g_strEVMVersion)) + { + string versionOptionStr = m_args[g_strEVMVersion].as(); + std::optional versionOption = langutil::EVMVersion::fromString(versionOptionStr); + if (!versionOption) + { + serr() << "Invalid option for --" << g_strEVMVersion << ": " << versionOptionStr << endl; + return false; + } + m_evmVersion = *versionOption; + } + + if (m_args.count(g_argAssemble) || m_args.count(g_argStrictAssembly) || m_args.count(g_argYul)) + { + vector const nonAssemblyModeOptions = { + // TODO: The list is not complete. Add more. + g_argOutputDir, + g_argGas, + g_argCombinedJson, + g_strOptimizeYul, + g_strNoOptimizeYul, + }; + if (countEnabledOptions(nonAssemblyModeOptions) >= 1) + { + auto optionEnabled = [&](string const& name){ return m_args.count(name) > 0; }; + auto enabledOptions = boost::copy_range>(nonAssemblyModeOptions | boost::adaptors::filtered(optionEnabled)); + + serr() << "The following options are invalid in assembly mode: "; + serr() << joinOptionNames(enabledOptions) << "."; + if (m_args.count(g_strOptimizeYul) || m_args.count(g_strNoOptimizeYul)) + serr() << " Optimization is disabled by default and can be enabled with --" << g_argOptimize << "." << endl; + serr() << endl; + return false; + } + + // switch to assembly mode + m_onlyAssemble = true; + using Input = yul::AssemblyStack::Language; + using Machine = yul::AssemblyStack::Machine; + Input inputLanguage = m_args.count(g_argYul) ? Input::Yul : (m_args.count(g_argStrictAssembly) ? Input::StrictAssembly : Input::Assembly); + Machine targetMachine = Machine::EVM; + bool optimize = m_args.count(g_argOptimize); + + optional yulOptimiserSteps; + if (m_args.count(g_strYulOptimizations)) + { + if (!optimize) + { + serr() << "--" << g_strYulOptimizations << " is invalid if Yul optimizer is disabled" << endl; + return false; + } + + try + { + yul::OptimiserSuite::validateSequence(m_args[g_strYulOptimizations].as()); + } + catch (yul::OptimizerException const& _exception) + { + serr() << "Invalid optimizer step sequence in --" << g_strYulOptimizations << ": " << _exception.what() << endl; + return false; + } + + yulOptimiserSteps = m_args[g_strYulOptimizations].as(); + } + + if (m_args.count(g_argMachine)) + { + string machine = m_args[g_argMachine].as(); + if (machine == g_strEVM) + targetMachine = Machine::EVM; + else if (machine == g_strEwasm) + targetMachine = Machine::Ewasm; + else + { + serr() << "Invalid option for --" << g_argMachine << ": " << machine << endl; + return false; + } + } + if (targetMachine == Machine::Ewasm && inputLanguage == Input::StrictAssembly) + inputLanguage = Input::Ewasm; + if (m_args.count(g_strYulDialect)) + { + string dialect = m_args[g_strYulDialect].as(); + if (dialect == g_strEVM) + inputLanguage = Input::StrictAssembly; + else if (dialect == g_strEwasm) + { + inputLanguage = Input::Ewasm; + if (targetMachine != Machine::Ewasm) + { + serr() << "If you select Ewasm as --" << g_strYulDialect << ", "; + serr() << "--" << g_argMachine << " has to be Ewasm as well." << endl; + return false; + } + } + else + { + serr() << "Invalid option for --" << g_strYulDialect << ": " << dialect << endl; + return false; + } + } + if (optimize && (inputLanguage != Input::StrictAssembly && inputLanguage != Input::Ewasm)) + { + serr() << + "Optimizer can only be used for strict assembly. Use --" << + g_strStrictAssembly << + "." << + endl; + return false; + } + if (targetMachine == Machine::Ewasm && inputLanguage != Input::StrictAssembly && inputLanguage != Input::Ewasm) + { + serr() << "The selected input language is not directly supported when targeting the Ewasm machine "; + serr() << "and automatic translation is not available." << endl; + return false; + } + serr() << + "Warning: Yul is still experimental. Please use the output with care." << + endl; + + return assemble(inputLanguage, targetMachine, optimize, yulOptimiserSteps); + } + else if (countEnabledOptions({g_strYulDialect, g_argMachine}) >= 1) + { + serr() << "--" << g_strYulDialect << " and --" << g_argMachine << " "; + serr() << "are only valid in assembly mode." << endl; + return false; + } + + if (m_args.count(g_argLink)) + { + // switch to linker mode + m_onlyLink = true; + return link(); + } + + if (m_args.count(g_argMetadataHash)) + { + string hashStr = m_args[g_argMetadataHash].as(); + if (hashStr == g_strIPFS) + m_metadataHash = CompilerStack::MetadataHash::IPFS; + else if (hashStr == g_strSwarm) + m_metadataHash = CompilerStack::MetadataHash::Bzzr1; + else if (hashStr == g_strNone) + m_metadataHash = CompilerStack::MetadataHash::None; + else + { + serr() << "Invalid option for --" << g_argMetadataHash << ": " << hashStr << endl; + return false; + } + } + + if (m_args.count(g_argModelCheckerContracts)) + { + string contractsStr = m_args[g_argModelCheckerContracts].as(); + optional contracts = ModelCheckerContracts::fromString(contractsStr); + if (!contracts) + { + serr() << "Invalid option for --" << g_argModelCheckerContracts << ": " << contractsStr << endl; + return false; + } + m_modelCheckerSettings.contracts = move(*contracts); + } + + + if (m_args.count(g_argModelCheckerEngine)) + { + string engineStr = m_args[g_argModelCheckerEngine].as(); + optional engine = ModelCheckerEngine::fromString(engineStr); + if (!engine) + { + serr() << "Invalid option for --" << g_argModelCheckerEngine << ": " << engineStr << endl; + return false; + } + m_modelCheckerSettings.engine = *engine; + } + + if (m_args.count(g_argModelCheckerTargets)) + { + string targetsStr = m_args[g_argModelCheckerTargets].as(); + optional targets = ModelCheckerTargets::fromString(targetsStr); + if (!targets) + { + serr() << "Invalid option for --" << g_argModelCheckerTargets << ": " << targetsStr << endl; + return false; + } + m_modelCheckerSettings.targets = *targets; + } + + if (m_args.count(g_argModelCheckerTimeout)) + m_modelCheckerSettings.timeout = m_args[g_argModelCheckerTimeout].as(); + + m_compiler = make_unique(m_fileReader.reader()); + + SourceReferenceFormatter formatter(serr(false), m_coloredOutput, m_withErrorIds); + + try + { + if (m_args.count(g_argMetadataLiteral) > 0) + m_compiler->useMetadataLiteralSources(true); + if (m_args.count(g_argMetadataHash)) + m_compiler->setMetadataHash(m_metadataHash); + if ( + m_args.count(g_argModelCheckerContracts) || + m_args.count(g_argModelCheckerEngine) || + m_args.count(g_argModelCheckerTargets) || + m_args.count(g_argModelCheckerTimeout) + ) + m_compiler->setModelCheckerSettings(m_modelCheckerSettings); + if (m_args.count(g_argInputFile)) + m_compiler->setRemappings(m_remappings); + + 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 + + m_compiler->enableIRGeneration(m_args.count(g_argIR) || m_args.count(g_argIROptimized)); + m_compiler->enableEwasmGeneration(m_args.count(g_argEwasm)); + + OptimiserSettings settings = m_args.count(g_argOptimize) ? OptimiserSettings::standard() : OptimiserSettings::minimal(); + settings.expectedExecutionsPerDeployment = m_args[g_argOptimizeRuns].as(); + if (m_args.count(g_strNoOptimizeYul)) + settings.runYulOptimiser = false; + if (m_args.count(g_strYulOptimizations)) + { + if (!settings.runYulOptimiser) + { + serr() << "--" << g_strYulOptimizations << " is invalid if Yul optimizer is disabled" << endl; + return false; + } + + try + { + yul::OptimiserSuite::validateSequence(m_args[g_strYulOptimizations].as()); + } + catch (yul::OptimizerException const& _exception) + { + serr() << "Invalid optimizer step sequence in --" << g_strYulOptimizations << ": " << _exception.what() << endl; + return false; + } + + settings.yulOptimiserSteps = m_args[g_strYulOptimizations].as(); + } + settings.optimizeStackAllocation = settings.runYulOptimiser; + m_compiler->setOptimiserSettings(settings); + + if (m_args.count(g_argImportAst)) + { + try + { + m_compiler->importASTs(parseAstFromInput()); + + if (!m_compiler->analyze()) + { + for (auto const& error: m_compiler->errors()) + formatter.printErrorInformation(*error); + astAssert(false, "Analysis of the AST failed"); + } + } + catch (Exception const& _exc) + { + serr() << string("Failed to import AST: ") << _exc.what() << endl; + return false; + } + } + else + { + m_compiler->setSources(m_fileReader.sourceCodes()); + if (m_args.count(g_argErrorRecovery)) + m_compiler->setParserErrorRecovery(true); + } + + bool successful = m_compiler->compile(m_stopAfter); + + for (auto const& error: m_compiler->errors()) + { + g_hasOutput = true; + formatter.printErrorInformation(*error); + } + + if (!successful) + { + if (m_args.count(g_argErrorRecovery)) + return true; + else + return false; + } + } + catch (CompilerError const& _exception) + { + g_hasOutput = true; + formatter.printExceptionInformation(_exception, "Compiler error"); + return false; + } + catch (InternalCompilerError const& _exception) + { + serr() << + "Internal compiler error during compilation:" << + endl << + boost::diagnostic_information(_exception); + return false; + } + catch (UnimplementedFeatureError const& _exception) + { + serr() << + "Unimplemented feature:" << + endl << + boost::diagnostic_information(_exception); + return false; + } + catch (smtutil::SMTLogicError const& _exception) + { + serr() << + "SMT logic error during analysis:" << + endl << + boost::diagnostic_information(_exception); + return false; + } + catch (Error const& _error) + { + if (_error.type() == Error::Type::DocstringParsingError) + serr() << "Documentation parsing error: " << *boost::get_error_info(_error) << endl; + else + { + g_hasOutput = true; + formatter.printExceptionInformation(_error, _error.typeName()); + } + + return false; + } + catch (Exception const& _exception) + { + serr() << "Exception during compilation: " << boost::diagnostic_information(_exception) << endl; + return false; + } + catch (std::exception const& _e) + { + serr() << "Unknown exception during compilation" << ( + _e.what() ? ": " + string(_e.what()) : "." + ) << endl; + return false; + } + catch (...) + { + serr() << "Unknown exception during compilation." << endl; + return false; + } + + return true; +} + +void CommandLineInterface::handleCombinedJSON() +{ + if (!m_args.count(g_argCombinedJson)) + return; + + Json::Value output(Json::objectValue); + + output[g_strVersion] = frontend::VersionString; + set requests; + boost::split(requests, m_args[g_argCombinedJson].as(), boost::is_any_of(",")); + vector contracts = m_compiler->contractNames(); + + if (!contracts.empty()) + output[g_strContracts] = Json::Value(Json::objectValue); + for (string const& contractName: contracts) + { + Json::Value& contractData = output[g_strContracts][contractName] = Json::objectValue; + if (requests.count(g_strAbi)) + contractData[g_strAbi] = m_compiler->contractABI(contractName); + if (requests.count("metadata")) + contractData["metadata"] = m_compiler->metadata(contractName); + if (requests.count(g_strBinary) && m_compiler->compilationSuccessful()) + contractData[g_strBinary] = m_compiler->object(contractName).toHex(); + if (requests.count(g_strBinaryRuntime) && m_compiler->compilationSuccessful()) + contractData[g_strBinaryRuntime] = m_compiler->runtimeObject(contractName).toHex(); + if (requests.count(g_strOpcodes) && m_compiler->compilationSuccessful()) + contractData[g_strOpcodes] = evmasm::disassemble(m_compiler->object(contractName).bytecode); + if (requests.count(g_strAsm) && m_compiler->compilationSuccessful()) + contractData[g_strAsm] = m_compiler->assemblyJSON(contractName); + if (requests.count(g_strStorageLayout) && m_compiler->compilationSuccessful()) + contractData[g_strStorageLayout] = m_compiler->storageLayout(contractName); + if (requests.count(g_strGeneratedSources) && m_compiler->compilationSuccessful()) + contractData[g_strGeneratedSources] = m_compiler->generatedSources(contractName, false); + if (requests.count(g_strGeneratedSourcesRuntime) && m_compiler->compilationSuccessful()) + contractData[g_strGeneratedSourcesRuntime] = m_compiler->generatedSources(contractName, true); + if (requests.count(g_strSrcMap) && m_compiler->compilationSuccessful()) + { + auto map = m_compiler->sourceMapping(contractName); + contractData[g_strSrcMap] = map ? *map : ""; + } + if (requests.count(g_strSrcMapRuntime) && m_compiler->compilationSuccessful()) + { + auto map = m_compiler->runtimeSourceMapping(contractName); + contractData[g_strSrcMapRuntime] = map ? *map : ""; + } + if (requests.count(g_strFunDebug) && m_compiler->compilationSuccessful()) + contractData[g_strFunDebug] = StandardCompiler::formatFunctionDebugData( + m_compiler->object(contractName).functionDebugData + ); + if (requests.count(g_strFunDebugRuntime) && m_compiler->compilationSuccessful()) + contractData[g_strFunDebugRuntime] = StandardCompiler::formatFunctionDebugData( + m_compiler->runtimeObject(contractName).functionDebugData + ); + if (requests.count(g_strSignatureHashes)) + contractData[g_strSignatureHashes] = m_compiler->methodIdentifiers(contractName); + if (requests.count(g_strNatspecDev)) + contractData[g_strNatspecDev] = m_compiler->natspecDev(contractName); + if (requests.count(g_strNatspecUser)) + contractData[g_strNatspecUser] = m_compiler->natspecUser(contractName); + } + + bool needsSourceList = requests.count(g_strAst) || requests.count(g_strSrcMap) || requests.count(g_strSrcMapRuntime); + if (needsSourceList) + { + // Indices into this array are used to abbreviate source names in source locations. + output[g_strSourceList] = Json::Value(Json::arrayValue); + + for (auto const& source: m_compiler->sourceNames()) + output[g_strSourceList].append(source); + } + + if (requests.count(g_strAst)) + { + output[g_strSources] = Json::Value(Json::objectValue); + for (auto const& sourceCode: m_fileReader.sourceCodes()) + { + ASTJsonConverter converter(m_compiler->state(), m_compiler->sourceIndices()); + output[g_strSources][sourceCode.first] = Json::Value(Json::objectValue); + output[g_strSources][sourceCode.first]["AST"] = converter.toJson(m_compiler->ast(sourceCode.first)); + } + } + + string json = m_args.count(g_argPrettyJson) ? jsonPrettyPrint(removeNullMembers(std::move(output))) : + jsonCompactPrint(removeNullMembers(std::move(output))); + + if (m_args.count(g_argOutputDir)) + createJson("combined", json); + else + sout() << json << endl; +} + +void CommandLineInterface::handleAst() +{ + if (!m_args.count(g_argAstCompactJson)) + return; + + vector asts; + for (auto const& sourceCode: m_fileReader.sourceCodes()) + asts.push_back(&m_compiler->ast(sourceCode.first)); + + if (m_args.count(g_argOutputDir)) + { + for (auto const& sourceCode: m_fileReader.sourceCodes()) + { + stringstream data; + string postfix = ""; + ASTJsonConverter(m_compiler->state(), m_compiler->sourceIndices()).print(data, m_compiler->ast(sourceCode.first)); + postfix += "_json"; + boost::filesystem::path path(sourceCode.first); + createFile(path.filename().string() + postfix + ".ast", data.str()); + } + } + else + { + sout() << "JSON AST (compact format):" << endl << endl; + for (auto const& sourceCode: m_fileReader.sourceCodes()) + { + sout() << endl << "======= " << sourceCode.first << " =======" << endl; + ASTJsonConverter(m_compiler->state(), m_compiler->sourceIndices()).print(sout(), m_compiler->ast(sourceCode.first)); + } + } +} + +bool CommandLineInterface::actOnInput() +{ + if (m_args.count(g_argStandardJSON) || m_onlyAssemble) + // Already done in "processInput" phase. + return true; + else if (m_onlyLink) + writeLinkedFiles(); + else + outputCompilationResults(); + return !m_error; +} + +bool CommandLineInterface::link() +{ + // Map from how the libraries will be named inside the bytecode to their addresses. + map librariesReplacements; + int const placeholderSize = 40; // 20 bytes or 40 hex characters + for (auto const& library: m_libraries) + { + string const& name = library.first; + // Library placeholders are 40 hex digits (20 bytes) that start and end with '__'. + // This leaves 36 characters for the library identifier. The identifier used to + // be just the cropped or '_'-padded library name, but this changed to + // the cropped hex representation of the hash of the library name. + // We support both ways of linking here. + librariesReplacements["__" + evmasm::LinkerObject::libraryPlaceholder(name) + "__"] = library.second; + + string replacement = "__"; + for (size_t i = 0; i < placeholderSize - 4; ++i) + replacement.push_back(i < name.size() ? name[i] : '_'); + replacement += "__"; + librariesReplacements[replacement] = library.second; + } + + FileReader::StringMap sourceCodes = m_fileReader.sourceCodes(); + for (auto& src: sourceCodes) + { + auto end = src.second.end(); + for (auto it = src.second.begin(); it != end;) + { + while (it != end && *it != '_') ++it; + if (it == end) break; + if ( + end - it < placeholderSize || + *(it + 1) != '_' || + *(it + placeholderSize - 2) != '_' || + *(it + placeholderSize - 1) != '_' + ) + { + serr() << "Error in binary object file " << src.first << " at position " << (it - src.second.begin()) << endl; + serr() << '"' << string(it, it + min(placeholderSize, static_cast(end - it))) << "\" is not a valid link reference." << endl; + return false; + } + + string foundPlaceholder(it, it + placeholderSize); + if (librariesReplacements.count(foundPlaceholder)) + { + string hexStr(toHex(librariesReplacements.at(foundPlaceholder).asBytes())); + copy(hexStr.begin(), hexStr.end(), it); + } + else + serr() << "Reference \"" << foundPlaceholder << "\" in file \"" << src.first << "\" still unresolved." << endl; + it += placeholderSize; + } + // Remove hints for resolved libraries. + for (auto const& library: m_libraries) + boost::algorithm::erase_all(src.second, "\n" + libraryPlaceholderHint(library.first)); + while (!src.second.empty() && *prev(src.second.end()) == '\n') + src.second.resize(src.second.size() - 1); + } + m_fileReader.setSources(move(sourceCodes)); + + return true; +} + +void CommandLineInterface::writeLinkedFiles() +{ + for (auto const& src: m_fileReader.sourceCodes()) + if (src.first == g_stdinFileName) + sout() << src.second << endl; + else + { + ofstream outFile(src.first); + outFile << src.second; + if (!outFile) + { + serr() << "Could not write to file " << src.first << ". Aborting." << endl; + return; + } + } + sout() << "Linking completed." << endl; +} + +string CommandLineInterface::libraryPlaceholderHint(string const& _libraryName) +{ + return "// " + evmasm::LinkerObject::libraryPlaceholder(_libraryName) + " -> " + _libraryName; +} + +string CommandLineInterface::objectWithLinkRefsHex(evmasm::LinkerObject const& _obj) +{ + string out = _obj.toHex(); + if (!_obj.linkReferences.empty()) + { + out += "\n"; + for (auto const& linkRef: _obj.linkReferences) + out += "\n" + libraryPlaceholderHint(linkRef.second); + } + return out; +} + +bool CommandLineInterface::assemble( + yul::AssemblyStack::Language _language, + yul::AssemblyStack::Machine _targetMachine, + bool _optimize, + optional _yulOptimiserSteps +) +{ + solAssert(_optimize || !_yulOptimiserSteps.has_value(), ""); + + bool successful = true; + map assemblyStacks; + for (auto const& src: m_fileReader.sourceCodes()) + { + OptimiserSettings settings = _optimize ? OptimiserSettings::full() : OptimiserSettings::minimal(); + if (_yulOptimiserSteps.has_value()) + settings.yulOptimiserSteps = _yulOptimiserSteps.value(); + + auto& stack = assemblyStacks[src.first] = yul::AssemblyStack(m_evmVersion, _language, settings); + try + { + if (!stack.parseAndAnalyze(src.first, src.second)) + successful = false; + else + stack.optimize(); + } + catch (Exception const& _exception) + { + serr() << "Exception in assembler: " << boost::diagnostic_information(_exception) << endl; + return false; + } + catch (std::exception const& _e) + { + serr() << + "Unknown exception during compilation" << + (_e.what() ? ": " + string(_e.what()) : ".") << + endl; + return false; + } + catch (...) + { + serr() << "Unknown exception in assembler." << endl; + return false; + } + } + + for (auto const& sourceAndStack: assemblyStacks) + { + auto const& stack = sourceAndStack.second; + SourceReferenceFormatter formatter(serr(false), m_coloredOutput, m_withErrorIds); + + for (auto const& error: stack.errors()) + { + g_hasOutput = true; + formatter.printErrorInformation(*error); + } + if (!Error::containsOnlyWarnings(stack.errors())) + successful = false; + } + + if (!successful) + return false; + + for (auto const& src: m_fileReader.sourceCodes()) + { + string machine = + _targetMachine == yul::AssemblyStack::Machine::EVM ? "EVM" : + "Ewasm"; + sout() << endl << "======= " << src.first << " (" << machine << ") =======" << endl; + + yul::AssemblyStack& stack = assemblyStacks[src.first]; + + sout() << endl << "Pretty printed source:" << endl; + sout() << stack.print() << endl; + + if (_language != yul::AssemblyStack::Language::Ewasm && _targetMachine == yul::AssemblyStack::Machine::Ewasm) + { + try + { + stack.translate(yul::AssemblyStack::Language::Ewasm); + stack.optimize(); + } + catch (Exception const& _exception) + { + serr() << "Exception in assembler: " << boost::diagnostic_information(_exception) << endl; + return false; + } + catch (std::exception const& _e) + { + serr() << + "Unknown exception during compilation" << + (_e.what() ? ": " + string(_e.what()) : ".") << + endl; + return false; + } + catch (...) + { + serr() << "Unknown exception in assembler." << endl; + return false; + } + + sout() << endl << "==========================" << endl; + sout() << endl << "Translated source:" << endl; + sout() << stack.print() << endl; + } + + yul::MachineAssemblyObject object; + try + { + object = stack.assemble(_targetMachine); + object.bytecode->link(m_libraries); + } + catch (Exception const& _exception) + { + serr() << "Exception while assembling: " << boost::diagnostic_information(_exception) << endl; + return false; + } + catch (std::exception const& _e) + { + serr() << "Unknown exception during compilation" << ( + _e.what() ? ": " + string(_e.what()) : "." + ) << endl; + return false; + } + catch (...) + { + serr() << "Unknown exception while assembling." << endl; + return false; + } + + sout() << endl << "Binary representation:" << endl; + if (object.bytecode) + sout() << object.bytecode->toHex() << endl; + else + serr() << "No binary representation found." << endl; + + sout() << endl << "Text representation:" << endl; + if (!object.assembly.empty()) + sout() << object.assembly << endl; + else + serr() << "No text representation found." << endl; + } + + return true; +} + +void CommandLineInterface::outputCompilationResults() +{ + handleCombinedJSON(); + + // do we need AST output? + handleAst(); + + if ( + !m_compiler->compilationSuccessful() && + m_stopAfter == CompilerStack::State::CompilationSuccessful + ) + { + serr() << endl << "Compilation halted after AST generation due to errors." << endl; + return; + } + + vector contracts = m_compiler->contractNames(); + for (string const& contract: contracts) + { + if (needsHumanTargetedStdout(m_args)) + sout() << endl << "======= " << contract << " =======" << endl; + + // do we need EVM assembly? + if (m_args.count(g_argAsm) || m_args.count(g_argAsmJson)) + { + string ret; + if (m_args.count(g_argAsmJson)) + ret = jsonPrettyPrint(removeNullMembers(m_compiler->assemblyJSON(contract))); + else + ret = m_compiler->assemblyString(contract, m_fileReader.sourceCodes()); + + if (m_args.count(g_argOutputDir)) + { + createFile(m_compiler->filesystemFriendlyName(contract) + (m_args.count(g_argAsmJson) ? "_evm.json" : ".evm"), ret); + } + else + { + sout() << "EVM assembly:" << endl << ret << endl; + } + } + + if (m_args.count(g_argGas)) + handleGasEstimation(contract); + + handleBytecode(contract); + handleIR(contract); + handleIROptimized(contract); + handleEwasm(contract); + handleSignatureHashes(contract); + handleMetadata(contract); + handleABI(contract); + handleStorageLayout(contract); + handleNatspec(true, contract); + handleNatspec(false, contract); + } // end of contracts iteration + + if (!g_hasOutput) + { + if (m_args.count(g_argOutputDir)) + sout() << "Compiler run successful. Artifact(s) can be found in directory " << m_args.at(g_argOutputDir).as() << "." << endl; + else + serr() << "Compiler run successful, no output requested." << endl; + } +} + +size_t CommandLineInterface::countEnabledOptions(vector const& _optionNames) const +{ + size_t count = 0; + for (string const& _option: _optionNames) + count += m_args.count(_option); + + return count; +} + +string CommandLineInterface::joinOptionNames(vector const& _optionNames, string _separator) +{ + return boost::algorithm::join( + _optionNames | ranges::views::transform([](string const& _option){ return "--" + _option; }), + _separator + ); +} + +} diff --git a/solc/CommandLineParser.h b/solc/CommandLineParser.h new file mode 100644 index 000000000..73c220e37 --- /dev/null +++ b/solc/CommandLineParser.h @@ -0,0 +1,142 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +/** + * @author Lefteris + * @date 2014 + * Solidity command line interface. + */ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace solidity::frontend +{ + +//forward declaration +enum class DocumentationType: uint8_t; + +class CommandLineInterface +{ +public: + /// Parse command line arguments and return false if we should not continue + bool parseArguments(int _argc, char** _argv); + /// Parse the files and create source code objects + bool processInput(); + /// Perform actions on the input depending on provided compiler arguments + /// @returns true on success. + bool actOnInput(); + +private: + bool link(); + void writeLinkedFiles(); + /// @returns the ``// -> name`` hint for library placeholders. + static std::string libraryPlaceholderHint(std::string const& _libraryName); + /// @returns the full object with library placeholder hints in hex. + static std::string objectWithLinkRefsHex(evmasm::LinkerObject const& _obj); + + bool assemble( + yul::AssemblyStack::Language _language, + yul::AssemblyStack::Machine _targetMachine, + bool _optimize, + std::optional _yulOptimiserSteps = std::nullopt + ); + + void outputCompilationResults(); + + void handleCombinedJSON(); + void handleAst(); + void handleBinary(std::string const& _contract); + void handleOpcode(std::string const& _contract); + void handleIR(std::string const& _contract); + void handleIROptimized(std::string const& _contract); + void handleEwasm(std::string const& _contract); + void handleBytecode(std::string const& _contract); + void handleSignatureHashes(std::string const& _contract); + void handleMetadata(std::string const& _contract); + void handleABI(std::string const& _contract); + void handleNatspec(bool _natspecDev, std::string const& _contract); + void handleGasEstimation(std::string const& _contract); + void handleStorageLayout(std::string const& _contract); + + /// Fills @a m_sourceCodes initially and @a m_redirects. + bool readInputFilesAndConfigureRemappings(); + /// Tries to read from the file @a _input or interprets _input literally if that fails. + /// It then tries to parse the contents and appends to m_libraries. + bool parseLibraryOption(std::string const& _input); + + /// Tries to read @ m_sourceCodes as a JSONs holding ASTs + /// such that they can be imported into the compiler (importASTs()) + /// (produced by --combined-json ast,compact-format + /// or standard-json output + std::map parseAstFromInput(); + + /// Create a file in the given directory + /// @arg _fileName the name of the file + /// @arg _data to be written + void createFile(std::string const& _fileName, std::string const& _data); + + /// Create a json file in the given directory + /// @arg _fileName the name of the file (the extension will be replaced with .json) + /// @arg _json json string to be written + void createJson(std::string const& _fileName, std::string const& _json); + + size_t countEnabledOptions(std::vector const& _optionNames) const; + static std::string joinOptionNames(std::vector const& _optionNames, std::string _separator = ", "); + + bool m_error = false; ///< If true, some error occurred. + + bool m_onlyAssemble = false; + + bool m_onlyLink = false; + + FileReader m_fileReader; + + /// Compiler arguments variable map + boost::program_options::variables_map m_args; + /// list of remappings + std::vector m_remappings; + /// map of library names to addresses + std::map m_libraries; + /// Solidity compiler stack + std::unique_ptr m_compiler; + CompilerStack::State m_stopAfter = CompilerStack::State::CompilationSuccessful; + /// EVM version to use + langutil::EVMVersion m_evmVersion; + /// How to handle revert strings + RevertStrings m_revertStrings = RevertStrings::Default; + /// Chosen hash method for the bytecode metadata. + CompilerStack::MetadataHash m_metadataHash = CompilerStack::MetadataHash::IPFS; + /// Model checker settings. + ModelCheckerSettings m_modelCheckerSettings; + /// Whether or not to colorize diagnostics output. + bool m_coloredOutput = true; + /// Whether or not to output error IDs. + bool m_withErrorIds = false; +}; + +} From d781955b26e3a9770f26990612c30760623158e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 7 Jun 2021 12:33:04 +0200 Subject: [PATCH 06/98] Extract CommandLineParser out of CommandLineInterface --- solc/CMakeLists.txt | 1 + solc/CommandLineInterface.cpp | 1348 +++++---------------------------- solc/CommandLineInterface.h | 59 +- solc/CommandLineParser.cpp | 1272 ++++--------------------------- solc/CommandLineParser.h | 250 +++--- 5 files changed, 533 insertions(+), 2397 deletions(-) diff --git a/solc/CMakeLists.txt b/solc/CMakeLists.txt index 73f4381c6..fe68d4eb9 100644 --- a/solc/CMakeLists.txt +++ b/solc/CMakeLists.txt @@ -1,5 +1,6 @@ set(libsolcli_sources CommandLineInterface.cpp CommandLineInterface.h + CommandLineParser.cpp CommandLineParser.h ) add_library(solcli ${libsolcli_sources}) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 7c2cad4ef..cea5c7e1e 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -24,11 +24,8 @@ #include #include "solidity/BuildInfo.h" -#include "license.h" -#include #include -#include #include #include #include @@ -40,13 +37,11 @@ #include #include -#include #include #include #include -#include #include #include @@ -67,8 +62,6 @@ #include #include -#include - #ifdef _WIN32 // windows #include #define isatty _isatty @@ -77,7 +70,6 @@ #include #endif -#include #include #include @@ -85,24 +77,24 @@ #define STDERR_FILENO 2 #endif + using namespace std; using namespace solidity; using namespace solidity::util; using namespace solidity::langutil; -namespace po = boost::program_options; - namespace solidity::frontend { -bool g_hasOutput = false; - namespace { -std::ostream& sout() +static bool g_hasOutput = false; + +std::ostream& sout(bool _used = true) { - g_hasOutput = true; + if (_used) + g_hasOutput = true; return cout; } @@ -118,249 +110,60 @@ std::ostream& serr(bool _used = true) #define cout #define cerr -static string const g_stdinFileNameStr = ""; +static string const g_stdinFileName = ""; static string const g_strAbi = "abi"; -static string const g_strAllowPaths = "allow-paths"; -static string const g_strBasePath = "base-path"; static string const g_strAsm = "asm"; -static string const g_strAsmJson = "asm-json"; -static string const g_strAssemble = "assemble"; static string const g_strAst = "ast"; -static string const g_strAstCompactJson = "ast-compact-json"; static string const g_strBinary = "bin"; static string const g_strBinaryRuntime = "bin-runtime"; -static string const g_strCombinedJson = "combined-json"; -static string const g_strCompactJSON = "compact-format"; static string const g_strContracts = "contracts"; -static string const g_strErrorRecovery = "error-recovery"; -static string const g_strEVM = "evm"; -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"; -static string const g_strHelp = "help"; -static string const g_strImportAst = "import-ast"; -static string const g_strInputFile = "input-file"; -static string const g_strInterface = "interface"; -static string const g_strYul = "yul"; -static string const g_strYulDialect = "yul-dialect"; -static string const g_strIR = "ir"; -static string const g_strIROptimized = "ir-optimized"; -static string const g_strIPFS = "ipfs"; -static string const g_strLicense = "license"; -static string const g_strLibraries = "libraries"; -static string const g_strLink = "link"; -static string const g_strMachine = "machine"; -static string const g_strMetadata = "metadata"; -static string const g_strMetadataHash = "metadata-hash"; -static string const g_strMetadataLiteral = "metadata-literal"; -static string const g_strModelCheckerContracts = "model-checker-contracts"; -static string const g_strModelCheckerEngine = "model-checker-engine"; -static string const g_strModelCheckerTargets = "model-checker-targets"; -static string const g_strModelCheckerTimeout = "model-checker-timeout"; -static string const g_strNatspecDev = "devdoc"; -static string const g_strNatspecUser = "userdoc"; -static string const g_strNone = "none"; -static string const g_strNoOptimizeYul = "no-optimize-yul"; -static string const g_strOpcodes = "opcodes"; -static string const g_strOptimize = "optimize"; -static string const g_strOptimizeRuns = "optimize-runs"; -static string const g_strOptimizeYul = "optimize-yul"; -static string const g_strYulOptimizations = "yul-optimizations"; -static string const g_strOutputDir = "output-dir"; -static string const g_strOverwrite = "overwrite"; -static string const g_strRevertStrings = "revert-strings"; -static string const g_strStorageLayout = "storage-layout"; -static string const g_strStopAfter = "stop-after"; -static string const g_strParsing = "parsing"; - -/// Possible arguments to for --revert-strings -static set const g_revertStringsArgs -{ - revertStringsToString(RevertStrings::Default), - revertStringsToString(RevertStrings::Strip), - revertStringsToString(RevertStrings::Debug), - revertStringsToString(RevertStrings::VerboseDebug) -}; - -static string const g_strSignatureHashes = "hashes"; -static string const g_strSources = "sources"; -static string const g_strSourceList = "sourceList"; -static string const g_strSrcMap = "srcmap"; -static string const g_strSrcMapRuntime = "srcmap-runtime"; static string const g_strFunDebug = "function-debug"; static string const g_strFunDebugRuntime = "function-debug-runtime"; -static string const g_strStandardJSON = "standard-json"; -static string const g_strStrictAssembly = "strict-assembly"; -static string const g_strSwarm = "swarm"; -static string const g_strPrettyJson = "pretty-json"; +static string const g_strGeneratedSources = "generated-sources"; +static string const g_strGeneratedSourcesRuntime = "generated-sources-runtime"; +static string const g_strNatspecDev = "devdoc"; +static string const g_strNatspecUser = "userdoc"; +static string const g_strOpcodes = "opcodes"; +static string const g_strSignatureHashes = "hashes"; +static string const g_strSourceList = "sourceList"; +static string const g_strSources = "sources"; +static string const g_strSrcMap = "srcmap"; +static string const g_strSrcMapRuntime = "srcmap-runtime"; +static string const g_strStorageLayout = "storage-layout"; static string const g_strVersion = "version"; -static string const g_strIgnoreMissingFiles = "ignore-missing"; -static string const g_strColor = "color"; -static string const g_strNoColor = "no-color"; -static string const g_strErrorIds = "error-codes"; -static string const g_argAbi = g_strAbi; -static string const g_argPrettyJson = g_strPrettyJson; -static string const g_argAllowPaths = g_strAllowPaths; -static string const g_argBasePath = g_strBasePath; -static string const g_argAsm = g_strAsm; -static string const g_argAsmJson = g_strAsmJson; -static string const g_argAssemble = g_strAssemble; -static string const g_argAstCompactJson = g_strAstCompactJson; -static string const g_argBinary = g_strBinary; -static string const g_argBinaryRuntime = g_strBinaryRuntime; -static string const g_argCombinedJson = g_strCombinedJson; -static string const g_argCompactJSON = g_strCompactJSON; -static string const g_argErrorRecovery = g_strErrorRecovery; -static string const g_argGas = g_strGas; -static string const g_argHelp = g_strHelp; -static string const g_argImportAst = g_strImportAst; -static string const g_argInputFile = g_strInputFile; -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; -static string const g_argMetadata = g_strMetadata; -static string const g_argMetadataHash = g_strMetadataHash; -static string const g_argMetadataLiteral = g_strMetadataLiteral; -static string const g_argModelCheckerContracts = g_strModelCheckerContracts; -static string const g_argModelCheckerEngine = g_strModelCheckerEngine; -static string const g_argModelCheckerTargets = g_strModelCheckerTargets; -static string const g_argModelCheckerTimeout = g_strModelCheckerTimeout; -static string const g_argNatspecDev = g_strNatspecDev; -static string const g_argNatspecUser = g_strNatspecUser; -static string const g_argOpcodes = g_strOpcodes; -static string const g_argOptimize = g_strOptimize; -static string const g_argOptimizeRuns = g_strOptimizeRuns; -static string const g_argOutputDir = g_strOutputDir; -static string const g_argSignatureHashes = g_strSignatureHashes; -static string const g_argStandardJSON = g_strStandardJSON; -static string const g_argStorageLayout = g_strStorageLayout; -static string const g_argStrictAssembly = g_strStrictAssembly; -static string const g_argVersion = g_strVersion; -static string const g_stdinFileName = g_stdinFileNameStr; -static string const g_argIgnoreMissingFiles = g_strIgnoreMissingFiles; -static string const g_argColor = g_strColor; -static string const g_argNoColor = g_strNoColor; -static string const g_argErrorIds = g_strErrorIds; - -/// Possible arguments to for --combined-json -static set const g_combinedJsonArgs +static bool needsHumanTargetedStdout(CommandLineOptions const& _options) { - g_strAbi, - g_strAsm, - g_strAst, - g_strBinary, - g_strBinaryRuntime, - g_strCompactJSON, - g_strFunDebug, - g_strFunDebugRuntime, - g_strGeneratedSources, - g_strGeneratedSourcesRuntime, - g_strInterface, - g_strMetadata, - g_strNatspecUser, - g_strNatspecDev, - g_strOpcodes, - g_strSignatureHashes, - g_strSrcMap, - g_strSrcMapRuntime, - g_strStorageLayout -}; - -/// Possible arguments to for --machine -static set const g_machineArgs -{ - g_strEVM, - g_strEwasm -}; - -/// Possible arguments to for --yul-dialect -static set const g_yulDialectArgs -{ - g_strEVM, - g_strEwasm -}; - -/// Possible arguments to for --metadata-hash -static set const g_metadataHashArgs -{ - g_strIPFS, - g_strSwarm, - g_strNone -}; - -static void version() -{ - sout() << - "solc, the solidity compiler commandline interface" << - endl << - "Version: " << - solidity::frontend::VersionString << - endl; - exit(0); -} - -static void license() -{ - sout() << otherLicenses << endl; - // This is a static variable generated by cmake from LICENSE.txt - sout() << licenseText << endl; - exit(0); -} - -static bool needsHumanTargetedStdout(po::variables_map const& _args) -{ - if (_args.count(g_argGas)) + if (_options.compiler.estimateGas) return true; - if (_args.count(g_argOutputDir)) + if (!_options.output.dir.empty()) return false; - for (string const& arg: { - g_argAbi, - g_argAsm, - g_argAsmJson, - g_argBinary, - g_argBinaryRuntime, - g_argMetadata, - g_argNatspecUser, - g_argNatspecDev, - g_argOpcodes, - g_argSignatureHashes, - g_argStorageLayout - }) - if (_args.count(arg)) - return true; - return false; + return + _options.compiler.outputs.abi || + _options.compiler.outputs.asm_ || + _options.compiler.outputs.asmJson || + _options.compiler.outputs.binary || + _options.compiler.outputs.binaryRuntime || + _options.compiler.outputs.metadata || + _options.compiler.outputs.natspecUser || + _options.compiler.outputs.natspecDev || + _options.compiler.outputs.opcodes || + _options.compiler.outputs.signatureHashes || + _options.compiler.outputs.storageLayout; } -namespace +static bool coloredOutput(CommandLineOptions const& _options) { - -bool checkMutuallyExclusive(boost::program_options::variables_map const& args, std::string const& _optionA, std::string const& _optionB) -{ - if (args.count(_optionA) && args.count(_optionB)) - { - serr() << "Option " << _optionA << " and " << _optionB << " are mutually exclusive." << endl; - return false; - } - - return true; -} - + return + (!_options.formatting.coloredOutput.has_value() && isatty(STDERR_FILENO)) || + (_options.formatting.coloredOutput.has_value() && _options.formatting.coloredOutput.value()); } void CommandLineInterface::handleBinary(string const& _contract) { - if (m_args.count(g_argBinary)) + if (m_options.compiler.outputs.binary) { - if (m_args.count(g_argOutputDir)) + if (!m_options.output.dir.empty()) createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin", objectWithLinkRefsHex(m_compiler->object(_contract))); else { @@ -368,9 +171,9 @@ void CommandLineInterface::handleBinary(string const& _contract) sout() << objectWithLinkRefsHex(m_compiler->object(_contract)) << endl; } } - if (m_args.count(g_argBinaryRuntime)) + if (m_options.compiler.outputs.binaryRuntime) { - if (m_args.count(g_argOutputDir)) + if (!m_options.output.dir.empty()) createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin-runtime", objectWithLinkRefsHex(m_compiler->runtimeObject(_contract))); else { @@ -382,7 +185,7 @@ void CommandLineInterface::handleBinary(string const& _contract) void CommandLineInterface::handleOpcode(string const& _contract) { - if (m_args.count(g_argOutputDir)) + if (!m_options.output.dir.empty()) createFile(m_compiler->filesystemFriendlyName(_contract) + ".opcode", evmasm::disassemble(m_compiler->object(_contract).bytecode)); else { @@ -394,10 +197,10 @@ void CommandLineInterface::handleOpcode(string const& _contract) void CommandLineInterface::handleIR(string const& _contractName) { - if (!m_args.count(g_argIR)) + if (!m_options.compiler.outputs.ir) return; - if (m_args.count(g_argOutputDir)) + if (!m_options.output.dir.empty()) createFile(m_compiler->filesystemFriendlyName(_contractName) + ".yul", m_compiler->yulIR(_contractName)); else { @@ -408,10 +211,10 @@ void CommandLineInterface::handleIR(string const& _contractName) void CommandLineInterface::handleIROptimized(string const& _contractName) { - if (!m_args.count(g_argIROptimized)) + if (!m_options.compiler.outputs.irOptimized) return; - if (m_args.count(g_argOutputDir)) + if (!m_options.output.dir.empty()) createFile(m_compiler->filesystemFriendlyName(_contractName) + "_opt.yul", m_compiler->yulIROptimized(_contractName)); else { @@ -422,10 +225,10 @@ void CommandLineInterface::handleIROptimized(string const& _contractName) void CommandLineInterface::handleEwasm(string const& _contractName) { - if (!m_args.count(g_argEwasm)) + if (!m_options.compiler.outputs.ewasm) return; - if (m_args.count(g_argOutputDir)) + if (!m_options.output.dir.empty()) { createFile(m_compiler->filesystemFriendlyName(_contractName) + ".wast", m_compiler->ewasm(_contractName)); createFile( @@ -443,15 +246,15 @@ void CommandLineInterface::handleEwasm(string const& _contractName) void CommandLineInterface::handleBytecode(string const& _contract) { - if (m_args.count(g_argOpcodes)) + if (m_options.compiler.outputs.opcodes) handleOpcode(_contract); - if (m_args.count(g_argBinary) || m_args.count(g_argBinaryRuntime)) + if (m_options.compiler.outputs.binary || m_options.compiler.outputs.binaryRuntime) handleBinary(_contract); } void CommandLineInterface::handleSignatureHashes(string const& _contract) { - if (!m_args.count(g_argSignatureHashes)) + if (!m_options.compiler.outputs.signatureHashes) return; Json::Value methodIdentifiers = m_compiler->methodIdentifiers(_contract); @@ -459,7 +262,7 @@ void CommandLineInterface::handleSignatureHashes(string const& _contract) for (auto const& name: methodIdentifiers.getMemberNames()) out += methodIdentifiers[name].asString() + ": " + name + "\n"; - if (m_args.count(g_argOutputDir)) + if (!m_options.output.dir.empty()) createFile(m_compiler->filesystemFriendlyName(_contract) + ".signatures", out); else sout() << "Function signatures:" << endl << out; @@ -467,11 +270,11 @@ void CommandLineInterface::handleSignatureHashes(string const& _contract) void CommandLineInterface::handleMetadata(string const& _contract) { - if (!m_args.count(g_argMetadata)) + if (!m_options.compiler.outputs.metadata) return; string data = m_compiler->metadata(_contract); - if (m_args.count(g_argOutputDir)) + if (!m_options.output.dir.empty()) createFile(m_compiler->filesystemFriendlyName(_contract) + "_meta.json", data); else sout() << "Metadata:" << endl << data << endl; @@ -479,11 +282,11 @@ void CommandLineInterface::handleMetadata(string const& _contract) void CommandLineInterface::handleABI(string const& _contract) { - if (!m_args.count(g_argAbi)) + if (!m_options.compiler.outputs.abi) return; string data = jsonCompactPrint(removeNullMembers(m_compiler->contractABI(_contract))); - if (m_args.count(g_argOutputDir)) + if (!m_options.output.dir.empty()) createFile(m_compiler->filesystemFriendlyName(_contract) + ".abi", data); else sout() << "Contract JSON ABI" << endl << data << endl; @@ -491,11 +294,11 @@ void CommandLineInterface::handleABI(string const& _contract) void CommandLineInterface::handleStorageLayout(string const& _contract) { - if (!m_args.count(g_argStorageLayout)) + if (!m_options.compiler.outputs.storageLayout) return; string data = jsonCompactPrint(removeNullMembers(m_compiler->storageLayout(_contract))); - if (m_args.count(g_argOutputDir)) + if (!m_options.output.dir.empty()) createFile(m_compiler->filesystemFriendlyName(_contract) + "_storage.json", data); else sout() << "Contract Storage Layout:" << endl << data << endl; @@ -503,24 +306,24 @@ void CommandLineInterface::handleStorageLayout(string const& _contract) void CommandLineInterface::handleNatspec(bool _natspecDev, string const& _contract) { - std::string argName; + bool enabled = false; std::string suffix; std::string title; if (_natspecDev) { - argName = g_argNatspecDev; + enabled = m_options.compiler.outputs.natspecDev; suffix = ".docdev"; title = "Developer Documentation"; } else { - argName = g_argNatspecUser; + enabled = m_options.compiler.outputs.natspecUser; suffix = ".docuser"; title = "User Documentation"; } - if (m_args.count(argName)) + if (enabled) { std::string output = jsonPrettyPrint( removeNullMembers( @@ -530,7 +333,7 @@ void CommandLineInterface::handleNatspec(bool _natspecDev, string const& _contra ) ); - if (m_args.count(g_argOutputDir)) + if (!m_options.output.dir.empty()) createFile(m_compiler->filesystemFriendlyName(_contract) + suffix, output); else { @@ -581,65 +384,45 @@ void CommandLineInterface::handleGasEstimation(string const& _contract) } } -bool CommandLineInterface::readInputFilesAndConfigureRemappings() +bool CommandLineInterface::readInputFilesAndConfigureFileReader() { - bool ignoreMissing = m_args.count(g_argIgnoreMissingFiles); - bool addStdin = false; - if (m_args.count(g_argInputFile)) - for (string path: m_args[g_argInputFile].as>()) + for (boost::filesystem::path const& allowedDirectory: m_options.input.allowedDirectories) + m_fileReader.allowDirectory(allowedDirectory); + + for (boost::filesystem::path const& infile: m_options.input.paths) + { + if (!boost::filesystem::exists(infile)) { - auto eq = find(path.begin(), path.end(), '='); - if (eq != path.end()) + if (!m_options.input.ignoreMissingFiles) { - if (auto r = ImportRemapper::parseRemapping(path)) - m_remappings.emplace_back(std::move(*r)); - else - { - serr() << "Invalid remapping: \"" << path << "\"." << endl; - return false; - } - - string remappingTarget(eq + 1, path.end()); - m_fileReader.allowDirectory(boost::filesystem::path(remappingTarget).remove_filename()); + serr() << infile << " is not found." << endl; + return false; } - else if (path == "-") - addStdin = true; else - { - auto infile = boost::filesystem::path(path); - if (!boost::filesystem::exists(infile)) - { - if (!ignoreMissing) - { - serr() << infile << " is not found." << endl; - return false; - } - else - serr() << infile << " is not found. Skipping." << endl; + serr() << infile << " is not found. Skipping." << endl; - continue; - } - - if (!boost::filesystem::is_regular_file(infile)) - { - if (!ignoreMissing) - { - serr() << infile << " is not a valid file." << endl; - return false; - } - else - serr() << infile << " is not a valid file. Skipping." << endl; - - continue; - } - - // NOTE: we ignore the FileNotFound exception as we manually check above - m_fileReader.setSource(infile, readFileAsString(infile.string())); - m_fileReader.allowDirectory(boost::filesystem::path(boost::filesystem::canonical(infile).string()).remove_filename()); - } + continue; } - if (addStdin) + if (!boost::filesystem::is_regular_file(infile)) + { + if (!m_options.input.ignoreMissingFiles) + { + serr() << infile << " is not a valid file." << endl; + return false; + } + else + serr() << infile << " is not a valid file. Skipping." << endl; + + continue; + } + + // NOTE: we ignore the FileNotFound exception as we manually check above + m_fileReader.setSource(infile, readFileAsString(infile.string())); + m_fileReader.allowDirectory(boost::filesystem::path(boost::filesystem::canonical(infile).string()).remove_filename()); + } + + if (m_options.input.addStdin) m_fileReader.setSource(g_stdinFileName, readUntilEnd(cin)); if (m_fileReader.sourceCodes().size() == 0) @@ -651,105 +434,6 @@ bool CommandLineInterface::readInputFilesAndConfigureRemappings() return true; } -bool CommandLineInterface::parseLibraryOption(string const& _input) -{ - namespace fs = boost::filesystem; - string data = _input; - try - { - if (fs::is_regular_file(_input)) - data = readFileAsString(_input); - } - catch (fs::filesystem_error const&) - { - // Thrown e.g. if path is too long. - } - catch (FileNotFound const&) - { - // Should not happen if `fs::is_regular_file` is correct. - } - catch (NotAFile const&) - { - // Should not happen if `fs::is_regular_file` is correct. - } - - vector libraries; - boost::split(libraries, data, boost::is_space() || boost::is_any_of(","), boost::token_compress_on); - for (string const& lib: libraries) - if (!lib.empty()) - { - //search for equal sign or last colon in string as our binaries output placeholders in the form of file=Name or file:Name - //so we need to search for `=` or `:` in the string - auto separator = lib.rfind('='); - bool isSeparatorEqualSign = true; - if (separator == string::npos) - { - separator = lib.rfind(':'); - if (separator == string::npos) - { - serr() << "Equal sign separator missing in library address specifier \"" << lib << "\"" << endl; - return false; - } - else - isSeparatorEqualSign = false; // separator is colon - } - else - if (lib.rfind('=') != lib.find('=')) - { - serr() << "Only one equal sign \"=\" is allowed in the address string \"" << lib << "\"." << endl; - return false; - } - - string libName(lib.begin(), lib.begin() + static_cast(separator)); - 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(separator) + 1, lib.end()); - boost::trim(addrString); - if (addrString.empty()) - { - serr() << "Empty address provided for library \"" << libName << "\"." << endl; - serr() << "Note that there should not be any whitespace after the " << (isSeparatorEqualSign ? "equal sign" : "colon") << "." << endl; - return false; - } - - if (addrString.substr(0, 2) == "0x") - addrString = addrString.substr(2); - else - { - serr() << "The address " << addrString << " is not prefixed with \"0x\"." << endl; - serr() << "Note that the address must be prefixed with \"0x\"." << endl; - return false; - } - - if (addrString.length() != 40) - { - serr() << "Invalid length for address for library \"" << libName << "\": " << addrString.length() << " instead of 40 characters." << endl; - return false; - } - if (!passesAddressChecksum(addrString, false)) - { - serr() << "Invalid checksum on address for library \"" << libName << "\": " << addrString << endl; - serr() << "The correct checksum is " << getChecksummedAddress(addrString) << endl; - return false; - } - bytes binAddr = fromHex(addrString); - h160 address(binAddr, h160::AlignRight); - if (binAddr.size() > 20 || address == h160()) - { - serr() << "Invalid address for library \"" << libName << "\": " << addrString << endl; - return false; - } - m_libraries[libName] = address; - } - - return true; -} - map CommandLineInterface::parseAstFromInput() { map sourceJsons; @@ -782,17 +466,17 @@ void CommandLineInterface::createFile(string const& _fileName, string const& _da { namespace fs = boost::filesystem; - fs::path outputDir(m_args.at(g_argOutputDir).as()); + solAssert(!m_options.output.dir.empty(), ""); // 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)); + fs::create_directories(fs::absolute(m_options.output.dir)); - string pathName = (outputDir / _fileName).string(); - if (fs::exists(pathName) && !m_args.count(g_strOverwrite)) + string pathName = (m_options.output.dir / _fileName).string(); + if (fs::exists(pathName) && !m_options.output.overwriteFiles) { - serr() << "Refusing to overwrite existing file \"" << pathName << "\" (use --" << g_strOverwrite << " to force)." << endl; + serr() << "Refusing to overwrite existing file \"" << pathName << "\" (use --overwrite to force)." << endl; m_error = true; return; } @@ -811,467 +495,47 @@ void CommandLineInterface::createJson(string const& _fileName, string const& _js createFile(boost::filesystem::basename(_fileName) + string(".json"), _json); } -bool CommandLineInterface::parseArguments(int _argc, char** _argv) +bool CommandLineInterface::parseArguments(int _argc, char const* const* _argv) { - g_hasOutput = false; - - // Declare the supported options. - po::options_description desc((R"(solc, the Solidity commandline compiler. - -This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you -are welcome to redistribute it under certain conditions. See 'solc --)" + g_strLicense + R"(' -for details. - -Usage: solc [options] [input_file...] -Compiles the given Solidity input files (or the standard input if none given or -"-" is used as a file name) and outputs the components specified in the options -at standard output or in files in the output directory, if specified. -Imports are automatically read from the filesystem, but it is also possible to -remap paths using the context:prefix=path syntax. -Example: -solc --)" + g_argBinary + R"( -o /tmp/solcoutput dapp-bin=/usr/local/lib/dapp-bin contract.sol - -General Information)").c_str(), - po::options_description::m_default_line_length, - po::options_description::m_default_line_length - 23 - ); - desc.add_options() - (g_argHelp.c_str(), "Show help message and exit.") - (g_argVersion.c_str(), "Show version and exit.") - (g_strLicense.c_str(), "Show licensing information and exit.") - ; - - po::options_description inputOptions("Input Options"); - inputOptions.add_options() - ( - g_argBasePath.c_str(), - po::value()->value_name("path"), - "Use the given path as the root of the source tree instead of the root of the filesystem." - ) - ( - g_argAllowPaths.c_str(), - po::value()->value_name("path(s)"), - "Allow a given path for imports. A list of paths can be supplied by separating them with a comma." - ) - ( - g_argIgnoreMissingFiles.c_str(), - "Ignore missing files." - ) - ( - g_argErrorRecovery.c_str(), - "Enables additional parser error recovery." - ) - ; - desc.add(inputOptions); - - po::options_description outputOptions("Output Options"); - outputOptions.add_options() - ( - (g_argOutputDir + ",o").c_str(), - po::value()->value_name("path"), - "If given, creates one file per component and contract/file at the specified directory." - ) - ( - g_strOverwrite.c_str(), - "Overwrite existing files (used together with -o)." - ) - ( - g_strEVMVersion.c_str(), - po::value()->value_name("version")->default_value(EVMVersion{}.name()), - "Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon, " - "byzantium, constantinople, petersburg, istanbul 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, ",")), - "Strip revert (and require) reason strings or add additional debugging information." - ) - ( - g_strStopAfter.c_str(), - po::value()->value_name("stage"), - "Stop execution after the given compiler stage. Valid options: \"parsing\"." - ) - ; - desc.add(outputOptions); - - po::options_description alternativeInputModes("Alternative Input Modes"); - alternativeInputModes.add_options() - ( - g_argStandardJSON.c_str(), - "Switch to Standard JSON input / output mode, ignoring all options. " - "It reads from standard input, if no input file was given, otherwise it reads from the provided input file. The result will be written to standard output." - ) - ( - g_argLink.c_str(), - ("Switch to linker mode, ignoring all options apart from --" + g_argLibraries + " " - "and modify binaries in place.").c_str() - ) - ( - g_argAssemble.c_str(), - ("Switch to assembly mode, ignoring all options except " - "--" + g_argMachine + ", --" + g_strYulDialect + ", --" + g_argOptimize + " and --" + g_strYulOptimizations + " " - "and assumes input is assembly.").c_str() - ) - ( - g_argYul.c_str(), - ("Switch to Yul mode, ignoring all options except " - "--" + g_argMachine + ", --" + g_strYulDialect + ", --" + g_argOptimize + " and --" + g_strYulOptimizations + " " - "and assumes input is Yul.").c_str() - ) - ( - g_argStrictAssembly.c_str(), - ("Switch to strict assembly mode, ignoring all options except " - "--" + g_argMachine + ", --" + g_strYulDialect + ", --" + g_argOptimize + " and --" + g_strYulOptimizations + " " - "and assumes input is strict assembly.").c_str() - ) - ( - g_argImportAst.c_str(), - ("Import ASTs to be compiled, assumes input holds the AST in compact JSON format. " - "Supported Inputs is the output of the --" + g_argStandardJSON + " or the one produced by " - "--" + g_argCombinedJson + " " + g_strAst + "," + g_strCompactJSON).c_str() - ) - ; - desc.add(alternativeInputModes); - - po::options_description assemblyModeOptions("Assembly Mode Options"); - assemblyModeOptions.add_options() - ( - g_argMachine.c_str(), - po::value()->value_name(boost::join(g_machineArgs, ",")), - "Target machine in assembly or Yul mode." - ) - ( - g_strYulDialect.c_str(), - po::value()->value_name(boost::join(g_yulDialectArgs, ",")), - "Input dialect to use in assembly or yul mode." - ) - ; - desc.add(assemblyModeOptions); - - po::options_description linkerModeOptions("Linker Mode Options"); - linkerModeOptions.add_options() - ( - g_argLibraries.c_str(), - po::value>()->value_name("libs"), - "Direct string or file containing library addresses. Syntax: " - "=
[, or whitespace] ...\n" - "Address is interpreted as a hex string prefixed by 0x." - ) - ; - desc.add(linkerModeOptions); - - po::options_description outputFormatting("Output Formatting"); - outputFormatting.add_options() - ( - g_argPrettyJson.c_str(), - "Output JSON in pretty format. Currently it only works with the combined JSON output." - ) - ( - g_argColor.c_str(), - "Force colored output." - ) - ( - g_argNoColor.c_str(), - "Explicitly disable colored output, disabling terminal auto-detection." - ) - ( - g_argErrorIds.c_str(), - "Output error codes." - ) - ; - desc.add(outputFormatting); - - po::options_description outputComponents("Output Components"); - outputComponents.add_options() - (g_argAstCompactJson.c_str(), "AST of all source files in a compact JSON format.") - (g_argAsm.c_str(), "EVM assembly of the contracts.") - (g_argAsmJson.c_str(), "EVM assembly of the contracts in JSON format.") - (g_argOpcodes.c_str(), "Opcodes of the contracts.") - (g_argBinary.c_str(), "Binary of the contracts in hex.") - (g_argBinaryRuntime.c_str(), "Binary of the runtime part of the contracts in hex.") - (g_argAbi.c_str(), "ABI specification of the contracts.") - (g_argIR.c_str(), "Intermediate Representation (IR) of all contracts (EXPERIMENTAL).") - (g_argIROptimized.c_str(), "Optimized intermediate Representation (IR) of all contracts (EXPERIMENTAL).") - (g_argEwasm.c_str(), "Ewasm text representation of all contracts (EXPERIMENTAL).") - (g_argSignatureHashes.c_str(), "Function signature hashes of the contracts.") - (g_argNatspecUser.c_str(), "Natspec user documentation of all contracts.") - (g_argNatspecDev.c_str(), "Natspec developer documentation of all contracts.") - (g_argMetadata.c_str(), "Combined Metadata JSON whose Swarm hash is stored on-chain.") - (g_argStorageLayout.c_str(), "Slots, offsets and types of the contract's state variables.") - ; - desc.add(outputComponents); - - po::options_description extraOutput("Extra Output"); - extraOutput.add_options() - ( - g_argGas.c_str(), - "Print an estimate of the maximal gas usage for each function." - ) - ( - g_argCombinedJson.c_str(), - po::value()->value_name(boost::join(g_combinedJsonArgs, ",")), - "Output a single json document containing the specified information." - ) - ; - desc.add(extraOutput); - - po::options_description metadataOptions("Metadata Options"); - metadataOptions.add_options() - ( - g_argMetadataHash.c_str(), - po::value()->value_name(boost::join(g_metadataHashArgs, ",")), - "Choose hash method for the bytecode metadata or disable it." - ) - ( - g_argMetadataLiteral.c_str(), - "Store referenced sources as literal data in the metadata output." - ) - ; - desc.add(metadataOptions); - - po::options_description optimizerOptions("Optimizer Options"); - optimizerOptions.add_options() - ( - g_argOptimize.c_str(), - "Enable bytecode optimizer." - ) - ( - g_argOptimizeRuns.c_str(), - // TODO: The type in OptimiserSettings is size_t but we only accept values up to 2**32-1 - // on the CLI and in Standard JSON. We should just switch to uint32_t everywhere. - po::value()->value_name("n")->default_value(static_cast(OptimiserSettings{}.expectedExecutionsPerDeployment)), - "Set for how many contract runs to optimize. " - "Lower values will optimize more for initial deployment cost, higher values will optimize more for high-frequency usage." - ) - ( - g_strOptimizeYul.c_str(), - ("Legacy option, ignored. Use the general --" + g_argOptimize + " to enable Yul optimizer.").c_str() - ) - ( - g_strNoOptimizeYul.c_str(), - "Disable Yul optimizer in Solidity." - ) - ( - g_strYulOptimizations.c_str(), - po::value()->value_name("steps"), - "Forces yul optimizer to use the specified sequence of optimization steps instead of the built-in one." - ) - ; - desc.add(optimizerOptions); - - po::options_description smtCheckerOptions("Model Checker Options"); - smtCheckerOptions.add_options() - ( - g_strModelCheckerContracts.c_str(), - po::value()->value_name("default,:")->default_value("default"), - "Select which contracts should be analyzed using the form :." - "Multiple pairs : can be selected at the same time, separated by a comma " - "and no spaces." - ) - ( - g_strModelCheckerEngine.c_str(), - po::value()->value_name("all,bmc,chc,none")->default_value("none"), - "Select model checker engine." - ) - ( - g_strModelCheckerTargets.c_str(), - po::value()->value_name("default,constantCondition,underflow,overflow,divByZero,balance,assert,popEmptyArray,outOfBounds")->default_value("default"), - "Select model checker verification targets. " - "Multiple targets can be selected at the same time, separated by a comma " - "and no spaces." - ) - ( - g_strModelCheckerTimeout.c_str(), - po::value()->value_name("ms"), - "Set model checker timeout per query in milliseconds. " - "The default is a deterministic resource limit. " - "A timeout of 0 means no resource/time restrictions for any query." - ) - ; - desc.add(smtCheckerOptions); - - po::options_description allOptions = desc; - allOptions.add_options()(g_argInputFile.c_str(), po::value>(), "input file"); - - // All positional options should be interpreted as input files - po::positional_options_description filesPositions; - filesPositions.add(g_argInputFile.c_str(), -1); - - // parse the compiler arguments - try - { - po::command_line_parser cmdLineParser(_argc, _argv); - cmdLineParser.style(po::command_line_style::default_style & (~po::command_line_style::allow_guessing)); - cmdLineParser.options(allOptions).positional(filesPositions); - po::store(cmdLineParser.run(), m_args); - } - catch (po::error const& _exception) - { - serr() << _exception.what() << endl; + CommandLineParser parser; + bool success = parser.parse(_argc, _argv, isatty(fileno(stdin))); + if (!success) return false; - } - - if (!checkMutuallyExclusive(m_args, g_argColor, g_argNoColor)) - return false; - - static vector const conflictingWithStopAfter{ - g_argBinary, - g_argIR, - g_argIROptimized, - g_argEwasm, - g_argGas, - g_argAsm, - g_argAsmJson, - g_argOpcodes - }; - - for (auto& option: conflictingWithStopAfter) - if (!checkMutuallyExclusive(m_args, g_strStopAfter, option)) - return false; - - m_coloredOutput = !m_args.count(g_argNoColor) && (isatty(STDERR_FILENO) || m_args.count(g_argColor)); - - m_withErrorIds = m_args.count(g_argErrorIds); - - if (m_args.count(g_argHelp) || (isatty(fileno(stdin)) && _argc == 1)) - { - sout() << desc; - return false; - } - - if (m_args.count(g_argVersion)) - { - version(); - return false; - } - - if (m_args.count(g_strLicense)) - { - license(); - return false; - } - - if (m_args.count(g_strRevertStrings)) - { - string revertStringsString = m_args[g_strRevertStrings].as(); - std::optional revertStrings = revertStringsFromString(revertStringsString); - if (!revertStrings) - { - serr() << "Invalid option for --" << g_strRevertStrings << ": " << revertStringsString << endl; - return false; - } - if (*revertStrings == RevertStrings::VerboseDebug) - { - serr() << "Only \"default\", \"strip\" and \"debug\" are implemented for --" << g_strRevertStrings << " for now." << endl; - return false; - } - m_revertStrings = *revertStrings; - } - - if (m_args.count(g_argCombinedJson)) - { - vector requests; - for (string const& item: boost::split(requests, m_args[g_argCombinedJson].as(), boost::is_any_of(","))) - if (!g_combinedJsonArgs.count(item)) - { - serr() << "Invalid option to --" << g_argCombinedJson << ": " << item << endl; - return false; - } - } - po::notify(m_args); + g_hasOutput = g_hasOutput || CommandLineParser::hasOutput(); + m_options = parser.options(); return true; } bool CommandLineInterface::processInput() { - if (m_args.count(g_argBasePath)) - { - boost::filesystem::path const fspath{m_args[g_argBasePath].as()}; - if (!boost::filesystem::is_directory(fspath)) - { - serr() << "Base path must be a directory: \"" << fspath << "\"\n"; - return false; - } - m_fileReader.setBasePath(fspath); - } + m_fileReader.setBasePath(m_options.input.basePath); - if (m_args.count(g_argAllowPaths)) + if (m_options.input.basePath != "" && !boost::filesystem::is_directory(m_options.input.basePath)) { - vector paths; - for (string const& path: boost::split(paths, m_args[g_argAllowPaths].as(), boost::is_any_of(","))) - { - auto filesystem_path = boost::filesystem::path(path); - // If the given path had a trailing slash, the Boost filesystem - // path will have it's last component set to '.'. This breaks - // path comparison in later parts of the code, so we need to strip - // it. - if (filesystem_path.filename() == ".") - filesystem_path.remove_filename(); - m_fileReader.allowDirectory(filesystem_path); - } - } - - if (m_args.count(g_strStopAfter)) - { - if (m_args[g_strStopAfter].as() != "parsing") - { - serr() << "Valid options for --" << g_strStopAfter << " are: \"parsing\".\n"; - return false; - } - else - m_stopAfter = CompilerStack::State::Parsed; - } - - vector const exclusiveModes = { - g_argStandardJSON, - g_argLink, - g_argAssemble, - g_argStrictAssembly, - g_argYul, - g_argImportAst, - }; - if (countEnabledOptions(exclusiveModes) > 1) - { - serr() << "The following options are mutually exclusive: " << joinOptionNames(exclusiveModes) << ". "; - serr() << "Select at most one." << endl; + serr() << "Base path must be a directory: \"" << m_options.input.basePath << "\"\n"; return false; } - if (m_args.count(g_argStandardJSON)) + if (m_options.input.mode == InputMode::StandardJson) { - vector inputFiles; - string jsonFile; - if (m_args.count(g_argInputFile)) - inputFiles = m_args[g_argInputFile].as>(); - if (inputFiles.size() == 1) - jsonFile = inputFiles[0]; - else if (inputFiles.size() > 1) - { - serr() << "If --" << g_argStandardJSON << " is used, only zero or one input files are supported." << endl; - return false; - } string input; - if (jsonFile.empty()) + if (m_options.input.standardJsonFile.empty()) input = readUntilEnd(cin); else { try { - input = readFileAsString(jsonFile); + input = readFileAsString(m_options.input.standardJsonFile); } catch (FileNotFound const&) { - serr() << "File not found: " << jsonFile << endl; + serr() << "File not found: " << m_options.input.standardJsonFile << endl; return false; } catch (NotAFile const&) { - serr() << "Not a regular file: " << jsonFile << endl; + serr() << "Not a regular file: " << m_options.input.standardJsonFile << endl; return false; } } @@ -1280,265 +544,60 @@ bool CommandLineInterface::processInput() return true; } - if (!readInputFilesAndConfigureRemappings()) + if (!readInputFilesAndConfigureFileReader()) return false; - if (m_args.count(g_argLibraries)) - for (string const& library: m_args[g_argLibraries].as>()) - if (!parseLibraryOption(library)) - return false; + if (m_options.input.mode == InputMode::Assembler) + return assemble( + m_options.assembly.inputLanguage, + m_options.assembly.targetMachine, + m_options.optimizer.enabled, + m_options.optimizer.yulSteps + ); - if (m_args.count(g_strEVMVersion)) - { - string versionOptionStr = m_args[g_strEVMVersion].as(); - std::optional versionOption = langutil::EVMVersion::fromString(versionOptionStr); - if (!versionOption) - { - serr() << "Invalid option for --" << g_strEVMVersion << ": " << versionOptionStr << endl; - return false; - } - m_evmVersion = *versionOption; - } - - if (m_args.count(g_argAssemble) || m_args.count(g_argStrictAssembly) || m_args.count(g_argYul)) - { - vector const nonAssemblyModeOptions = { - // TODO: The list is not complete. Add more. - g_argOutputDir, - g_argGas, - g_argCombinedJson, - g_strOptimizeYul, - g_strNoOptimizeYul, - }; - if (countEnabledOptions(nonAssemblyModeOptions) >= 1) - { - auto optionEnabled = [&](string const& name){ return m_args.count(name) > 0; }; - auto enabledOptions = boost::copy_range>(nonAssemblyModeOptions | boost::adaptors::filtered(optionEnabled)); - - serr() << "The following options are invalid in assembly mode: "; - serr() << joinOptionNames(enabledOptions) << "."; - if (m_args.count(g_strOptimizeYul) || m_args.count(g_strNoOptimizeYul)) - serr() << " Optimization is disabled by default and can be enabled with --" << g_argOptimize << "." << endl; - serr() << endl; - return false; - } - - // switch to assembly mode - m_onlyAssemble = true; - using Input = yul::AssemblyStack::Language; - using Machine = yul::AssemblyStack::Machine; - Input inputLanguage = m_args.count(g_argYul) ? Input::Yul : (m_args.count(g_argStrictAssembly) ? Input::StrictAssembly : Input::Assembly); - Machine targetMachine = Machine::EVM; - bool optimize = m_args.count(g_argOptimize); - - optional yulOptimiserSteps; - if (m_args.count(g_strYulOptimizations)) - { - if (!optimize) - { - serr() << "--" << g_strYulOptimizations << " is invalid if Yul optimizer is disabled" << endl; - return false; - } - - try - { - yul::OptimiserSuite::validateSequence(m_args[g_strYulOptimizations].as()); - } - catch (yul::OptimizerException const& _exception) - { - serr() << "Invalid optimizer step sequence in --" << g_strYulOptimizations << ": " << _exception.what() << endl; - return false; - } - - yulOptimiserSteps = m_args[g_strYulOptimizations].as(); - } - - if (m_args.count(g_argMachine)) - { - string machine = m_args[g_argMachine].as(); - if (machine == g_strEVM) - targetMachine = Machine::EVM; - else if (machine == g_strEwasm) - targetMachine = Machine::Ewasm; - else - { - serr() << "Invalid option for --" << g_argMachine << ": " << machine << endl; - return false; - } - } - if (targetMachine == Machine::Ewasm && inputLanguage == Input::StrictAssembly) - inputLanguage = Input::Ewasm; - if (m_args.count(g_strYulDialect)) - { - string dialect = m_args[g_strYulDialect].as(); - if (dialect == g_strEVM) - inputLanguage = Input::StrictAssembly; - else if (dialect == g_strEwasm) - { - inputLanguage = Input::Ewasm; - if (targetMachine != Machine::Ewasm) - { - serr() << "If you select Ewasm as --" << g_strYulDialect << ", "; - serr() << "--" << g_argMachine << " has to be Ewasm as well." << endl; - return false; - } - } - else - { - serr() << "Invalid option for --" << g_strYulDialect << ": " << dialect << endl; - return false; - } - } - if (optimize && (inputLanguage != Input::StrictAssembly && inputLanguage != Input::Ewasm)) - { - serr() << - "Optimizer can only be used for strict assembly. Use --" << - g_strStrictAssembly << - "." << - endl; - return false; - } - if (targetMachine == Machine::Ewasm && inputLanguage != Input::StrictAssembly && inputLanguage != Input::Ewasm) - { - serr() << "The selected input language is not directly supported when targeting the Ewasm machine "; - serr() << "and automatic translation is not available." << endl; - return false; - } - serr() << - "Warning: Yul is still experimental. Please use the output with care." << - endl; - - return assemble(inputLanguage, targetMachine, optimize, yulOptimiserSteps); - } - else if (countEnabledOptions({g_strYulDialect, g_argMachine}) >= 1) - { - serr() << "--" << g_strYulDialect << " and --" << g_argMachine << " "; - serr() << "are only valid in assembly mode." << endl; - return false; - } - - if (m_args.count(g_argLink)) - { - // switch to linker mode - m_onlyLink = true; + if (m_options.input.mode == InputMode::Linker) return link(); - } - if (m_args.count(g_argMetadataHash)) - { - string hashStr = m_args[g_argMetadataHash].as(); - if (hashStr == g_strIPFS) - m_metadataHash = CompilerStack::MetadataHash::IPFS; - else if (hashStr == g_strSwarm) - m_metadataHash = CompilerStack::MetadataHash::Bzzr1; - else if (hashStr == g_strNone) - m_metadataHash = CompilerStack::MetadataHash::None; - else - { - serr() << "Invalid option for --" << g_argMetadataHash << ": " << hashStr << endl; - return false; - } - } + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + return compile(); +} - if (m_args.count(g_argModelCheckerContracts)) - { - string contractsStr = m_args[g_argModelCheckerContracts].as(); - optional contracts = ModelCheckerContracts::fromString(contractsStr); - if (!contracts) - { - serr() << "Invalid option for --" << g_argModelCheckerContracts << ": " << contractsStr << endl; - return false; - } - m_modelCheckerSettings.contracts = move(*contracts); - } - - - if (m_args.count(g_argModelCheckerEngine)) - { - string engineStr = m_args[g_argModelCheckerEngine].as(); - optional engine = ModelCheckerEngine::fromString(engineStr); - if (!engine) - { - serr() << "Invalid option for --" << g_argModelCheckerEngine << ": " << engineStr << endl; - return false; - } - m_modelCheckerSettings.engine = *engine; - } - - if (m_args.count(g_argModelCheckerTargets)) - { - string targetsStr = m_args[g_argModelCheckerTargets].as(); - optional targets = ModelCheckerTargets::fromString(targetsStr); - if (!targets) - { - serr() << "Invalid option for --" << g_argModelCheckerTargets << ": " << targetsStr << endl; - return false; - } - m_modelCheckerSettings.targets = *targets; - } - - if (m_args.count(g_argModelCheckerTimeout)) - m_modelCheckerSettings.timeout = m_args[g_argModelCheckerTimeout].as(); +bool CommandLineInterface::compile() +{ + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); m_compiler = make_unique(m_fileReader.reader()); - SourceReferenceFormatter formatter(serr(false), m_coloredOutput, m_withErrorIds); + SourceReferenceFormatter formatter(serr(false), coloredOutput(m_options), m_options.formatting.withErrorIds); try { - if (m_args.count(g_argMetadataLiteral) > 0) + if (m_options.metadata.literalSources) m_compiler->useMetadataLiteralSources(true); - if (m_args.count(g_argMetadataHash)) - m_compiler->setMetadataHash(m_metadataHash); - if ( - m_args.count(g_argModelCheckerContracts) || - m_args.count(g_argModelCheckerEngine) || - m_args.count(g_argModelCheckerTargets) || - m_args.count(g_argModelCheckerTimeout) - ) - m_compiler->setModelCheckerSettings(m_modelCheckerSettings); - if (m_args.count(g_argInputFile)) - m_compiler->setRemappings(m_remappings); - - 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); + m_compiler->setMetadataHash(m_options.metadata.hash); + if (m_options.modelChecker.initialize) + m_compiler->setModelCheckerSettings(m_options.modelChecker.settings); + m_compiler->setRemappings(m_options.input.remappings); + m_compiler->setLibraries(m_options.linker.libraries); + m_compiler->setViaIR(m_options.output.experimentalViaIR); + m_compiler->setEVMVersion(m_options.output.evmVersion); + m_compiler->setRevertStringBehaviour(m_options.output.revertStrings); // TODO: Perhaps we should not compile unless requested - m_compiler->enableIRGeneration(m_args.count(g_argIR) || m_args.count(g_argIROptimized)); - m_compiler->enableEwasmGeneration(m_args.count(g_argEwasm)); + m_compiler->enableIRGeneration(m_options.compiler.outputs.ir || m_options.compiler.outputs.irOptimized); + m_compiler->enableEwasmGeneration(m_options.compiler.outputs.ewasm); - OptimiserSettings settings = m_args.count(g_argOptimize) ? OptimiserSettings::standard() : OptimiserSettings::minimal(); - settings.expectedExecutionsPerDeployment = m_args[g_argOptimizeRuns].as(); - if (m_args.count(g_strNoOptimizeYul)) + OptimiserSettings settings = m_options.optimizer.enabled ? OptimiserSettings::standard() : OptimiserSettings::minimal(); + settings.expectedExecutionsPerDeployment = m_options.optimizer.expectedExecutionsPerDeployment; + if (m_options.optimizer.noOptimizeYul) settings.runYulOptimiser = false; - if (m_args.count(g_strYulOptimizations)) - { - if (!settings.runYulOptimiser) - { - serr() << "--" << g_strYulOptimizations << " is invalid if Yul optimizer is disabled" << endl; - return false; - } - try - { - yul::OptimiserSuite::validateSequence(m_args[g_strYulOptimizations].as()); - } - catch (yul::OptimizerException const& _exception) - { - serr() << "Invalid optimizer step sequence in --" << g_strYulOptimizations << ": " << _exception.what() << endl; - return false; - } - - settings.yulOptimiserSteps = m_args[g_strYulOptimizations].as(); - } + if (m_options.optimizer.yulSteps.has_value()) + settings.yulOptimiserSteps = m_options.optimizer.yulSteps.value(); settings.optimizeStackAllocation = settings.runYulOptimiser; m_compiler->setOptimiserSettings(settings); - if (m_args.count(g_argImportAst)) + if (m_options.input.mode == InputMode::CompilerWithASTImport) { try { @@ -1560,11 +619,10 @@ bool CommandLineInterface::processInput() else { m_compiler->setSources(m_fileReader.sourceCodes()); - if (m_args.count(g_argErrorRecovery)) - m_compiler->setParserErrorRecovery(true); + m_compiler->setParserErrorRecovery(m_options.input.errorRecovery); } - bool successful = m_compiler->compile(m_stopAfter); + bool successful = m_compiler->compile(m_options.output.stopAfter); for (auto const& error: m_compiler->errors()) { @@ -1573,12 +631,7 @@ bool CommandLineInterface::processInput() } if (!successful) - { - if (m_args.count(g_argErrorRecovery)) - return true; - else - return false; - } + return m_options.input.errorRecovery; } catch (CompilerError const& _exception) { @@ -1645,14 +698,12 @@ bool CommandLineInterface::processInput() void CommandLineInterface::handleCombinedJSON() { - if (!m_args.count(g_argCombinedJson)) + if (!m_options.compiler.combinedJsonRequests.has_value()) return; Json::Value output(Json::objectValue); output[g_strVersion] = frontend::VersionString; - set requests; - boost::split(requests, m_args[g_argCombinedJson].as(), boost::is_any_of(",")); vector contracts = m_compiler->contractNames(); if (!contracts.empty()) @@ -1660,51 +711,54 @@ void CommandLineInterface::handleCombinedJSON() for (string const& contractName: contracts) { Json::Value& contractData = output[g_strContracts][contractName] = Json::objectValue; - if (requests.count(g_strAbi)) + if (m_options.compiler.combinedJsonRequests->abi) contractData[g_strAbi] = m_compiler->contractABI(contractName); - if (requests.count("metadata")) + if (m_options.compiler.combinedJsonRequests->metadata) contractData["metadata"] = m_compiler->metadata(contractName); - if (requests.count(g_strBinary) && m_compiler->compilationSuccessful()) + if (m_options.compiler.combinedJsonRequests->binary && m_compiler->compilationSuccessful()) contractData[g_strBinary] = m_compiler->object(contractName).toHex(); - if (requests.count(g_strBinaryRuntime) && m_compiler->compilationSuccessful()) + if (m_options.compiler.combinedJsonRequests->binaryRuntime && m_compiler->compilationSuccessful()) contractData[g_strBinaryRuntime] = m_compiler->runtimeObject(contractName).toHex(); - if (requests.count(g_strOpcodes) && m_compiler->compilationSuccessful()) + if (m_options.compiler.combinedJsonRequests->opcodes && m_compiler->compilationSuccessful()) contractData[g_strOpcodes] = evmasm::disassemble(m_compiler->object(contractName).bytecode); - if (requests.count(g_strAsm) && m_compiler->compilationSuccessful()) + if (m_options.compiler.combinedJsonRequests->asm_ && m_compiler->compilationSuccessful()) contractData[g_strAsm] = m_compiler->assemblyJSON(contractName); - if (requests.count(g_strStorageLayout) && m_compiler->compilationSuccessful()) + if (m_options.compiler.combinedJsonRequests->storageLayout && m_compiler->compilationSuccessful()) contractData[g_strStorageLayout] = m_compiler->storageLayout(contractName); - if (requests.count(g_strGeneratedSources) && m_compiler->compilationSuccessful()) + if (m_options.compiler.combinedJsonRequests->generatedSources && m_compiler->compilationSuccessful()) contractData[g_strGeneratedSources] = m_compiler->generatedSources(contractName, false); - if (requests.count(g_strGeneratedSourcesRuntime) && m_compiler->compilationSuccessful()) + if (m_options.compiler.combinedJsonRequests->generatedSourcesRuntime && m_compiler->compilationSuccessful()) contractData[g_strGeneratedSourcesRuntime] = m_compiler->generatedSources(contractName, true); - if (requests.count(g_strSrcMap) && m_compiler->compilationSuccessful()) + if (m_options.compiler.combinedJsonRequests->srcMap && m_compiler->compilationSuccessful()) { auto map = m_compiler->sourceMapping(contractName); contractData[g_strSrcMap] = map ? *map : ""; } - if (requests.count(g_strSrcMapRuntime) && m_compiler->compilationSuccessful()) + if (m_options.compiler.combinedJsonRequests->srcMapRuntime && m_compiler->compilationSuccessful()) { auto map = m_compiler->runtimeSourceMapping(contractName); contractData[g_strSrcMapRuntime] = map ? *map : ""; } - if (requests.count(g_strFunDebug) && m_compiler->compilationSuccessful()) + if (m_options.compiler.combinedJsonRequests->funDebug && m_compiler->compilationSuccessful()) contractData[g_strFunDebug] = StandardCompiler::formatFunctionDebugData( m_compiler->object(contractName).functionDebugData ); - if (requests.count(g_strFunDebugRuntime) && m_compiler->compilationSuccessful()) + if (m_options.compiler.combinedJsonRequests->funDebugRuntime && m_compiler->compilationSuccessful()) contractData[g_strFunDebugRuntime] = StandardCompiler::formatFunctionDebugData( m_compiler->runtimeObject(contractName).functionDebugData ); - if (requests.count(g_strSignatureHashes)) + if (m_options.compiler.combinedJsonRequests->signatureHashes) contractData[g_strSignatureHashes] = m_compiler->methodIdentifiers(contractName); - if (requests.count(g_strNatspecDev)) + if (m_options.compiler.combinedJsonRequests->natspecDev) contractData[g_strNatspecDev] = m_compiler->natspecDev(contractName); - if (requests.count(g_strNatspecUser)) + if (m_options.compiler.combinedJsonRequests->natspecUser) contractData[g_strNatspecUser] = m_compiler->natspecUser(contractName); } - bool needsSourceList = requests.count(g_strAst) || requests.count(g_strSrcMap) || requests.count(g_strSrcMapRuntime); + bool needsSourceList = + m_options.compiler.combinedJsonRequests->ast || + m_options.compiler.combinedJsonRequests->srcMap || + m_options.compiler.combinedJsonRequests->srcMapRuntime; if (needsSourceList) { // Indices into this array are used to abbreviate source names in source locations. @@ -1714,7 +768,7 @@ void CommandLineInterface::handleCombinedJSON() output[g_strSourceList].append(source); } - if (requests.count(g_strAst)) + if (m_options.compiler.combinedJsonRequests->ast) { output[g_strSources] = Json::Value(Json::objectValue); for (auto const& sourceCode: m_fileReader.sourceCodes()) @@ -1725,10 +779,10 @@ void CommandLineInterface::handleCombinedJSON() } } - string json = m_args.count(g_argPrettyJson) ? jsonPrettyPrint(removeNullMembers(std::move(output))) : + string json = m_options.formatting.prettyJson ? jsonPrettyPrint(removeNullMembers(std::move(output))) : jsonCompactPrint(removeNullMembers(std::move(output))); - if (m_args.count(g_argOutputDir)) + if (!m_options.output.dir.empty()) createJson("combined", json); else sout() << json << endl; @@ -1736,14 +790,14 @@ void CommandLineInterface::handleCombinedJSON() void CommandLineInterface::handleAst() { - if (!m_args.count(g_argAstCompactJson)) + if (!m_options.compiler.outputs.astCompactJson) return; vector asts; for (auto const& sourceCode: m_fileReader.sourceCodes()) asts.push_back(&m_compiler->ast(sourceCode.first)); - if (m_args.count(g_argOutputDir)) + if (!m_options.output.dir.empty()) { for (auto const& sourceCode: m_fileReader.sourceCodes()) { @@ -1768,13 +822,16 @@ void CommandLineInterface::handleAst() bool CommandLineInterface::actOnInput() { - if (m_args.count(g_argStandardJSON) || m_onlyAssemble) + if (m_options.input.mode == InputMode::StandardJson || m_options.input.mode == InputMode::Assembler) // Already done in "processInput" phase. return true; - else if (m_onlyLink) + else if (m_options.input.mode == InputMode::Linker) writeLinkedFiles(); else + { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); outputCompilationResults(); + } return !m_error; } @@ -1783,7 +840,7 @@ bool CommandLineInterface::link() // Map from how the libraries will be named inside the bytecode to their addresses. map librariesReplacements; int const placeholderSize = 40; // 20 bytes or 40 hex characters - for (auto const& library: m_libraries) + for (auto const& library: m_options.linker.libraries) { string const& name = library.first; // Library placeholders are 40 hex digits (20 bytes) that start and end with '__'. @@ -1831,7 +888,7 @@ bool CommandLineInterface::link() it += placeholderSize; } // Remove hints for resolved libraries. - for (auto const& library: m_libraries) + for (auto const& library: m_options.linker.libraries) boost::algorithm::erase_all(src.second, "\n" + libraryPlaceholderHint(library.first)); while (!src.second.empty() && *prev(src.second.end()) == '\n') src.second.resize(src.second.size() - 1); @@ -1893,7 +950,7 @@ bool CommandLineInterface::assemble( if (_yulOptimiserSteps.has_value()) settings.yulOptimiserSteps = _yulOptimiserSteps.value(); - auto& stack = assemblyStacks[src.first] = yul::AssemblyStack(m_evmVersion, _language, settings); + auto& stack = assemblyStacks[src.first] = yul::AssemblyStack(m_options.output.evmVersion, _language, settings); try { if (!stack.parseAndAnalyze(src.first, src.second)) @@ -1924,7 +981,7 @@ bool CommandLineInterface::assemble( for (auto const& sourceAndStack: assemblyStacks) { auto const& stack = sourceAndStack.second; - SourceReferenceFormatter formatter(serr(false), m_coloredOutput, m_withErrorIds); + SourceReferenceFormatter formatter(serr(false), coloredOutput(m_options), m_options.formatting.withErrorIds); for (auto const& error: stack.errors()) { @@ -1985,7 +1042,7 @@ bool CommandLineInterface::assemble( try { object = stack.assemble(_targetMachine); - object.bytecode->link(m_libraries); + object.bytecode->link(m_options.linker.libraries); } catch (Exception const& _exception) { @@ -2030,7 +1087,7 @@ void CommandLineInterface::outputCompilationResults() if ( !m_compiler->compilationSuccessful() && - m_stopAfter == CompilerStack::State::CompilationSuccessful + m_options.output.stopAfter == CompilerStack::State::CompilationSuccessful ) { serr() << endl << "Compilation halted after AST generation due to errors." << endl; @@ -2040,21 +1097,21 @@ void CommandLineInterface::outputCompilationResults() vector contracts = m_compiler->contractNames(); for (string const& contract: contracts) { - if (needsHumanTargetedStdout(m_args)) + if (needsHumanTargetedStdout(m_options)) sout() << endl << "======= " << contract << " =======" << endl; // do we need EVM assembly? - if (m_args.count(g_argAsm) || m_args.count(g_argAsmJson)) + if (m_options.compiler.outputs.asm_ || m_options.compiler.outputs.asmJson) { string ret; - if (m_args.count(g_argAsmJson)) + if (m_options.compiler.outputs.asmJson) ret = jsonPrettyPrint(removeNullMembers(m_compiler->assemblyJSON(contract))); else ret = m_compiler->assemblyString(contract, m_fileReader.sourceCodes()); - if (m_args.count(g_argOutputDir)) + if (!m_options.output.dir.empty()) { - createFile(m_compiler->filesystemFriendlyName(contract) + (m_args.count(g_argAsmJson) ? "_evm.json" : ".evm"), ret); + createFile(m_compiler->filesystemFriendlyName(contract) + (m_options.compiler.outputs.asmJson ? "_evm.json" : ".evm"), ret); } else { @@ -2062,7 +1119,7 @@ void CommandLineInterface::outputCompilationResults() } } - if (m_args.count(g_argGas)) + if (m_options.compiler.estimateGas) handleGasEstimation(contract); handleBytecode(contract); @@ -2079,28 +1136,11 @@ void CommandLineInterface::outputCompilationResults() if (!g_hasOutput) { - if (m_args.count(g_argOutputDir)) - sout() << "Compiler run successful. Artifact(s) can be found in directory " << m_args.at(g_argOutputDir).as() << "." << endl; + if (!m_options.output.dir.empty()) + sout() << "Compiler run successful. Artifact(s) can be found in directory " << m_options.output.dir << "." << endl; else serr() << "Compiler run successful, no output requested." << endl; } } -size_t CommandLineInterface::countEnabledOptions(vector const& _optionNames) const -{ - size_t count = 0; - for (string const& _option: _optionNames) - count += m_args.count(_option); - - return count; -} - -string CommandLineInterface::joinOptionNames(vector const& _optionNames, string _separator) -{ - return boost::algorithm::join( - _optionNames | ranges::views::transform([](string const& _option){ return "--" + _option; }), - _separator - ); -} - } diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 73c220e37..26638af1e 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -22,36 +22,39 @@ */ #pragma once +#include + #include #include #include -#include #include -#include - -#include -#include #include +#include namespace solidity::frontend { -//forward declaration -enum class DocumentationType: uint8_t; - class CommandLineInterface { public: + explicit CommandLineInterface(CommandLineOptions const& _options = CommandLineOptions{}): + m_options(_options) + {} + /// Parse command line arguments and return false if we should not continue - bool parseArguments(int _argc, char** _argv); + bool parseArguments(int _argc, char const* const* _argv); /// Parse the files and create source code objects bool processInput(); /// Perform actions on the input depending on provided compiler arguments /// @returns true on success. bool actOnInput(); + CommandLineOptions const& options() const { return m_options; } + FileReader const& fileReader() const { return m_fileReader; } + private: + bool compile(); bool link(); void writeLinkedFiles(); /// @returns the ``// -> name`` hint for library placeholders. @@ -83,11 +86,9 @@ private: void handleGasEstimation(std::string const& _contract); void handleStorageLayout(std::string const& _contract); - /// Fills @a m_sourceCodes initially and @a m_redirects. - bool readInputFilesAndConfigureRemappings(); - /// Tries to read from the file @a _input or interprets _input literally if that fails. - /// It then tries to parse the contents and appends to m_libraries. - bool parseLibraryOption(std::string const& _input); + /// Reads the content of input files specified on the command line and passes them to FileReader. + /// @return false if there are no input files or input files cannot be read. + bool readInputFilesAndConfigureFileReader(); /// Tries to read @ m_sourceCodes as a JSONs holding ASTs /// such that they can be imported into the compiler (importASTs()) @@ -105,38 +106,10 @@ private: /// @arg _json json string to be written void createJson(std::string const& _fileName, std::string const& _json); - size_t countEnabledOptions(std::vector const& _optionNames) const; - static std::string joinOptionNames(std::vector const& _optionNames, std::string _separator = ", "); - bool m_error = false; ///< If true, some error occurred. - - bool m_onlyAssemble = false; - - bool m_onlyLink = false; - FileReader m_fileReader; - - /// Compiler arguments variable map - boost::program_options::variables_map m_args; - /// list of remappings - std::vector m_remappings; - /// map of library names to addresses - std::map m_libraries; - /// Solidity compiler stack std::unique_ptr m_compiler; - CompilerStack::State m_stopAfter = CompilerStack::State::CompilationSuccessful; - /// EVM version to use - langutil::EVMVersion m_evmVersion; - /// How to handle revert strings - RevertStrings m_revertStrings = RevertStrings::Default; - /// Chosen hash method for the bytecode metadata. - CompilerStack::MetadataHash m_metadataHash = CompilerStack::MetadataHash::IPFS; - /// Model checker settings. - ModelCheckerSettings m_modelCheckerSettings; - /// Whether or not to colorize diagnostics output. - bool m_coloredOutput = true; - /// Whether or not to output error IDs. - bool m_withErrorIds = false; + CommandLineOptions m_options; }; } diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 7c2cad4ef..67c74236a 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -15,79 +15,18 @@ along with solidity. If not, see . */ // SPDX-License-Identifier: GPL-3.0 -/** - * @author Lefteris - * @author Gav Wood - * @date 2014 - * Solidity command line interface. - */ -#include -#include "solidity/BuildInfo.h" #include "license.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include +#include #include - -#include -#include - -#include #include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include #include #include -#ifdef _WIN32 // windows - #include - #define isatty _isatty - #define fileno _fileno -#else // unix - #include -#endif - -#include -#include -#include - -#if !defined(STDERR_FILENO) - #define STDERR_FILENO 2 -#endif - using namespace std; -using namespace solidity; -using namespace solidity::util; using namespace solidity::langutil; namespace po = boost::program_options; @@ -95,7 +34,7 @@ namespace po = boost::program_options; namespace solidity::frontend { -bool g_hasOutput = false; +static bool g_hasOutput = false; namespace { @@ -118,7 +57,6 @@ std::ostream& serr(bool _used = true) #define cout #define cerr -static string const g_stdinFileNameStr = ""; static string const g_strAbi = "abi"; static string const g_strAllowPaths = "allow-paths"; static string const g_strBasePath = "base-path"; @@ -131,7 +69,6 @@ static string const g_strBinary = "bin"; static string const g_strBinaryRuntime = "bin-runtime"; static string const g_strCombinedJson = "combined-json"; static string const g_strCompactJSON = "compact-format"; -static string const g_strContracts = "contracts"; static string const g_strErrorRecovery = "error-recovery"; static string const g_strEVM = "evm"; static string const g_strEVMVersion = "evm-version"; @@ -245,7 +182,6 @@ static string const g_argStandardJSON = g_strStandardJSON; static string const g_argStorageLayout = g_strStorageLayout; static string const g_argStrictAssembly = g_strStrictAssembly; static string const g_argVersion = g_strVersion; -static string const g_stdinFileName = g_stdinFileNameStr; static string const g_argIgnoreMissingFiles = g_strIgnoreMissingFiles; static string const g_argColor = g_strColor; static string const g_argNoColor = g_strNoColor; @@ -297,7 +233,8 @@ static set const g_metadataHashArgs g_strNone }; -static void version() +[[noreturn]] +static void printVersionAndExit() { sout() << "solc, the solidity compiler commandline interface" << @@ -305,39 +242,16 @@ static void version() "Version: " << solidity::frontend::VersionString << endl; - exit(0); + exit(EXIT_SUCCESS); } -static void license() +[[noreturn]] +static void printLicenseAndExit() { sout() << otherLicenses << endl; // This is a static variable generated by cmake from LICENSE.txt sout() << licenseText << endl; - exit(0); -} - -static bool needsHumanTargetedStdout(po::variables_map const& _args) -{ - if (_args.count(g_argGas)) - return true; - if (_args.count(g_argOutputDir)) - return false; - for (string const& arg: { - g_argAbi, - g_argAsm, - g_argAsmJson, - g_argBinary, - g_argBinaryRuntime, - g_argMetadata, - g_argNatspecUser, - g_argNatspecDev, - g_argOpcodes, - g_argSignatureHashes, - g_argStorageLayout - }) - if (_args.count(arg)) - return true; - return false; + exit(EXIT_SUCCESS); } namespace @@ -356,235 +270,9 @@ bool checkMutuallyExclusive(boost::program_options::variables_map const& args, s } -void CommandLineInterface::handleBinary(string const& _contract) +bool CommandLineParser::parseInputPathsAndRemappings() { - if (m_args.count(g_argBinary)) - { - if (m_args.count(g_argOutputDir)) - createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin", objectWithLinkRefsHex(m_compiler->object(_contract))); - else - { - sout() << "Binary:" << endl; - sout() << objectWithLinkRefsHex(m_compiler->object(_contract)) << endl; - } - } - if (m_args.count(g_argBinaryRuntime)) - { - if (m_args.count(g_argOutputDir)) - createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin-runtime", objectWithLinkRefsHex(m_compiler->runtimeObject(_contract))); - else - { - sout() << "Binary of the runtime part:" << endl; - sout() << objectWithLinkRefsHex(m_compiler->runtimeObject(_contract)) << endl; - } - } -} - -void CommandLineInterface::handleOpcode(string const& _contract) -{ - if (m_args.count(g_argOutputDir)) - createFile(m_compiler->filesystemFriendlyName(_contract) + ".opcode", evmasm::disassemble(m_compiler->object(_contract).bytecode)); - else - { - sout() << "Opcodes:" << endl; - sout() << std::uppercase << evmasm::disassemble(m_compiler->object(_contract).bytecode); - sout() << endl; - } -} - -void CommandLineInterface::handleIR(string const& _contractName) -{ - if (!m_args.count(g_argIR)) - return; - - if (m_args.count(g_argOutputDir)) - createFile(m_compiler->filesystemFriendlyName(_contractName) + ".yul", m_compiler->yulIR(_contractName)); - else - { - sout() << "IR:" << endl; - sout() << m_compiler->yulIR(_contractName) << endl; - } -} - -void CommandLineInterface::handleIROptimized(string const& _contractName) -{ - if (!m_args.count(g_argIROptimized)) - return; - - if (m_args.count(g_argOutputDir)) - createFile(m_compiler->filesystemFriendlyName(_contractName) + "_opt.yul", m_compiler->yulIROptimized(_contractName)); - else - { - sout() << "Optimized IR:" << endl; - sout() << m_compiler->yulIROptimized(_contractName) << endl; - } -} - -void CommandLineInterface::handleEwasm(string const& _contractName) -{ - if (!m_args.count(g_argEwasm)) - return; - - if (m_args.count(g_argOutputDir)) - { - createFile(m_compiler->filesystemFriendlyName(_contractName) + ".wast", m_compiler->ewasm(_contractName)); - createFile( - m_compiler->filesystemFriendlyName(_contractName) + ".wasm", - asString(m_compiler->ewasmObject(_contractName).bytecode) - ); - } - else - { - sout() << "Ewasm text:" << endl; - sout() << m_compiler->ewasm(_contractName) << endl; - sout() << "Ewasm binary (hex): " << m_compiler->ewasmObject(_contractName).toHex() << endl; - } -} - -void CommandLineInterface::handleBytecode(string const& _contract) -{ - if (m_args.count(g_argOpcodes)) - handleOpcode(_contract); - if (m_args.count(g_argBinary) || m_args.count(g_argBinaryRuntime)) - handleBinary(_contract); -} - -void CommandLineInterface::handleSignatureHashes(string const& _contract) -{ - if (!m_args.count(g_argSignatureHashes)) - return; - - Json::Value methodIdentifiers = m_compiler->methodIdentifiers(_contract); - string out; - for (auto const& name: methodIdentifiers.getMemberNames()) - out += methodIdentifiers[name].asString() + ": " + name + "\n"; - - if (m_args.count(g_argOutputDir)) - createFile(m_compiler->filesystemFriendlyName(_contract) + ".signatures", out); - else - sout() << "Function signatures:" << endl << out; -} - -void CommandLineInterface::handleMetadata(string const& _contract) -{ - if (!m_args.count(g_argMetadata)) - return; - - string data = m_compiler->metadata(_contract); - if (m_args.count(g_argOutputDir)) - createFile(m_compiler->filesystemFriendlyName(_contract) + "_meta.json", data); - else - sout() << "Metadata:" << endl << data << endl; -} - -void CommandLineInterface::handleABI(string const& _contract) -{ - if (!m_args.count(g_argAbi)) - return; - - string data = jsonCompactPrint(removeNullMembers(m_compiler->contractABI(_contract))); - if (m_args.count(g_argOutputDir)) - createFile(m_compiler->filesystemFriendlyName(_contract) + ".abi", data); - else - sout() << "Contract JSON ABI" << endl << data << endl; -} - -void CommandLineInterface::handleStorageLayout(string const& _contract) -{ - if (!m_args.count(g_argStorageLayout)) - return; - - string data = jsonCompactPrint(removeNullMembers(m_compiler->storageLayout(_contract))); - if (m_args.count(g_argOutputDir)) - createFile(m_compiler->filesystemFriendlyName(_contract) + "_storage.json", data); - else - sout() << "Contract Storage Layout:" << endl << data << endl; -} - -void CommandLineInterface::handleNatspec(bool _natspecDev, string const& _contract) -{ - std::string argName; - std::string suffix; - std::string title; - - if (_natspecDev) - { - argName = g_argNatspecDev; - suffix = ".docdev"; - title = "Developer Documentation"; - } - else - { - argName = g_argNatspecUser; - suffix = ".docuser"; - title = "User Documentation"; - } - - if (m_args.count(argName)) - { - std::string output = jsonPrettyPrint( - removeNullMembers( - _natspecDev ? - m_compiler->natspecDev(_contract) : - m_compiler->natspecUser(_contract) - ) - ); - - if (m_args.count(g_argOutputDir)) - createFile(m_compiler->filesystemFriendlyName(_contract) + suffix, output); - else - { - sout() << title << endl; - sout() << output << endl; - } - - } -} - -void CommandLineInterface::handleGasEstimation(string const& _contract) -{ - Json::Value estimates = m_compiler->gasEstimates(_contract); - sout() << "Gas estimation:" << endl; - - if (estimates["creation"].isObject()) - { - Json::Value creation = estimates["creation"]; - sout() << "construction:" << endl; - sout() << " " << creation["executionCost"].asString(); - sout() << " + " << creation["codeDepositCost"].asString(); - sout() << " = " << creation["totalCost"].asString() << endl; - } - - if (estimates["external"].isObject()) - { - Json::Value externalFunctions = estimates["external"]; - sout() << "external:" << endl; - for (auto const& name: externalFunctions.getMemberNames()) - { - if (name.empty()) - sout() << " fallback:\t"; - else - sout() << " " << name << ":\t"; - sout() << externalFunctions[name].asString() << endl; - } - } - - if (estimates["internal"].isObject()) - { - Json::Value internalFunctions = estimates["internal"]; - sout() << "internal:" << endl; - for (auto const& name: internalFunctions.getMemberNames()) - { - sout() << " " << name << ":\t"; - sout() << internalFunctions[name].asString() << endl; - } - } -} - -bool CommandLineInterface::readInputFilesAndConfigureRemappings() -{ - bool ignoreMissing = m_args.count(g_argIgnoreMissingFiles); - bool addStdin = false; + m_options.input.ignoreMissingFiles = (m_args.count(g_argIgnoreMissingFiles) > 0); if (m_args.count(g_argInputFile)) for (string path: m_args[g_argInputFile].as>()) { @@ -592,7 +280,7 @@ bool CommandLineInterface::readInputFilesAndConfigureRemappings() if (eq != path.end()) { if (auto r = ImportRemapper::parseRemapping(path)) - m_remappings.emplace_back(std::move(*r)); + m_options.input.remappings.emplace_back(std::move(*r)); else { serr() << "Invalid remapping: \"" << path << "\"." << endl; @@ -600,58 +288,18 @@ bool CommandLineInterface::readInputFilesAndConfigureRemappings() } string remappingTarget(eq + 1, path.end()); - m_fileReader.allowDirectory(boost::filesystem::path(remappingTarget).remove_filename()); + m_options.input.allowedDirectories.insert(boost::filesystem::path(remappingTarget).remove_filename()); } else if (path == "-") - addStdin = true; + m_options.input.addStdin = true; else - { - auto infile = boost::filesystem::path(path); - if (!boost::filesystem::exists(infile)) - { - if (!ignoreMissing) - { - serr() << infile << " is not found." << endl; - return false; - } - else - serr() << infile << " is not found. Skipping." << endl; - - continue; - } - - if (!boost::filesystem::is_regular_file(infile)) - { - if (!ignoreMissing) - { - serr() << infile << " is not a valid file." << endl; - return false; - } - else - serr() << infile << " is not a valid file. Skipping." << endl; - - continue; - } - - // NOTE: we ignore the FileNotFound exception as we manually check above - m_fileReader.setSource(infile, readFileAsString(infile.string())); - m_fileReader.allowDirectory(boost::filesystem::path(boost::filesystem::canonical(infile).string()).remove_filename()); - } + m_options.input.paths.insert(path); } - if (addStdin) - m_fileReader.setSource(g_stdinFileName, readUntilEnd(cin)); - - if (m_fileReader.sourceCodes().size() == 0) - { - serr() << "No input files given. If you wish to use the standard input please specify \"-\" explicitly." << endl; - return false; - } - return true; } -bool CommandLineInterface::parseLibraryOption(string const& _input) +bool CommandLineParser::parseLibraryOption(string const& _input) { namespace fs = boost::filesystem; string data = _input; @@ -702,7 +350,7 @@ bool CommandLineInterface::parseLibraryOption(string const& _input) string libName(lib.begin(), lib.begin() + static_cast(separator)); boost::trim(libName); - if (m_libraries.count(libName)) + if (m_options.linker.libraries.count(libName)) { serr() << "Address specified more than once for library \"" << libName << "\"." << endl; return false; @@ -744,74 +392,13 @@ bool CommandLineInterface::parseLibraryOption(string const& _input) serr() << "Invalid address for library \"" << libName << "\": " << addrString << endl; return false; } - m_libraries[libName] = address; + m_options.linker.libraries[libName] = address; } return true; } -map CommandLineInterface::parseAstFromInput() -{ - map sourceJsons; - map tmpSources; - - for (SourceCode const& sourceCode: m_fileReader.sourceCodes() | ranges::views::values) - { - Json::Value ast; - astAssert(jsonParseStrict(sourceCode, ast), "Input file could not be parsed to JSON"); - astAssert(ast.isMember("sources"), "Invalid Format for import-JSON: Must have 'sources'-object"); - - for (auto& src: ast["sources"].getMemberNames()) - { - std::string astKey = ast["sources"][src].isMember("ast") ? "ast" : "AST"; - - astAssert(ast["sources"][src].isMember(astKey), "astkey is not member"); - astAssert(ast["sources"][src][astKey]["nodeType"].asString() == "SourceUnit", "Top-level node should be a 'SourceUnit'"); - astAssert(sourceJsons.count(src) == 0, "All sources must have unique names"); - sourceJsons.emplace(src, move(ast["sources"][src][astKey])); - tmpSources[src] = util::jsonCompactPrint(ast); - } - } - - m_fileReader.setSources(tmpSources); - - return sourceJsons; -} - -void CommandLineInterface::createFile(string const& _fileName, string const& _data) -{ - namespace fs = boost::filesystem; - - 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; - m_error = true; - return; - } - ofstream outFile(pathName); - outFile << _data; - if (!outFile) - { - serr() << "Could not write to file \"" << pathName << "\"." << endl; - m_error = true; - return; - } -} - -void CommandLineInterface::createJson(string const& _fileName, string const& _json) -{ - createFile(boost::filesystem::basename(_fileName) + string(".json"), _json); -} - -bool CommandLineInterface::parseArguments(int _argc, char** _argv) +bool CommandLineParser::parse(int _argc, char const* const* _argv, bool interactiveTerminal) { g_hasOutput = false; @@ -1118,7 +705,7 @@ General Information)").c_str(), if (!checkMutuallyExclusive(m_args, g_argColor, g_argNoColor)) return false; - static vector const conflictingWithStopAfter{ + array const conflictingWithStopAfter{ g_argBinary, g_argIR, g_argIROptimized, @@ -1133,27 +720,26 @@ General Information)").c_str(), if (!checkMutuallyExclusive(m_args, g_strStopAfter, option)) return false; - m_coloredOutput = !m_args.count(g_argNoColor) && (isatty(STDERR_FILENO) || m_args.count(g_argColor)); + if (m_args.count(g_argColor) > 0) + m_options.formatting.coloredOutput = true; + else if (m_args.count(g_argNoColor) > 0) + m_options.formatting.coloredOutput = false; + else + solAssert(!m_options.formatting.coloredOutput.has_value(), ""); - m_withErrorIds = m_args.count(g_argErrorIds); + m_options.formatting.withErrorIds = m_args.count(g_argErrorIds); - if (m_args.count(g_argHelp) || (isatty(fileno(stdin)) && _argc == 1)) + if (m_args.count(g_argHelp) || (interactiveTerminal && _argc == 1)) { sout() << desc; return false; } if (m_args.count(g_argVersion)) - { - version(); - return false; - } + printVersionAndExit(); if (m_args.count(g_strLicense)) - { - license(); - return false; - } + printLicenseAndExit(); if (m_args.count(g_strRevertStrings)) { @@ -1169,36 +755,44 @@ General Information)").c_str(), serr() << "Only \"default\", \"strip\" and \"debug\" are implemented for --" << g_strRevertStrings << " for now." << endl; return false; } - m_revertStrings = *revertStrings; + m_options.output.revertStrings = *revertStrings; } - if (m_args.count(g_argCombinedJson)) - { - vector requests; - for (string const& item: boost::split(requests, m_args[g_argCombinedJson].as(), boost::is_any_of(","))) - if (!g_combinedJsonArgs.count(item)) - { - serr() << "Invalid option to --" << g_argCombinedJson << ": " << item << endl; - return false; - } - } + if (!parseCombinedJsonOption()) + return false; + + if (m_args.count(g_argOutputDir)) + m_options.output.dir = m_args.at(g_argOutputDir).as(); + + m_options.output.overwriteFiles = (m_args.count(g_strOverwrite) > 0); + m_options.formatting.prettyJson = (m_args.count(g_argPrettyJson) > 0); + + static_assert( + sizeof(m_options.compiler.selectedOutputs) == 15 * sizeof(bool), + "Remember to update code below if you add/remove fields." + ); + m_options.compiler.outputs.astCompactJson = (m_args.count(g_argAstCompactJson) > 0); + m_options.compiler.outputs.asm_ = (m_args.count(g_argAsm) > 0); + m_options.compiler.outputs.asmJson = (m_args.count(g_argAsmJson) > 0); + m_options.compiler.outputs.opcodes = (m_args.count(g_argOpcodes) > 0); + m_options.compiler.outputs.binary = (m_args.count(g_argBinary) > 0); + m_options.compiler.outputs.binaryRuntime = (m_args.count(g_argBinaryRuntime) > 0); + m_options.compiler.outputs.abi = (m_args.count(g_argAbi) > 0); + m_options.compiler.outputs.ir = (m_args.count(g_argIR) > 0); + m_options.compiler.outputs.irOptimized = (m_args.count(g_argIROptimized) > 0); + m_options.compiler.outputs.ewasm = (m_args.count(g_argEwasm) > 0); + m_options.compiler.outputs.signatureHashes = (m_args.count(g_argSignatureHashes) > 0); + m_options.compiler.outputs.natspecUser = (m_args.count(g_argNatspecUser) > 0); + m_options.compiler.outputs.natspecDev = (m_args.count(g_argNatspecDev) > 0); + m_options.compiler.outputs.metadata = (m_args.count(g_argMetadata) > 0); + m_options.compiler.outputs.storageLayout = (m_args.count(g_argStorageLayout) > 0); + + m_options.compiler.estimateGas = (m_args.count(g_argGas) > 0); + po::notify(m_args); - return true; -} - -bool CommandLineInterface::processInput() -{ if (m_args.count(g_argBasePath)) - { - boost::filesystem::path const fspath{m_args[g_argBasePath].as()}; - if (!boost::filesystem::is_directory(fspath)) - { - serr() << "Base path must be a directory: \"" << fspath << "\"\n"; - return false; - } - m_fileReader.setBasePath(fspath); - } + m_options.input.basePath = m_args[g_argBasePath].as(); if (m_args.count(g_argAllowPaths)) { @@ -1212,7 +806,7 @@ bool CommandLineInterface::processInput() // it. if (filesystem_path.filename() == ".") filesystem_path.remove_filename(); - m_fileReader.allowDirectory(filesystem_path); + m_options.input.allowedDirectories.insert(filesystem_path); } } @@ -1224,7 +818,7 @@ bool CommandLineInterface::processInput() return false; } else - m_stopAfter = CompilerStack::State::Parsed; + m_options.output.stopAfter = CompilerStack::State::Parsed; } vector const exclusiveModes = { @@ -1244,43 +838,23 @@ bool CommandLineInterface::processInput() if (m_args.count(g_argStandardJSON)) { + m_options.input.mode = InputMode::StandardJson; + vector inputFiles; - string jsonFile; if (m_args.count(g_argInputFile)) inputFiles = m_args[g_argInputFile].as>(); if (inputFiles.size() == 1) - jsonFile = inputFiles[0]; + m_options.input.standardJsonFile = inputFiles[0]; else if (inputFiles.size() > 1) { serr() << "If --" << g_argStandardJSON << " is used, only zero or one input files are supported." << endl; return false; } - string input; - if (jsonFile.empty()) - input = readUntilEnd(cin); - else - { - try - { - input = readFileAsString(jsonFile); - } - catch (FileNotFound const&) - { - serr() << "File not found: " << jsonFile << endl; - return false; - } - catch (NotAFile const&) - { - serr() << "Not a regular file: " << jsonFile << endl; - return false; - } - } - StandardCompiler compiler(m_fileReader.reader()); - sout() << compiler.compile(std::move(input)) << endl; + return true; } - if (!readInputFilesAndConfigureRemappings()) + if (!parseInputPathsAndRemappings()) return false; if (m_args.count(g_argLibraries)) @@ -1297,11 +871,13 @@ bool CommandLineInterface::processInput() serr() << "Invalid option for --" << g_strEVMVersion << ": " << versionOptionStr << endl; return false; } - m_evmVersion = *versionOption; + m_options.output.evmVersion = *versionOption; } if (m_args.count(g_argAssemble) || m_args.count(g_argStrictAssembly) || m_args.count(g_argYul)) { + m_options.input.mode = InputMode::Assembler; + vector const nonAssemblyModeOptions = { // TODO: The list is not complete. Add more. g_argOutputDir, @@ -1324,17 +900,15 @@ bool CommandLineInterface::processInput() } // switch to assembly mode - m_onlyAssemble = true; using Input = yul::AssemblyStack::Language; using Machine = yul::AssemblyStack::Machine; - Input inputLanguage = m_args.count(g_argYul) ? Input::Yul : (m_args.count(g_argStrictAssembly) ? Input::StrictAssembly : Input::Assembly); - Machine targetMachine = Machine::EVM; - bool optimize = m_args.count(g_argOptimize); + m_options.assembly.inputLanguage = m_args.count(g_argYul) ? Input::Yul : (m_args.count(g_argStrictAssembly) ? Input::StrictAssembly : Input::Assembly); + m_options.optimizer.enabled = (m_args.count(g_argOptimize) > 0); + m_options.optimizer.noOptimizeYul = (m_args.count(g_strNoOptimizeYul) > 0); - optional yulOptimiserSteps; if (m_args.count(g_strYulOptimizations)) { - if (!optimize) + if (!m_options.optimizer.enabled) { serr() << "--" << g_strYulOptimizations << " is invalid if Yul optimizer is disabled" << endl; return false; @@ -1350,33 +924,33 @@ bool CommandLineInterface::processInput() return false; } - yulOptimiserSteps = m_args[g_strYulOptimizations].as(); + m_options.optimizer.yulSteps = m_args[g_strYulOptimizations].as(); } if (m_args.count(g_argMachine)) { string machine = m_args[g_argMachine].as(); if (machine == g_strEVM) - targetMachine = Machine::EVM; + m_options.assembly.targetMachine = Machine::EVM; else if (machine == g_strEwasm) - targetMachine = Machine::Ewasm; + m_options.assembly.targetMachine = Machine::Ewasm; else { serr() << "Invalid option for --" << g_argMachine << ": " << machine << endl; return false; } } - if (targetMachine == Machine::Ewasm && inputLanguage == Input::StrictAssembly) - inputLanguage = Input::Ewasm; + if (m_options.assembly.targetMachine == Machine::Ewasm && m_options.assembly.inputLanguage == Input::StrictAssembly) + m_options.assembly.inputLanguage = Input::Ewasm; if (m_args.count(g_strYulDialect)) { string dialect = m_args[g_strYulDialect].as(); if (dialect == g_strEVM) - inputLanguage = Input::StrictAssembly; + m_options.assembly.inputLanguage = Input::StrictAssembly; else if (dialect == g_strEwasm) { - inputLanguage = Input::Ewasm; - if (targetMachine != Machine::Ewasm) + m_options.assembly.inputLanguage = Input::Ewasm; + if (m_options.assembly.targetMachine != Machine::Ewasm) { serr() << "If you select Ewasm as --" << g_strYulDialect << ", "; serr() << "--" << g_argMachine << " has to be Ewasm as well." << endl; @@ -1389,7 +963,7 @@ bool CommandLineInterface::processInput() return false; } } - if (optimize && (inputLanguage != Input::StrictAssembly && inputLanguage != Input::Ewasm)) + if (m_options.optimizer.enabled && (m_options.assembly.inputLanguage != Input::StrictAssembly && m_options.assembly.inputLanguage != Input::Ewasm)) { serr() << "Optimizer can only be used for strict assembly. Use --" << @@ -1398,7 +972,7 @@ bool CommandLineInterface::processInput() endl; return false; } - if (targetMachine == Machine::Ewasm && inputLanguage != Input::StrictAssembly && inputLanguage != Input::Ewasm) + if (m_options.assembly.targetMachine == Machine::Ewasm && m_options.assembly.inputLanguage != Input::StrictAssembly && m_options.assembly.inputLanguage != Input::Ewasm) { serr() << "The selected input language is not directly supported when targeting the Ewasm machine "; serr() << "and automatic translation is not available." << endl; @@ -1408,7 +982,7 @@ bool CommandLineInterface::processInput() "Warning: Yul is still experimental. Please use the output with care." << endl; - return assemble(inputLanguage, targetMachine, optimize, yulOptimiserSteps); + return true; } else if (countEnabledOptions({g_strYulDialect, g_argMachine}) >= 1) { @@ -1419,20 +993,19 @@ bool CommandLineInterface::processInput() if (m_args.count(g_argLink)) { - // switch to linker mode - m_onlyLink = true; - return link(); + m_options.input.mode = InputMode::Linker; + return true; } if (m_args.count(g_argMetadataHash)) { string hashStr = m_args[g_argMetadataHash].as(); if (hashStr == g_strIPFS) - m_metadataHash = CompilerStack::MetadataHash::IPFS; + m_options.metadata.hash = CompilerStack::MetadataHash::IPFS; else if (hashStr == g_strSwarm) - m_metadataHash = CompilerStack::MetadataHash::Bzzr1; + m_options.metadata.hash = CompilerStack::MetadataHash::Bzzr1; else if (hashStr == g_strNone) - m_metadataHash = CompilerStack::MetadataHash::None; + m_options.metadata.hash = CompilerStack::MetadataHash::None; else { serr() << "Invalid option for --" << g_argMetadataHash << ": " << hashStr << endl; @@ -1449,10 +1022,9 @@ bool CommandLineInterface::processInput() serr() << "Invalid option for --" << g_argModelCheckerContracts << ": " << contractsStr << endl; return false; } - m_modelCheckerSettings.contracts = move(*contracts); + m_options.modelChecker.settings.contracts = move(*contracts); } - if (m_args.count(g_argModelCheckerEngine)) { string engineStr = m_args[g_argModelCheckerEngine].as(); @@ -1462,7 +1034,7 @@ bool CommandLineInterface::processInput() serr() << "Invalid option for --" << g_argModelCheckerEngine << ": " << engineStr << endl; return false; } - m_modelCheckerSettings.engine = *engine; + m_options.modelChecker.settings.engine = *engine; } if (m_args.count(g_argModelCheckerTargets)) @@ -1474,619 +1046,98 @@ bool CommandLineInterface::processInput() serr() << "Invalid option for --" << g_argModelCheckerTargets << ": " << targetsStr << endl; return false; } - m_modelCheckerSettings.targets = *targets; + m_options.modelChecker.settings.targets = *targets; } if (m_args.count(g_argModelCheckerTimeout)) - m_modelCheckerSettings.timeout = m_args[g_argModelCheckerTimeout].as(); + m_options.modelChecker.settings.timeout = m_args[g_argModelCheckerTimeout].as(); - m_compiler = make_unique(m_fileReader.reader()); + m_options.metadata.literalSources = (m_args.count(g_argMetadataLiteral) > 0); + m_options.modelChecker.initialize = + m_args.count(g_argModelCheckerContracts) || + m_args.count(g_argModelCheckerEngine) || + m_args.count(g_argModelCheckerTargets) || + m_args.count(g_argModelCheckerTimeout); + m_options.output.experimentalViaIR = (m_args.count(g_argExperimentalViaIR) > 0); + m_options.optimizer.expectedExecutionsPerDeployment = m_args[g_argOptimizeRuns].as(); - SourceReferenceFormatter formatter(serr(false), m_coloredOutput, m_withErrorIds); + m_options.optimizer.enabled = (m_args.count(g_argOptimize) > 0); + m_options.optimizer.noOptimizeYul = (m_args.count(g_strNoOptimizeYul) > 0); - try + OptimiserSettings settings = m_options.optimizer.enabled ? OptimiserSettings::standard() : OptimiserSettings::minimal(); + if (m_options.optimizer.noOptimizeYul) + settings.runYulOptimiser = false; + if (m_args.count(g_strYulOptimizations)) { - if (m_args.count(g_argMetadataLiteral) > 0) - m_compiler->useMetadataLiteralSources(true); - if (m_args.count(g_argMetadataHash)) - m_compiler->setMetadataHash(m_metadataHash); - if ( - m_args.count(g_argModelCheckerContracts) || - m_args.count(g_argModelCheckerEngine) || - m_args.count(g_argModelCheckerTargets) || - m_args.count(g_argModelCheckerTimeout) - ) - m_compiler->setModelCheckerSettings(m_modelCheckerSettings); - if (m_args.count(g_argInputFile)) - m_compiler->setRemappings(m_remappings); - - 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 - - m_compiler->enableIRGeneration(m_args.count(g_argIR) || m_args.count(g_argIROptimized)); - m_compiler->enableEwasmGeneration(m_args.count(g_argEwasm)); - - OptimiserSettings settings = m_args.count(g_argOptimize) ? OptimiserSettings::standard() : OptimiserSettings::minimal(); - settings.expectedExecutionsPerDeployment = m_args[g_argOptimizeRuns].as(); - if (m_args.count(g_strNoOptimizeYul)) - settings.runYulOptimiser = false; - if (m_args.count(g_strYulOptimizations)) + if (!settings.runYulOptimiser) { - if (!settings.runYulOptimiser) - { - serr() << "--" << g_strYulOptimizations << " is invalid if Yul optimizer is disabled" << endl; - return false; - } - - try - { - yul::OptimiserSuite::validateSequence(m_args[g_strYulOptimizations].as()); - } - catch (yul::OptimizerException const& _exception) - { - serr() << "Invalid optimizer step sequence in --" << g_strYulOptimizations << ": " << _exception.what() << endl; - return false; - } - - settings.yulOptimiserSteps = m_args[g_strYulOptimizations].as(); - } - settings.optimizeStackAllocation = settings.runYulOptimiser; - m_compiler->setOptimiserSettings(settings); - - if (m_args.count(g_argImportAst)) - { - try - { - m_compiler->importASTs(parseAstFromInput()); - - if (!m_compiler->analyze()) - { - for (auto const& error: m_compiler->errors()) - formatter.printErrorInformation(*error); - astAssert(false, "Analysis of the AST failed"); - } - } - catch (Exception const& _exc) - { - serr() << string("Failed to import AST: ") << _exc.what() << endl; - return false; - } - } - else - { - m_compiler->setSources(m_fileReader.sourceCodes()); - if (m_args.count(g_argErrorRecovery)) - m_compiler->setParserErrorRecovery(true); + serr() << "--" << g_strYulOptimizations << " is invalid if Yul optimizer is disabled" << endl; + return false; } - bool successful = m_compiler->compile(m_stopAfter); - - for (auto const& error: m_compiler->errors()) + try { - g_hasOutput = true; - formatter.printErrorInformation(*error); + yul::OptimiserSuite::validateSequence(m_args[g_strYulOptimizations].as()); + } + catch (yul::OptimizerException const& _exception) + { + serr() << "Invalid optimizer step sequence in --" << g_strYulOptimizations << ": " << _exception.what() << endl; + return false; } - if (!successful) - { - if (m_args.count(g_argErrorRecovery)) - return true; - else - return false; - } - } - catch (CompilerError const& _exception) - { - g_hasOutput = true; - formatter.printExceptionInformation(_exception, "Compiler error"); - return false; - } - catch (InternalCompilerError const& _exception) - { - serr() << - "Internal compiler error during compilation:" << - endl << - boost::diagnostic_information(_exception); - return false; - } - catch (UnimplementedFeatureError const& _exception) - { - serr() << - "Unimplemented feature:" << - endl << - boost::diagnostic_information(_exception); - return false; - } - catch (smtutil::SMTLogicError const& _exception) - { - serr() << - "SMT logic error during analysis:" << - endl << - boost::diagnostic_information(_exception); - return false; - } - catch (Error const& _error) - { - if (_error.type() == Error::Type::DocstringParsingError) - serr() << "Documentation parsing error: " << *boost::get_error_info(_error) << endl; - else - { - g_hasOutput = true; - formatter.printExceptionInformation(_error, _error.typeName()); - } - - return false; - } - catch (Exception const& _exception) - { - serr() << "Exception during compilation: " << boost::diagnostic_information(_exception) << endl; - return false; - } - catch (std::exception const& _e) - { - serr() << "Unknown exception during compilation" << ( - _e.what() ? ": " + string(_e.what()) : "." - ) << endl; - return false; - } - catch (...) - { - serr() << "Unknown exception during compilation." << endl; - return false; + m_options.optimizer.yulSteps = m_args[g_strYulOptimizations].as(); } + if (m_args.count(g_argImportAst) > 0) + m_options.input.mode = InputMode::CompilerWithASTImport; + else + m_options.input.errorRecovery = (m_args.count(g_argErrorRecovery) > 0); + + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); return true; } -void CommandLineInterface::handleCombinedJSON() +bool CommandLineParser::hasOutput() +{ + return g_hasOutput; +} + +bool CommandLineParser::parseCombinedJsonOption() { if (!m_args.count(g_argCombinedJson)) - return; - - Json::Value output(Json::objectValue); - - output[g_strVersion] = frontend::VersionString; - set requests; - boost::split(requests, m_args[g_argCombinedJson].as(), boost::is_any_of(",")); - vector contracts = m_compiler->contractNames(); - - if (!contracts.empty()) - output[g_strContracts] = Json::Value(Json::objectValue); - for (string const& contractName: contracts) - { - Json::Value& contractData = output[g_strContracts][contractName] = Json::objectValue; - if (requests.count(g_strAbi)) - contractData[g_strAbi] = m_compiler->contractABI(contractName); - if (requests.count("metadata")) - contractData["metadata"] = m_compiler->metadata(contractName); - if (requests.count(g_strBinary) && m_compiler->compilationSuccessful()) - contractData[g_strBinary] = m_compiler->object(contractName).toHex(); - if (requests.count(g_strBinaryRuntime) && m_compiler->compilationSuccessful()) - contractData[g_strBinaryRuntime] = m_compiler->runtimeObject(contractName).toHex(); - if (requests.count(g_strOpcodes) && m_compiler->compilationSuccessful()) - contractData[g_strOpcodes] = evmasm::disassemble(m_compiler->object(contractName).bytecode); - if (requests.count(g_strAsm) && m_compiler->compilationSuccessful()) - contractData[g_strAsm] = m_compiler->assemblyJSON(contractName); - if (requests.count(g_strStorageLayout) && m_compiler->compilationSuccessful()) - contractData[g_strStorageLayout] = m_compiler->storageLayout(contractName); - if (requests.count(g_strGeneratedSources) && m_compiler->compilationSuccessful()) - contractData[g_strGeneratedSources] = m_compiler->generatedSources(contractName, false); - if (requests.count(g_strGeneratedSourcesRuntime) && m_compiler->compilationSuccessful()) - contractData[g_strGeneratedSourcesRuntime] = m_compiler->generatedSources(contractName, true); - if (requests.count(g_strSrcMap) && m_compiler->compilationSuccessful()) - { - auto map = m_compiler->sourceMapping(contractName); - contractData[g_strSrcMap] = map ? *map : ""; - } - if (requests.count(g_strSrcMapRuntime) && m_compiler->compilationSuccessful()) - { - auto map = m_compiler->runtimeSourceMapping(contractName); - contractData[g_strSrcMapRuntime] = map ? *map : ""; - } - if (requests.count(g_strFunDebug) && m_compiler->compilationSuccessful()) - contractData[g_strFunDebug] = StandardCompiler::formatFunctionDebugData( - m_compiler->object(contractName).functionDebugData - ); - if (requests.count(g_strFunDebugRuntime) && m_compiler->compilationSuccessful()) - contractData[g_strFunDebugRuntime] = StandardCompiler::formatFunctionDebugData( - m_compiler->runtimeObject(contractName).functionDebugData - ); - if (requests.count(g_strSignatureHashes)) - contractData[g_strSignatureHashes] = m_compiler->methodIdentifiers(contractName); - if (requests.count(g_strNatspecDev)) - contractData[g_strNatspecDev] = m_compiler->natspecDev(contractName); - if (requests.count(g_strNatspecUser)) - contractData[g_strNatspecUser] = m_compiler->natspecUser(contractName); - } - - bool needsSourceList = requests.count(g_strAst) || requests.count(g_strSrcMap) || requests.count(g_strSrcMapRuntime); - if (needsSourceList) - { - // Indices into this array are used to abbreviate source names in source locations. - output[g_strSourceList] = Json::Value(Json::arrayValue); - - for (auto const& source: m_compiler->sourceNames()) - output[g_strSourceList].append(source); - } - - if (requests.count(g_strAst)) - { - output[g_strSources] = Json::Value(Json::objectValue); - for (auto const& sourceCode: m_fileReader.sourceCodes()) - { - ASTJsonConverter converter(m_compiler->state(), m_compiler->sourceIndices()); - output[g_strSources][sourceCode.first] = Json::Value(Json::objectValue); - output[g_strSources][sourceCode.first]["AST"] = converter.toJson(m_compiler->ast(sourceCode.first)); - } - } - - string json = m_args.count(g_argPrettyJson) ? jsonPrettyPrint(removeNullMembers(std::move(output))) : - jsonCompactPrint(removeNullMembers(std::move(output))); - - if (m_args.count(g_argOutputDir)) - createJson("combined", json); - else - sout() << json << endl; -} - -void CommandLineInterface::handleAst() -{ - if (!m_args.count(g_argAstCompactJson)) - return; - - vector asts; - for (auto const& sourceCode: m_fileReader.sourceCodes()) - asts.push_back(&m_compiler->ast(sourceCode.first)); - - if (m_args.count(g_argOutputDir)) - { - for (auto const& sourceCode: m_fileReader.sourceCodes()) - { - stringstream data; - string postfix = ""; - ASTJsonConverter(m_compiler->state(), m_compiler->sourceIndices()).print(data, m_compiler->ast(sourceCode.first)); - postfix += "_json"; - boost::filesystem::path path(sourceCode.first); - createFile(path.filename().string() + postfix + ".ast", data.str()); - } - } - else - { - sout() << "JSON AST (compact format):" << endl << endl; - for (auto const& sourceCode: m_fileReader.sourceCodes()) - { - sout() << endl << "======= " << sourceCode.first << " =======" << endl; - ASTJsonConverter(m_compiler->state(), m_compiler->sourceIndices()).print(sout(), m_compiler->ast(sourceCode.first)); - } - } -} - -bool CommandLineInterface::actOnInput() -{ - if (m_args.count(g_argStandardJSON) || m_onlyAssemble) - // Already done in "processInput" phase. return true; - else if (m_onlyLink) - writeLinkedFiles(); - else - outputCompilationResults(); - return !m_error; -} -bool CommandLineInterface::link() -{ - // Map from how the libraries will be named inside the bytecode to their addresses. - map librariesReplacements; - int const placeholderSize = 40; // 20 bytes or 40 hex characters - for (auto const& library: m_libraries) - { - string const& name = library.first; - // Library placeholders are 40 hex digits (20 bytes) that start and end with '__'. - // This leaves 36 characters for the library identifier. The identifier used to - // be just the cropped or '_'-padded library name, but this changed to - // the cropped hex representation of the hash of the library name. - // We support both ways of linking here. - librariesReplacements["__" + evmasm::LinkerObject::libraryPlaceholder(name) + "__"] = library.second; - - string replacement = "__"; - for (size_t i = 0; i < placeholderSize - 4; ++i) - replacement.push_back(i < name.size() ? name[i] : '_'); - replacement += "__"; - librariesReplacements[replacement] = library.second; - } - - FileReader::StringMap sourceCodes = m_fileReader.sourceCodes(); - for (auto& src: sourceCodes) - { - auto end = src.second.end(); - for (auto it = src.second.begin(); it != end;) + set requests; + for (string const& item: boost::split(requests, m_args[g_argCombinedJson].as(), boost::is_any_of(","))) + if (!g_combinedJsonArgs.count(item)) { - while (it != end && *it != '_') ++it; - if (it == end) break; - if ( - end - it < placeholderSize || - *(it + 1) != '_' || - *(it + placeholderSize - 2) != '_' || - *(it + placeholderSize - 1) != '_' - ) - { - serr() << "Error in binary object file " << src.first << " at position " << (it - src.second.begin()) << endl; - serr() << '"' << string(it, it + min(placeholderSize, static_cast(end - it))) << "\" is not a valid link reference." << endl; - return false; - } - - string foundPlaceholder(it, it + placeholderSize); - if (librariesReplacements.count(foundPlaceholder)) - { - string hexStr(toHex(librariesReplacements.at(foundPlaceholder).asBytes())); - copy(hexStr.begin(), hexStr.end(), it); - } - else - serr() << "Reference \"" << foundPlaceholder << "\" in file \"" << src.first << "\" still unresolved." << endl; - it += placeholderSize; + serr() << "Invalid option to --" << g_argCombinedJson << ": " << item << endl; + return false; } - // Remove hints for resolved libraries. - for (auto const& library: m_libraries) - boost::algorithm::erase_all(src.second, "\n" + libraryPlaceholderHint(library.first)); - while (!src.second.empty() && *prev(src.second.end()) == '\n') - src.second.resize(src.second.size() - 1); - } - m_fileReader.setSources(move(sourceCodes)); + + m_options.compiler.combinedJsonRequests = CombinedJsonRequests{}; + m_options.compiler.combinedJsonRequests->abi = (requests.count(g_strAbi) > 0); + m_options.compiler.combinedJsonRequests->metadata = (requests.count("metadata") > 0); + m_options.compiler.combinedJsonRequests->binary = (requests.count(g_strBinary) > 0); + m_options.compiler.combinedJsonRequests->binaryRuntime = (requests.count(g_strBinaryRuntime) > 0); + m_options.compiler.combinedJsonRequests->opcodes = (requests.count(g_strOpcodes) > 0); + m_options.compiler.combinedJsonRequests->asm_ = (requests.count(g_strAsm) > 0); + m_options.compiler.combinedJsonRequests->storageLayout = (requests.count(g_strStorageLayout) > 0); + m_options.compiler.combinedJsonRequests->generatedSources = (requests.count(g_strGeneratedSources) > 0); + m_options.compiler.combinedJsonRequests->generatedSourcesRuntime = (requests.count(g_strGeneratedSourcesRuntime) > 0); + m_options.compiler.combinedJsonRequests->srcMap = (requests.count(g_strSrcMap) > 0); + m_options.compiler.combinedJsonRequests->srcMapRuntime = (requests.count(g_strSrcMapRuntime) > 0); + m_options.compiler.combinedJsonRequests->funDebug = (requests.count(g_strFunDebug) > 0); + m_options.compiler.combinedJsonRequests->funDebugRuntime = (requests.count(g_strFunDebugRuntime) > 0); + m_options.compiler.combinedJsonRequests->signatureHashes = (requests.count(g_strSignatureHashes) > 0); + m_options.compiler.combinedJsonRequests->natspecDev = (requests.count(g_strNatspecDev) > 0); + m_options.compiler.combinedJsonRequests->natspecUser = (requests.count(g_strNatspecUser) > 0); + m_options.compiler.combinedJsonRequests->ast = (requests.count(g_strAst) > 0); return true; } -void CommandLineInterface::writeLinkedFiles() -{ - for (auto const& src: m_fileReader.sourceCodes()) - if (src.first == g_stdinFileName) - sout() << src.second << endl; - else - { - ofstream outFile(src.first); - outFile << src.second; - if (!outFile) - { - serr() << "Could not write to file " << src.first << ". Aborting." << endl; - return; - } - } - sout() << "Linking completed." << endl; -} - -string CommandLineInterface::libraryPlaceholderHint(string const& _libraryName) -{ - return "// " + evmasm::LinkerObject::libraryPlaceholder(_libraryName) + " -> " + _libraryName; -} - -string CommandLineInterface::objectWithLinkRefsHex(evmasm::LinkerObject const& _obj) -{ - string out = _obj.toHex(); - if (!_obj.linkReferences.empty()) - { - out += "\n"; - for (auto const& linkRef: _obj.linkReferences) - out += "\n" + libraryPlaceholderHint(linkRef.second); - } - return out; -} - -bool CommandLineInterface::assemble( - yul::AssemblyStack::Language _language, - yul::AssemblyStack::Machine _targetMachine, - bool _optimize, - optional _yulOptimiserSteps -) -{ - solAssert(_optimize || !_yulOptimiserSteps.has_value(), ""); - - bool successful = true; - map assemblyStacks; - for (auto const& src: m_fileReader.sourceCodes()) - { - OptimiserSettings settings = _optimize ? OptimiserSettings::full() : OptimiserSettings::minimal(); - if (_yulOptimiserSteps.has_value()) - settings.yulOptimiserSteps = _yulOptimiserSteps.value(); - - auto& stack = assemblyStacks[src.first] = yul::AssemblyStack(m_evmVersion, _language, settings); - try - { - if (!stack.parseAndAnalyze(src.first, src.second)) - successful = false; - else - stack.optimize(); - } - catch (Exception const& _exception) - { - serr() << "Exception in assembler: " << boost::diagnostic_information(_exception) << endl; - return false; - } - catch (std::exception const& _e) - { - serr() << - "Unknown exception during compilation" << - (_e.what() ? ": " + string(_e.what()) : ".") << - endl; - return false; - } - catch (...) - { - serr() << "Unknown exception in assembler." << endl; - return false; - } - } - - for (auto const& sourceAndStack: assemblyStacks) - { - auto const& stack = sourceAndStack.second; - SourceReferenceFormatter formatter(serr(false), m_coloredOutput, m_withErrorIds); - - for (auto const& error: stack.errors()) - { - g_hasOutput = true; - formatter.printErrorInformation(*error); - } - if (!Error::containsOnlyWarnings(stack.errors())) - successful = false; - } - - if (!successful) - return false; - - for (auto const& src: m_fileReader.sourceCodes()) - { - string machine = - _targetMachine == yul::AssemblyStack::Machine::EVM ? "EVM" : - "Ewasm"; - sout() << endl << "======= " << src.first << " (" << machine << ") =======" << endl; - - yul::AssemblyStack& stack = assemblyStacks[src.first]; - - sout() << endl << "Pretty printed source:" << endl; - sout() << stack.print() << endl; - - if (_language != yul::AssemblyStack::Language::Ewasm && _targetMachine == yul::AssemblyStack::Machine::Ewasm) - { - try - { - stack.translate(yul::AssemblyStack::Language::Ewasm); - stack.optimize(); - } - catch (Exception const& _exception) - { - serr() << "Exception in assembler: " << boost::diagnostic_information(_exception) << endl; - return false; - } - catch (std::exception const& _e) - { - serr() << - "Unknown exception during compilation" << - (_e.what() ? ": " + string(_e.what()) : ".") << - endl; - return false; - } - catch (...) - { - serr() << "Unknown exception in assembler." << endl; - return false; - } - - sout() << endl << "==========================" << endl; - sout() << endl << "Translated source:" << endl; - sout() << stack.print() << endl; - } - - yul::MachineAssemblyObject object; - try - { - object = stack.assemble(_targetMachine); - object.bytecode->link(m_libraries); - } - catch (Exception const& _exception) - { - serr() << "Exception while assembling: " << boost::diagnostic_information(_exception) << endl; - return false; - } - catch (std::exception const& _e) - { - serr() << "Unknown exception during compilation" << ( - _e.what() ? ": " + string(_e.what()) : "." - ) << endl; - return false; - } - catch (...) - { - serr() << "Unknown exception while assembling." << endl; - return false; - } - - sout() << endl << "Binary representation:" << endl; - if (object.bytecode) - sout() << object.bytecode->toHex() << endl; - else - serr() << "No binary representation found." << endl; - - sout() << endl << "Text representation:" << endl; - if (!object.assembly.empty()) - sout() << object.assembly << endl; - else - serr() << "No text representation found." << endl; - } - - return true; -} - -void CommandLineInterface::outputCompilationResults() -{ - handleCombinedJSON(); - - // do we need AST output? - handleAst(); - - if ( - !m_compiler->compilationSuccessful() && - m_stopAfter == CompilerStack::State::CompilationSuccessful - ) - { - serr() << endl << "Compilation halted after AST generation due to errors." << endl; - return; - } - - vector contracts = m_compiler->contractNames(); - for (string const& contract: contracts) - { - if (needsHumanTargetedStdout(m_args)) - sout() << endl << "======= " << contract << " =======" << endl; - - // do we need EVM assembly? - if (m_args.count(g_argAsm) || m_args.count(g_argAsmJson)) - { - string ret; - if (m_args.count(g_argAsmJson)) - ret = jsonPrettyPrint(removeNullMembers(m_compiler->assemblyJSON(contract))); - else - ret = m_compiler->assemblyString(contract, m_fileReader.sourceCodes()); - - if (m_args.count(g_argOutputDir)) - { - createFile(m_compiler->filesystemFriendlyName(contract) + (m_args.count(g_argAsmJson) ? "_evm.json" : ".evm"), ret); - } - else - { - sout() << "EVM assembly:" << endl << ret << endl; - } - } - - if (m_args.count(g_argGas)) - handleGasEstimation(contract); - - handleBytecode(contract); - handleIR(contract); - handleIROptimized(contract); - handleEwasm(contract); - handleSignatureHashes(contract); - handleMetadata(contract); - handleABI(contract); - handleStorageLayout(contract); - handleNatspec(true, contract); - handleNatspec(false, contract); - } // end of contracts iteration - - if (!g_hasOutput) - { - if (m_args.count(g_argOutputDir)) - sout() << "Compiler run successful. Artifact(s) can be found in directory " << m_args.at(g_argOutputDir).as() << "." << endl; - else - serr() << "Compiler run successful, no output requested." << endl; - } -} - -size_t CommandLineInterface::countEnabledOptions(vector const& _optionNames) const +size_t CommandLineParser::countEnabledOptions(vector const& _optionNames) const { size_t count = 0; for (string const& _option: _optionNames) @@ -2095,12 +1146,11 @@ size_t CommandLineInterface::countEnabledOptions(vector const& _optionNa return count; } -string CommandLineInterface::joinOptionNames(vector const& _optionNames, string _separator) +string CommandLineParser::joinOptionNames(vector const& _optionNames, string _separator) { - return boost::algorithm::join( + return joinHumanReadable( _optionNames | ranges::views::transform([](string const& _option){ return "--" + _option; }), _separator ); } - -} +} // namespace solidity::frontend diff --git a/solc/CommandLineParser.h b/solc/CommandLineParser.h index 73c220e37..4aaf0d9b5 100644 --- a/solc/CommandLineParser.h +++ b/solc/CommandLineParser.h @@ -16,9 +16,7 @@ */ // SPDX-License-Identifier: GPL-3.0 /** - * @author Lefteris - * @date 2014 - * Solidity command line interface. + * Validates and parses command-line options into an internal representation. */ #pragma once @@ -32,111 +30,185 @@ #include #include +#include #include +#include +#include +#include +#include namespace solidity::frontend { -//forward declaration -enum class DocumentationType: uint8_t; +enum class InputMode +{ + Compiler, + CompilerWithASTImport, + StandardJson, + Linker, + Assembler, +}; -class CommandLineInterface +struct CompilerOutputs +{ + bool astCompactJson = false; + bool asm_ = false; + bool asmJson = false; + bool opcodes = false; + bool binary = false; + bool binaryRuntime = false; + bool abi = false; + bool ir = false; + bool irOptimized = false; + bool ewasm = false; + bool signatureHashes = false; + bool natspecUser = false; + bool natspecDev = false; + bool metadata = false; + bool storageLayout = false; +}; + +struct CombinedJsonRequests +{ + bool abi = false; + bool metadata = false; + bool binary = false; + bool binaryRuntime = false; + bool opcodes = false; + bool asm_ = false; + bool storageLayout = false; + bool generatedSources = false; + bool generatedSourcesRuntime = false; + bool srcMap = false; + bool srcMapRuntime = false; + bool funDebug = false; + bool funDebugRuntime = false; + bool signatureHashes = false; + bool natspecDev = false; + bool natspecUser = false; + bool ast = false; +}; + +struct CommandLineOptions +{ + struct + { + InputMode mode = InputMode::Compiler; + std::set paths; + std::string standardJsonFile; + std::vector remappings; + bool addStdin = false; + boost::filesystem::path basePath = ""; + FileReader::FileSystemPathSet allowedDirectories; + bool ignoreMissingFiles = false; + bool errorRecovery = false; + } input; + + struct + { + boost::filesystem::path dir; + bool overwriteFiles = false; + langutil::EVMVersion evmVersion; + bool experimentalViaIR = false; + RevertStrings revertStrings = RevertStrings::Default; + CompilerStack::State stopAfter = CompilerStack::State::CompilationSuccessful; + } output; + + struct + { + yul::AssemblyStack::Machine targetMachine = yul::AssemblyStack::Machine::EVM; + yul::AssemblyStack::Language inputLanguage = yul::AssemblyStack::Language::StrictAssembly; + } assembly; + + struct + { + std::map libraries; // library name -> address + } linker; + + struct + { + bool prettyJson = false; + std::optional coloredOutput; + bool withErrorIds = false; + } formatting; + + struct + { + CompilerOutputs outputs; + bool estimateGas = false; + std::optional combinedJsonRequests; + } compiler; + + struct + { + CompilerStack::MetadataHash hash = CompilerStack::MetadataHash::IPFS; + bool literalSources = false; + } metadata; + + struct + { + bool enabled = false; + unsigned expectedExecutionsPerDeployment = 0; + bool noOptimizeYul = false; + std::optional yulSteps; + } optimizer; + + struct + { + bool initialize = false; + ModelCheckerSettings settings; + } modelChecker; +}; + +/// Parses the command-line arguments and produces a filled-out CommandLineOptions structure. +/// Validates provided values and prints error messages in case of errors. +/// +/// The class is also responsible for handling options that only result in printing informational +/// text, without the need to invoke the compiler - printing usage banner, version or license. +class CommandLineParser { public: - /// Parse command line arguments and return false if we should not continue - bool parseArguments(int _argc, char** _argv); - /// Parse the files and create source code objects - bool processInput(); - /// Perform actions on the input depending on provided compiler arguments - /// @returns true on success. - bool actOnInput(); + /// Parses the command-line arguments and fills out the internal CommandLineOptions structure. + /// Performs validation and prints error messages. If requested, prints usage banner, version + /// or license. + /// @param interactiveTerminal specifies whether the terminal is taking input from the user. + /// This is used to determine whether to provide more user-friendly output in some situations. + /// E.g. whether to print help text when no arguments are provided. + /// @return true if there were no validation errors when parsing options and the + /// CommandLineOptions structure has been fully initialized. false if there were errors - in + /// this case CommandLineOptions may be only partially filled out. May also return false if + /// there is not further processing necessary and the program should just exit. + bool parse(int _argc, char const* const* _argv, bool interactiveTerminal); + + CommandLineOptions const& options() const { return m_options; } + + /// Returns true if any parser instance has written anything to cout or cerr since the last + /// call to parse(). + static bool hasOutput(); private: - bool link(); - void writeLinkedFiles(); - /// @returns the ``// -> name`` hint for library placeholders. - static std::string libraryPlaceholderHint(std::string const& _libraryName); - /// @returns the full object with library placeholder hints in hex. - static std::string objectWithLinkRefsHex(evmasm::LinkerObject const& _obj); + /// Parses the value supplied to --combined-json. + /// @return false if there are any validation errors, true otherwise. + bool parseCombinedJsonOption(); - bool assemble( - yul::AssemblyStack::Language _language, - yul::AssemblyStack::Machine _targetMachine, - bool _optimize, - std::optional _yulOptimiserSteps = std::nullopt - ); + /// Parses the names of the input files, remappings for all modes except for Standard JSON. + /// Does not check if files actually exist. + /// @return false if there are any validation errors, true otherwise. + bool parseInputPathsAndRemappings(); - void outputCompilationResults(); - - void handleCombinedJSON(); - void handleAst(); - void handleBinary(std::string const& _contract); - void handleOpcode(std::string const& _contract); - void handleIR(std::string const& _contract); - void handleIROptimized(std::string const& _contract); - void handleEwasm(std::string const& _contract); - void handleBytecode(std::string const& _contract); - void handleSignatureHashes(std::string const& _contract); - void handleMetadata(std::string const& _contract); - void handleABI(std::string const& _contract); - void handleNatspec(bool _natspecDev, std::string const& _contract); - void handleGasEstimation(std::string const& _contract); - void handleStorageLayout(std::string const& _contract); - - /// Fills @a m_sourceCodes initially and @a m_redirects. - bool readInputFilesAndConfigureRemappings(); - /// Tries to read from the file @a _input or interprets _input literally if that fails. - /// It then tries to parse the contents and appends to m_libraries. + /// Tries to read from the file @a _input or interprets @a _input literally if that fails. + /// It then tries to parse the contents and appends to m_options.libraries. + /// @return false if there are any validation errors, true otherwise. bool parseLibraryOption(std::string const& _input); - /// Tries to read @ m_sourceCodes as a JSONs holding ASTs - /// such that they can be imported into the compiler (importASTs()) - /// (produced by --combined-json ast,compact-format - /// or standard-json output - std::map parseAstFromInput(); - - /// Create a file in the given directory - /// @arg _fileName the name of the file - /// @arg _data to be written - void createFile(std::string const& _fileName, std::string const& _data); - - /// Create a json file in the given directory - /// @arg _fileName the name of the file (the extension will be replaced with .json) - /// @arg _json json string to be written - void createJson(std::string const& _fileName, std::string const& _json); - size_t countEnabledOptions(std::vector const& _optionNames) const; static std::string joinOptionNames(std::vector const& _optionNames, std::string _separator = ", "); - bool m_error = false; ///< If true, some error occurred. + CommandLineOptions m_options; - bool m_onlyAssemble = false; - - bool m_onlyLink = false; - - FileReader m_fileReader; - - /// Compiler arguments variable map + /// Map of command-line arguments produced by boost::program_options. + /// Serves as input for filling out m_options. boost::program_options::variables_map m_args; - /// list of remappings - std::vector m_remappings; - /// map of library names to addresses - std::map m_libraries; - /// Solidity compiler stack - std::unique_ptr m_compiler; - CompilerStack::State m_stopAfter = CompilerStack::State::CompilationSuccessful; - /// EVM version to use - langutil::EVMVersion m_evmVersion; - /// How to handle revert strings - RevertStrings m_revertStrings = RevertStrings::Default; - /// Chosen hash method for the bytecode metadata. - CompilerStack::MetadataHash m_metadataHash = CompilerStack::MetadataHash::IPFS; - /// Model checker settings. - ModelCheckerSettings m_modelCheckerSettings; - /// Whether or not to colorize diagnostics output. - bool m_coloredOutput = true; - /// Whether or not to output error IDs. - bool m_withErrorIds = false; }; } From 7d16c7b127b7dd49e445d4111aa604da17c7a121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 7 Jun 2021 18:57:35 +0200 Subject: [PATCH 07/98] Equality operators for ModelCheckerSettings and ImportRemapper --- libsolidity/formal/ModelCheckerSettings.h | 19 +++++++++++++++++++ libsolidity/interface/ImportRemapper.h | 9 +++++++++ 2 files changed, 28 insertions(+) diff --git a/libsolidity/formal/ModelCheckerSettings.h b/libsolidity/formal/ModelCheckerSettings.h index 6b4c121ae..cfcd31d2c 100644 --- a/libsolidity/formal/ModelCheckerSettings.h +++ b/libsolidity/formal/ModelCheckerSettings.h @@ -44,6 +44,9 @@ struct ModelCheckerContracts return has(_source) && contracts.at(_source).count(_contract); } + bool operator!=(ModelCheckerContracts const& _other) const noexcept { return !(*this == _other); } + bool operator==(ModelCheckerContracts const& _other) const noexcept { return contracts == _other.contracts; } + /// Represents which contracts should be analyzed by the SMTChecker /// as the most derived. /// The key is the source file. If the map is empty, all sources must be analyzed. @@ -79,6 +82,9 @@ struct ModelCheckerEngine return engineMap.at(_engine); return {}; } + + bool operator!=(ModelCheckerEngine const& _other) const noexcept { return !(*this == _other); } + bool operator==(ModelCheckerEngine const& _other) const noexcept { return bmc == _other.bmc && chc == _other.chc; } }; enum class VerificationTargetType { ConstantCondition, Underflow, Overflow, UnderOverflow, DivByZero, Balance, Assert, PopEmptyArray, OutOfBounds }; @@ -97,6 +103,9 @@ struct ModelCheckerTargets static std::map const targetStrings; + bool operator!=(ModelCheckerTargets const& _other) const noexcept { return !(*this == _other); } + bool operator==(ModelCheckerTargets const& _other) const noexcept { return targets == _other.targets; } + std::set targets; }; @@ -106,6 +115,16 @@ struct ModelCheckerSettings ModelCheckerEngine engine = ModelCheckerEngine::None(); ModelCheckerTargets targets = ModelCheckerTargets::Default(); std::optional timeout; + + bool operator!=(ModelCheckerSettings const& _other) const noexcept { return !(*this == _other); } + bool operator==(ModelCheckerSettings const& _other) const noexcept + { + return + contracts == _other.contracts && + engine == _other.engine && + targets == _other.targets && + timeout == _other.timeout; + } }; } diff --git a/libsolidity/interface/ImportRemapper.h b/libsolidity/interface/ImportRemapper.h index 15e2803dc..4451f0291 100644 --- a/libsolidity/interface/ImportRemapper.h +++ b/libsolidity/interface/ImportRemapper.h @@ -35,6 +35,15 @@ class ImportRemapper public: struct Remapping { + bool operator!=(Remapping const& _other) const noexcept { return !(*this == _other); } + bool operator==(Remapping const& _other) const noexcept + { + return + context == _other.context && + prefix == _other.prefix && + target == _other.target; + } + std::string context; std::string prefix; std::string target; From c17ee4fe65ce81b0ad1966656b7929ed87c3b6f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 10 Jun 2021 16:33:08 +0200 Subject: [PATCH 08/98] CommandLineParser: Equality operators for settings --- solc/CommandLineParser.cpp | 91 +++++++++++++++++++++++++++++++++++++- solc/CommandLineParser.h | 9 ++++ 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 67c74236a..263637907 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -270,6 +270,95 @@ bool checkMutuallyExclusive(boost::program_options::variables_map const& args, s } +bool CompilerOutputs::operator==(CompilerOutputs const& _other) const noexcept +{ + static_assert( + sizeof(*this) == 15 * sizeof(bool), + "Remember to update code below if you add/remove fields." + ); + + return + astCompactJson == _other.astCompactJson && + asm_ == _other.asm_ && + asmJson == _other.asmJson && + opcodes == _other.opcodes && + binary == _other.binary && + binaryRuntime == _other.binaryRuntime && + abi == _other.abi && + ir == _other.ir && + irOptimized == _other.irOptimized && + ewasm == _other.ewasm && + signatureHashes == _other.signatureHashes && + natspecUser == _other.natspecUser && + natspecDev == _other.natspecDev && + metadata == _other.metadata && + storageLayout == _other.storageLayout; +} + +bool CombinedJsonRequests::operator==(CombinedJsonRequests const& _other) const noexcept +{ + static_assert( + sizeof(*this) == 17 * sizeof(bool), + "Remember to update code below if you add/remove fields." + ); + + return + abi == _other.abi && + metadata == _other.metadata && + binary == _other.binary && + binaryRuntime == _other.binaryRuntime && + opcodes == _other.opcodes && + asm_ == _other.asm_ && + storageLayout == _other.storageLayout && + generatedSources == _other.generatedSources && + generatedSourcesRuntime == _other.generatedSourcesRuntime && + srcMap == _other.srcMap && + srcMapRuntime == _other.srcMapRuntime && + funDebug == _other.funDebug && + funDebugRuntime == _other.funDebugRuntime && + signatureHashes == _other.signatureHashes && + natspecDev == _other.natspecDev && + natspecUser == _other.natspecUser && + ast == _other.ast; +} + +bool CommandLineOptions::operator==(CommandLineOptions const& _other) const noexcept +{ + return + input.paths == _other.input.paths && + input.standardJsonFile == _other.input.standardJsonFile && + input.remappings == _other.input.remappings && + input.addStdin == _other.input.addStdin && + input.basePath == _other.input.basePath && + input.allowedDirectories == _other.input.allowedDirectories && + input.ignoreMissingFiles == _other.input.ignoreMissingFiles && + input.errorRecovery == _other.input.errorRecovery && + output.dir == _other.output.dir && + output.overwriteFiles == _other.output.overwriteFiles && + output.evmVersion == _other.output.evmVersion && + output.experimentalViaIR == _other.output.experimentalViaIR && + output.revertStrings == _other.output.revertStrings && + output.stopAfter == _other.output.stopAfter && + input.mode == _other.input.mode && + assembly.targetMachine == _other.assembly.targetMachine && + assembly.inputLanguage == _other.assembly.inputLanguage && + linker.libraries == _other.linker.libraries && + formatting.prettyJson == _other.formatting.prettyJson && + formatting.coloredOutput == _other.formatting.coloredOutput && + formatting.withErrorIds == _other.formatting.withErrorIds && + compiler.outputs == _other.compiler.outputs && + compiler.estimateGas == _other.compiler.estimateGas && + compiler.combinedJsonRequests == _other.compiler.combinedJsonRequests && + metadata.hash == _other.metadata.hash && + metadata.literalSources == _other.metadata.literalSources && + optimizer.enabled == _other.optimizer.enabled && + optimizer.expectedExecutionsPerDeployment == _other.optimizer.expectedExecutionsPerDeployment && + optimizer.noOptimizeYul == _other.optimizer.noOptimizeYul && + optimizer.yulSteps == _other.optimizer.yulSteps && + modelChecker.initialize == _other.modelChecker.initialize && + modelChecker.settings == _other.modelChecker.settings; +} + bool CommandLineParser::parseInputPathsAndRemappings() { m_options.input.ignoreMissingFiles = (m_args.count(g_argIgnoreMissingFiles) > 0); @@ -768,7 +857,7 @@ General Information)").c_str(), m_options.formatting.prettyJson = (m_args.count(g_argPrettyJson) > 0); static_assert( - sizeof(m_options.compiler.selectedOutputs) == 15 * sizeof(bool), + sizeof(m_options.compiler.outputs) == 15 * sizeof(bool), "Remember to update code below if you add/remove fields." ); m_options.compiler.outputs.astCompactJson = (m_args.count(g_argAstCompactJson) > 0); diff --git a/solc/CommandLineParser.h b/solc/CommandLineParser.h index 4aaf0d9b5..7d7223419 100644 --- a/solc/CommandLineParser.h +++ b/solc/CommandLineParser.h @@ -51,6 +51,9 @@ enum class InputMode struct CompilerOutputs { + bool operator!=(CompilerOutputs const& _other) const noexcept { return !(*this == _other); } + bool operator==(CompilerOutputs const& _other) const noexcept; + bool astCompactJson = false; bool asm_ = false; bool asmJson = false; @@ -70,6 +73,9 @@ struct CompilerOutputs struct CombinedJsonRequests { + bool operator!=(CombinedJsonRequests const& _other) const noexcept { return !(*this == _other); } + bool operator==(CombinedJsonRequests const& _other) const noexcept; + bool abi = false; bool metadata = false; bool binary = false; @@ -91,6 +97,9 @@ struct CombinedJsonRequests struct CommandLineOptions { + bool operator==(CommandLineOptions const& _other) const noexcept; + bool operator!=(CommandLineOptions const& _other) const noexcept { return !(*this == _other); } + struct { InputMode mode = InputMode::Compiler; From ef7abd224db22fc565ee3b835954603ad0888247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 7 Jun 2021 19:04:02 +0200 Subject: [PATCH 09/98] Tests for all input modes in CommandLineParser --- test/CMakeLists.txt | 8 +- test/solc/CommandLineParser.cpp | 399 ++++++++++++++++++++++++++++++++ 2 files changed, 406 insertions(+), 1 deletion(-) create mode 100644 test/solc/CommandLineParser.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2dc7a50dd..c82f1ee7b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -149,6 +149,11 @@ set(libyul_sources ) detect_stray_source_files("${libyul_sources}" "libyul/") +set(solcli_sources + solc/CommandLineParser.cpp +) +detect_stray_source_files("${solcli_sources}" "solc/") + set(yul_phaser_sources yulPhaser/TestHelpers.h yulPhaser/TestHelpers.cpp @@ -177,9 +182,10 @@ add_executable(soltest ${sources} ${libyul_sources} ${libsolidity_sources} ${libsolidity_util_sources} + ${solcli_sources} ${yul_phaser_sources} ) -target_link_libraries(soltest PRIVATE libsolc yul solidity smtutil solutil phaser Boost::boost yulInterpreter evmasm Boost::filesystem Boost::program_options Boost::unit_test_framework evmc) +target_link_libraries(soltest PRIVATE solcli libsolc yul solidity smtutil solutil phaser Boost::boost yulInterpreter evmasm Boost::filesystem Boost::program_options Boost::unit_test_framework evmc) # Special compilation flag for Visual Studio (version 2019 at least affected) diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp new file mode 100644 index 000000000..ece4710b0 --- /dev/null +++ b/test/solc/CommandLineParser.cpp @@ -0,0 +1,399 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +/// Unit tests for solc/CommandLineParser.h + +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace solidity::frontend; +using namespace solidity::langutil; +using namespace solidity::util; +using namespace solidity::yul; + +namespace +{ + +optional parseCommandLine(vector const& commandLine) +{ + size_t argc = commandLine.size(); + vector argv(argc + 1); + + // argv[argc] typically contains NULL + argv[argc] = nullptr; + + for (size_t i = 0; i < argc; ++i) + argv[i] = commandLine[i].c_str(); + + CommandLineParser cliParser; + bool success = cliParser.parse( + static_cast(argc), + argv.data(), + false // interactiveTerminal + ); + + if (!success) + return nullopt; + else + return cliParser.options(); +} + + +} // namespace + +namespace solidity::frontend::test +{ + +BOOST_AUTO_TEST_SUITE(CommandLineParserTest) + +BOOST_AUTO_TEST_CASE(no_options) +{ + vector commandLine = {"solc", "contract.sol"}; + + CommandLineOptions expectedOptions; + expectedOptions.input.paths = {"contract.sol"}; + expectedOptions.optimizer.expectedExecutionsPerDeployment = 200; + expectedOptions.modelChecker.initialize = true; + expectedOptions.modelChecker.settings = { + ModelCheckerContracts::Default(), + ModelCheckerEngine::None(), + ModelCheckerTargets::Default(), + nullopt, + }; + + optional parsedOptions = parseCommandLine(commandLine); + + BOOST_REQUIRE(parsedOptions.has_value()); + BOOST_TEST((parsedOptions.value() == expectedOptions)); +} + +BOOST_AUTO_TEST_CASE(cli_mode_options) +{ + for (InputMode inputMode: {InputMode::Compiler, InputMode::CompilerWithASTImport}) + { + vector commandLine = { + "solc", + "contract.sol", // Both modes do not care about file names, just about + "/tmp/projects/token.sol", // their content. They also both support stdin. + "/home/user/lib/dex.sol", + "file", + "input.json", + "-", + "/tmp=/usr/lib/", + "a:b=c/d", + ":contract.sol=", + "--base-path=/home/user/", + "--allow-paths=/tmp,/home,project,../contracts", + "--ignore-missing", + "--error-recovery", + "--output-dir=/tmp/out", + "--overwrite", + "--evm-version=spuriousDragon", + "--experimental-via-ir", + "--revert-strings=strip", + "--pretty-json", + "--no-color", + "--error-codes", + "--libraries=" + "dir1/file1.sol:L=0x1234567890123456789012345678901234567890," + "dir2/file2.sol:L=0x1111122222333334444455555666667777788888", + "--ast-compact-json", "--asm", "--asm-json", "--opcodes", "--bin", "--bin-runtime", "--abi", + "--ir", "--ir-optimized", "--ewasm", "--hashes", "--userdoc", "--devdoc", "--metadata", "--storage-layout", + "--gas", + "--combined-json=" + "abi,metadata,bin,bin-runtime,opcodes,asm,storage-layout,generated-sources,generated-sources-runtime," + "srcmap,srcmap-runtime,function-debug,function-debug-runtime,hashes,devdoc,userdoc,ast", + "--metadata-hash=swarm", + "--metadata-literal", + "--optimize", + "--optimize-runs=1000", + "--yul-optimizations=agf", + "--model-checker-contracts=contract1.yul:A,contract2.yul:B", + "--model-checker-engine=bmc", + "--model-checker-targets=underflow,divByZero", + "--model-checker-timeout=5", + }; + + if (inputMode == InputMode::CompilerWithASTImport) + commandLine += vector{ + "--import-ast", + }; + + CommandLineOptions expectedOptions; + expectedOptions.input.mode = inputMode; + expectedOptions.input.paths = {"contract.sol", "/tmp/projects/token.sol", "/home/user/lib/dex.sol", "file", "input.json"}; + expectedOptions.input.remappings = { + {"", "/tmp", "/usr/lib/"}, + {"a", "b", "c/d"}, + {"", "contract.sol", ""}, + }; + expectedOptions.input.addStdin = true; + expectedOptions.input.basePath = "/home/user/"; + expectedOptions.input.allowedDirectories = {"/tmp", "/home", "project", "../contracts", "", "c", "/usr/lib"}; + expectedOptions.input.ignoreMissingFiles = true; + expectedOptions.input.errorRecovery = (inputMode == InputMode::Compiler); + expectedOptions.output.dir = "/tmp/out"; + expectedOptions.output.overwriteFiles = true; + expectedOptions.output.evmVersion = EVMVersion::spuriousDragon(); + expectedOptions.output.experimentalViaIR = true; + expectedOptions.output.revertStrings = RevertStrings::Strip; + expectedOptions.linker.libraries = { + {"dir1/file1.sol:L", h160("1234567890123456789012345678901234567890")}, + {"dir2/file2.sol:L", h160("1111122222333334444455555666667777788888")}, + }; + expectedOptions.formatting.prettyJson = true; + expectedOptions.formatting.coloredOutput = false; + expectedOptions.formatting.withErrorIds = true; + expectedOptions.compiler.outputs = { + true, true, true, true, true, + true, true, true, true, true, + true, true, true, true, true, + }; + expectedOptions.compiler.estimateGas = true; + expectedOptions.compiler.combinedJsonRequests = { + true, true, true, true, true, + true, true, true, true, true, + true, true, true, true, true, + true, true, + }; + expectedOptions.metadata.hash = CompilerStack::MetadataHash::Bzzr1; + expectedOptions.metadata.literalSources = true; + expectedOptions.optimizer.enabled = true; + expectedOptions.optimizer.expectedExecutionsPerDeployment = 1000; + expectedOptions.optimizer.yulSteps = "agf"; + + expectedOptions.modelChecker.initialize = true; + expectedOptions.modelChecker.settings = { + {{{"contract1.yul", {"A"}}, {"contract2.yul", {"B"}}}}, + {true, false}, + {{VerificationTargetType::Underflow, VerificationTargetType::DivByZero}}, + 5, + }; + + optional parsedOptions = parseCommandLine(commandLine); + + BOOST_REQUIRE(parsedOptions.has_value()); + BOOST_TEST((parsedOptions.value() == expectedOptions)); + } +} + +BOOST_AUTO_TEST_CASE(assembly_mode_options) +{ + static vector, AssemblyStack::Machine, AssemblyStack::Language>> const allowedCombinations = { + {{"--machine=ewasm", "--yul-dialect=ewasm", "--assemble"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::Ewasm}, + {{"--machine=ewasm", "--yul-dialect=ewasm", "--yul"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::Ewasm}, + {{"--machine=ewasm", "--yul-dialect=ewasm", "--strict-assembly"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::Ewasm}, + {{"--machine=ewasm", "--yul-dialect=evm", "--assemble"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::StrictAssembly}, + {{"--machine=ewasm", "--yul-dialect=evm", "--yul"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::StrictAssembly}, + {{"--machine=ewasm", "--yul-dialect=evm", "--strict-assembly"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::StrictAssembly}, + {{"--machine=ewasm", "--strict-assembly"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::Ewasm}, + {{"--machine=evm", "--yul-dialect=evm", "--assemble"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::StrictAssembly}, + {{"--machine=evm", "--yul-dialect=evm", "--yul"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::StrictAssembly}, + {{"--machine=evm", "--yul-dialect=evm", "--strict-assembly"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::StrictAssembly}, + {{"--machine=evm", "--assemble"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::Assembly}, + {{"--machine=evm", "--yul"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::Yul}, + {{"--machine=evm", "--strict-assembly"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::StrictAssembly}, + {{"--assemble"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::Assembly}, + {{"--yul"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::Yul}, + {{"--strict-assembly"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::StrictAssembly}, + }; + + for (auto const& [assemblyOptions, expectedMachine, expectedLanguage]: allowedCombinations) + { + vector commandLine = { + "solc", + "contract.yul", + "/tmp/projects/token.yul", + "/home/user/lib/dex.yul", + "file", + "input.json", + "-", + "/tmp=/usr/lib/", + "a:b=c/d", + ":contract.yul=", + "--base-path=/home/user/", + "--allow-paths=/tmp,/home,project,../contracts", + "--ignore-missing", + "--error-recovery", // Ignored in assembly mode + "--overwrite", + "--evm-version=spuriousDragon", + "--experimental-via-ir", // Ignored in assembly mode + "--revert-strings=strip", // Accepted but has no effect in assembly mode + "--pretty-json", + "--no-color", + "--error-codes", + "--libraries=" + "dir1/file1.sol:L=0x1234567890123456789012345678901234567890," + "dir2/file2.sol:L=0x1111122222333334444455555666667777788888", + "--metadata-hash=swarm", // Ignored in assembly mode + "--metadata-literal", // Ignored in assembly mode + "--model-checker-contracts=" // Ignored in assembly mode + "contract1.yul:A," + "contract2.yul:B", + "--model-checker-engine=bmc", // Ignored in assembly mode + "--model-checker-targets=" // Ignored in assembly mode + "underflow," + "divByZero", + "--model-checker-timeout=5", // Ignored in assembly mode + + // Accepted but has no effect in assembly mode + "--ast-compact-json", "--asm", "--asm-json", "--opcodes", "--bin", "--bin-runtime", "--abi", + "--ir", "--ir-optimized", "--ewasm", "--hashes", "--userdoc", "--devdoc", "--metadata", "--storage-layout", + }; + commandLine += assemblyOptions; + if (expectedLanguage == AssemblyStack::Language::StrictAssembly || expectedLanguage == AssemblyStack::Language::Ewasm) + commandLine += vector{ + "--optimize", + "--optimize-runs=1000", // Ignored in assembly mode + "--yul-optimizations=agf", + }; + + CommandLineOptions expectedOptions; + expectedOptions.input.mode = InputMode::Assembler; + + expectedOptions.input.paths = {"contract.yul", "/tmp/projects/token.yul", "/home/user/lib/dex.yul", "file", "input.json"}; + expectedOptions.input.remappings = { + {"", "/tmp", "/usr/lib/"}, + {"a", "b", "c/d"}, + {"", "contract.yul", ""}, + }; + expectedOptions.input.addStdin = true; + expectedOptions.input.basePath = "/home/user/"; + expectedOptions.input.allowedDirectories = {"/tmp", "/home", "project", "../contracts", "", "c", "/usr/lib"}; + expectedOptions.input.ignoreMissingFiles = true; + expectedOptions.output.overwriteFiles = true; + expectedOptions.output.evmVersion = EVMVersion::spuriousDragon(); + expectedOptions.output.revertStrings = RevertStrings::Strip; + expectedOptions.assembly.targetMachine = expectedMachine; + expectedOptions.assembly.inputLanguage = expectedLanguage; + expectedOptions.linker.libraries = { + {"dir1/file1.sol:L", h160("1234567890123456789012345678901234567890")}, + {"dir2/file2.sol:L", h160("1111122222333334444455555666667777788888")}, + }; + expectedOptions.formatting.prettyJson = true; + expectedOptions.formatting.coloredOutput = false; + expectedOptions.formatting.withErrorIds = true; + expectedOptions.compiler.outputs = { + true, true, true, true, true, + true, true, true, true, true, + true, true, true, true, true, + }; + if (expectedLanguage == AssemblyStack::Language::StrictAssembly || expectedLanguage == AssemblyStack::Language::Ewasm) + { + expectedOptions.optimizer.enabled = true; + expectedOptions.optimizer.yulSteps = "agf"; + } + + optional parsedOptions = parseCommandLine(commandLine); + + BOOST_REQUIRE(parsedOptions.has_value()); + + BOOST_TEST((parsedOptions.value() == expectedOptions)); + } +} + +BOOST_AUTO_TEST_CASE(standard_json_mode_options) +{ + vector commandLine = { + "solc", + "input.json", + "--standard-json", + "--base-path=/home/user/", + "--allow-paths=/tmp,/home,project,../contracts", + "--ignore-missing", // Ignored in Standard JSON mode + "--error-recovery", // Ignored in Standard JSON mode + "--output-dir=/tmp/out", // Accepted but has no effect in Standard JSON mode + "--overwrite", // Accepted but has no effect in Standard JSON mode + "--evm-version=spuriousDragon", // Ignored in Standard JSON mode + "--experimental-via-ir", // Ignored in Standard JSON mode + "--revert-strings=strip", // Accepted but has no effect in Standard JSON mode + "--pretty-json", // Accepted but has no effect in Standard JSON mode + "--no-color", // Accepted but has no effect in Standard JSON mode + "--error-codes", // Accepted but has no effect in Standard JSON mode + "--libraries=" // Ignored in Standard JSON mode + "dir1/file1.sol:L=0x1234567890123456789012345678901234567890," + "dir2/file2.sol:L=0x1111122222333334444455555666667777788888", + "--gas", // Accepted but has no effect in Standard JSON mode + "--combined-json=abi,bin", // Accepted but has no effect in Standard JSON mode + "--metadata-hash=swarm", // Ignored in Standard JSON mode + "--metadata-literal", // Ignored in Standard JSON mode + "--optimize", // Ignored in Standard JSON mode + "--optimize-runs=1000", // Ignored in Standard JSON mode + "--yul-optimizations=agf", + "--model-checker-contracts=" // Ignored in Standard JSON mode + "contract1.yul:A," + "contract2.yul:B", + "--model-checker-engine=bmc", // Ignored in Standard JSON mode + "--model-checker-targets=" // Ignored in Standard JSON mode + "underflow," + "divByZero", + "--model-checker-timeout=5", // Ignored in Standard JSON mode + + // Accepted but has no effect in Standard JSON mode + "--ast-compact-json", "--asm", "--asm-json", "--opcodes", "--bin", "--bin-runtime", "--abi", + "--ir", "--ir-optimized", "--ewasm", "--hashes", "--userdoc", "--devdoc", "--metadata", "--storage-layout", + }; + + CommandLineOptions expectedOptions; + expectedOptions.input.mode = InputMode::StandardJson; + expectedOptions.input.paths = {}; + expectedOptions.input.standardJsonFile = "input.json"; + expectedOptions.input.basePath = "/home/user/"; + expectedOptions.input.allowedDirectories = {"/tmp", "/home", "project", "../contracts"}; + expectedOptions.output.dir = "/tmp/out"; + expectedOptions.output.overwriteFiles = true; + expectedOptions.output.revertStrings = RevertStrings::Strip; + expectedOptions.formatting.prettyJson = true; + expectedOptions.formatting.coloredOutput = false; + expectedOptions.formatting.withErrorIds = true; + expectedOptions.compiler.outputs = { + true, true, true, true, true, + true, true, true, true, true, + true, true, true, true, true, + }; + expectedOptions.compiler.estimateGas = true; + expectedOptions.compiler.combinedJsonRequests = CombinedJsonRequests{}; + expectedOptions.compiler.combinedJsonRequests->abi = true; + expectedOptions.compiler.combinedJsonRequests->binary = true; + + optional parsedOptions = parseCommandLine(commandLine); + + BOOST_REQUIRE(parsedOptions.has_value()); + BOOST_TEST((parsedOptions.value() == expectedOptions)); +} + +BOOST_AUTO_TEST_SUITE_END() + +} // namespace solidity::frontend::test From f0ec3dd6a2e8626426cc8c7143713c4a18fd880b Mon Sep 17 00:00:00 2001 From: Simon Tian <9939278+simontianx@users.noreply.github.com> Date: Tue, 6 Jul 2021 23:24:37 +0800 Subject: [PATCH 10/98] Update smtchecker.rst --- docs/smtchecker.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/smtchecker.rst b/docs/smtchecker.rst index 6fc0a1c57..dff8c8463 100644 --- a/docs/smtchecker.rst +++ b/docs/smtchecker.rst @@ -15,7 +15,7 @@ difference between what you did (the specification) and how you did it is what you wanted and that you did not miss any unintended effects of it. Solidity implements a formal verification approach based on -`SMT `_ and +`SMT (Satisfiability Modulo Theories) `_ and `Horn `_ solving. The SMTChecker module automatically tries to prove that the code satisfies the specification given by ``require`` and ``assert`` statements. That is, it considers From 6ba298c82beda040453a09bd23602f932a9e3857 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 7 Jul 2021 12:05:27 +0200 Subject: [PATCH 11/98] Include locations with empty source name. --- Changelog.md | 1 + libsolidity/interface/StandardCompiler.cpp | 2 +- test/cmdlineTests/standard_empty_file_name/output.json | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 45ef90635..d7307c981 100644 --- a/Changelog.md +++ b/Changelog.md @@ -14,6 +14,7 @@ Bugfixes: * Code Generator: Fix crash when passing an empty string literal to ``bytes.concat()``. * Code Generator: Fix internal compiler error when calling functions bound to calldata structs and arrays. * Code Generator: Fix internal compiler error when passing a 32-byte hex literal or a zero literal to ``bytes.concat()`` by disallowing such literals. + * Standard JSON: Include source location for errors in files with empty name. * Type Checker: Fix internal error and prevent static calls to unimplemented modifiers. * Yul Code Generator: Fix internal compiler error when using a long literal with bitwise negation. * Yul Code Generator: Fix source location references for calls to builtin functions. diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 1295c8411..a9c0cfd85 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -83,7 +83,7 @@ Json::Value formatFatalError(string const& _type, string const& _message) Json::Value formatSourceLocation(SourceLocation const* location) { Json::Value sourceLocation; - if (location && location->source && !location->source->name().empty()) + if (location && location->source) { sourceLocation["file"] = location->source->name(); sourceLocation["start"] = location->start; diff --git a/test/cmdlineTests/standard_empty_file_name/output.json b/test/cmdlineTests/standard_empty_file_name/output.json index 7a70b6aff..88cbe7667 100644 --- a/test/cmdlineTests/standard_empty_file_name/output.json +++ b/test/cmdlineTests/standard_empty_file_name/output.json @@ -1,3 +1,3 @@ {"errors":[{"component":"general","errorCode":"2904","formattedMessage":"DeclarationError: Declaration \"A\" not found in \"\" (referenced as \".\"). -","message":"Declaration \"A\" not found in \"\" (referenced as \".\").","severity":"error","type":"DeclarationError"}],"sources":{"":{"id":0}}} +","message":"Declaration \"A\" not found in \"\" (referenced as \".\").","severity":"error","sourceLocation":{"end":79,"file":"","start":59},"type":"DeclarationError"}],"sources":{"":{"id":0}}} From 6c33fbcb6af20d7aac0ca403d674cf5124ff9544 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 10 Jun 2021 17:45:09 +0200 Subject: [PATCH 12/98] CommandLineParser: Replace global sout/serr streams with class members - This removes the global variable and prevents stderr/stdout from being printed in tests --- solc/CommandLineInterface.cpp | 4 ++-- solc/CommandLineParser.cpp | 40 +++++++++------------------------ solc/CommandLineParser.h | 30 ++++++++++++++++++++++--- test/solc/CommandLineParser.cpp | 24 +++++++++++++++----- 4 files changed, 58 insertions(+), 40 deletions(-) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index cea5c7e1e..e365f9c9d 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -497,11 +497,11 @@ void CommandLineInterface::createJson(string const& _fileName, string const& _js bool CommandLineInterface::parseArguments(int _argc, char const* const* _argv) { - CommandLineParser parser; + CommandLineParser parser(sout(false), serr(false)); bool success = parser.parse(_argc, _argv, isatty(fileno(stdin))); if (!success) return false; - g_hasOutput = g_hasOutput || CommandLineParser::hasOutput(); + g_hasOutput = g_hasOutput || parser.hasOutput(); m_options = parser.options(); return true; diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 263637907..409648778 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -34,24 +34,16 @@ namespace po = boost::program_options; namespace solidity::frontend { -static bool g_hasOutput = false; - -namespace +ostream& CommandLineParser::sout() { - -std::ostream& sout() -{ - g_hasOutput = true; - return cout; + m_hasOutput = true; + return m_sout; } -std::ostream& serr(bool _used = true) +ostream& CommandLineParser::serr() { - if (_used) - g_hasOutput = true; - return cerr; -} - + m_hasOutput = true; + return m_serr; } #define cout @@ -233,8 +225,7 @@ static set const g_metadataHashArgs g_strNone }; -[[noreturn]] -static void printVersionAndExit() +void CommandLineParser::printVersionAndExit() { sout() << "solc, the solidity compiler commandline interface" << @@ -245,8 +236,7 @@ static void printVersionAndExit() exit(EXIT_SUCCESS); } -[[noreturn]] -static void printLicenseAndExit() +void CommandLineParser::printLicenseAndExit() { sout() << otherLicenses << endl; // This is a static variable generated by cmake from LICENSE.txt @@ -254,10 +244,8 @@ static void printLicenseAndExit() exit(EXIT_SUCCESS); } -namespace -{ -bool checkMutuallyExclusive(boost::program_options::variables_map const& args, std::string const& _optionA, std::string const& _optionB) +bool CommandLineParser::checkMutuallyExclusive(boost::program_options::variables_map const& args, string const& _optionA, string const& _optionB) { if (args.count(_optionA) && args.count(_optionB)) { @@ -268,8 +256,6 @@ bool checkMutuallyExclusive(boost::program_options::variables_map const& args, s return true; } -} - bool CompilerOutputs::operator==(CompilerOutputs const& _other) const noexcept { static_assert( @@ -489,7 +475,7 @@ bool CommandLineParser::parseLibraryOption(string const& _input) bool CommandLineParser::parse(int _argc, char const* const* _argv, bool interactiveTerminal) { - g_hasOutput = false; + m_hasOutput = false; // Declare the supported options. po::options_description desc((R"(solc, the Solidity commandline compiler. @@ -1186,11 +1172,6 @@ General Information)").c_str(), return true; } -bool CommandLineParser::hasOutput() -{ - return g_hasOutput; -} - bool CommandLineParser::parseCombinedJsonOption() { if (!m_args.count(g_argCombinedJson)) @@ -1242,4 +1223,5 @@ string CommandLineParser::joinOptionNames(vector const& _optionNames, st _separator ); } + } // namespace solidity::frontend diff --git a/solc/CommandLineParser.h b/solc/CommandLineParser.h index 7d7223419..b05caa705 100644 --- a/solc/CommandLineParser.h +++ b/solc/CommandLineParser.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -177,6 +178,11 @@ struct CommandLineOptions class CommandLineParser { public: + explicit CommandLineParser(std::ostream& _sout, std::ostream& _serr): + m_sout(_sout), + m_serr(_serr) + {} + /// Parses the command-line arguments and fills out the internal CommandLineOptions structure. /// Performs validation and prints error messages. If requested, prints usage banner, version /// or license. @@ -191,9 +197,8 @@ public: CommandLineOptions const& options() const { return m_options; } - /// Returns true if any parser instance has written anything to cout or cerr since the last - /// call to parse(). - static bool hasOutput(); + /// Returns true if the parser has written anything to any of its output streams. + bool hasOutput() const { return m_hasOutput; } private: /// Parses the value supplied to --combined-json. @@ -210,9 +215,28 @@ private: /// @return false if there are any validation errors, true otherwise. bool parseLibraryOption(std::string const& _input); + bool checkMutuallyExclusive( + boost::program_options::variables_map const& args, + std::string const& _optionA, + std::string const& _optionB + ); + [[noreturn]] void printVersionAndExit(); + [[noreturn]] void printLicenseAndExit(); size_t countEnabledOptions(std::vector const& _optionNames) const; static std::string joinOptionNames(std::vector const& _optionNames, std::string _separator = ", "); + /// Returns the stream that should receive normal output. Sets m_hasOutput to true if the + /// stream has ever been used. + std::ostream& sout(); + + /// Returns the stream that should receive error output. Sets m_hasOutput to true if the + /// stream has ever been used. + std::ostream& serr(); + + std::ostream& m_sout; + std::ostream& m_serr; + bool m_hasOutput = false; + CommandLineOptions m_options; /// Map of command-line arguments produced by boost::program_options. diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp index ece4710b0..a13fd0ccd 100644 --- a/test/solc/CommandLineParser.cpp +++ b/test/solc/CommandLineParser.cpp @@ -46,7 +46,7 @@ using namespace solidity::yul; namespace { -optional parseCommandLine(vector const& commandLine) +optional parseCommandLine(vector const& commandLine, ostream& _stdout, ostream& _stderr) { size_t argc = commandLine.size(); vector argv(argc + 1); @@ -57,7 +57,7 @@ optional parseCommandLine(vector const& commandLine) for (size_t i = 0; i < argc; ++i) argv[i] = commandLine[i].c_str(); - CommandLineParser cliParser; + CommandLineParser cliParser(_stdout, _stderr); bool success = cliParser.parse( static_cast(argc), argv.data(), @@ -93,8 +93,11 @@ BOOST_AUTO_TEST_CASE(no_options) nullopt, }; - optional parsedOptions = parseCommandLine(commandLine); + stringstream sout, serr; + optional parsedOptions = parseCommandLine(commandLine, sout, serr); + BOOST_TEST(sout.str() == ""); + BOOST_TEST(serr.str() == ""); BOOST_REQUIRE(parsedOptions.has_value()); BOOST_TEST((parsedOptions.value() == expectedOptions)); } @@ -202,8 +205,11 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) 5, }; - optional parsedOptions = parseCommandLine(commandLine); + stringstream sout, serr; + optional parsedOptions = parseCommandLine(commandLine, sout, serr); + BOOST_TEST(sout.str() == ""); + BOOST_TEST(serr.str() == ""); BOOST_REQUIRE(parsedOptions.has_value()); BOOST_TEST((parsedOptions.value() == expectedOptions)); } @@ -316,8 +322,11 @@ BOOST_AUTO_TEST_CASE(assembly_mode_options) expectedOptions.optimizer.yulSteps = "agf"; } - optional parsedOptions = parseCommandLine(commandLine); + stringstream sout, serr; + optional parsedOptions = parseCommandLine(commandLine, sout, serr); + BOOST_TEST(sout.str() == ""); + BOOST_TEST(serr.str() == "Warning: Yul is still experimental. Please use the output with care.\n"); BOOST_REQUIRE(parsedOptions.has_value()); BOOST_TEST((parsedOptions.value() == expectedOptions)); @@ -388,8 +397,11 @@ BOOST_AUTO_TEST_CASE(standard_json_mode_options) expectedOptions.compiler.combinedJsonRequests->abi = true; expectedOptions.compiler.combinedJsonRequests->binary = true; - optional parsedOptions = parseCommandLine(commandLine); + stringstream sout, serr; + optional parsedOptions = parseCommandLine(commandLine, sout, serr); + BOOST_TEST(sout.str() == ""); + BOOST_TEST(serr.str() == ""); BOOST_REQUIRE(parsedOptions.has_value()); BOOST_TEST((parsedOptions.value() == expectedOptions)); } From bb64d366ea0e6e648ce41f0965f819a49462207c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 10 Jun 2021 20:08:53 +0200 Subject: [PATCH 13/98] CommandLineInterface: Accept output streams as parameters --- solc/CommandLineInterface.cpp | 38 ++++++++++++++--------------------- solc/CommandLineInterface.h | 20 +++++++++++++++++- solc/main.cpp | 2 +- 3 files changed, 35 insertions(+), 25 deletions(-) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index e365f9c9d..466ca5bdc 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -70,7 +70,6 @@ #include #endif -#include #include #if !defined(STDERR_FILENO) @@ -86,25 +85,18 @@ using namespace solidity::langutil; namespace solidity::frontend { -namespace +ostream& CommandLineInterface::sout(bool _markAsUsed) { - -static bool g_hasOutput = false; - -std::ostream& sout(bool _used = true) -{ - if (_used) - g_hasOutput = true; - return cout; + if (_markAsUsed) + m_hasOutput = true; + return m_sout; } -std::ostream& serr(bool _used = true) +ostream& CommandLineInterface::serr(bool _markAsUsed) { - if (_used) - g_hasOutput = true; - return cerr; -} - + if (_markAsUsed) + m_hasOutput = true; + return m_serr; } #define cout @@ -497,11 +489,11 @@ void CommandLineInterface::createJson(string const& _fileName, string const& _js bool CommandLineInterface::parseArguments(int _argc, char const* const* _argv) { - CommandLineParser parser(sout(false), serr(false)); + CommandLineParser parser(sout(/* _markAsUsed */ false), serr(/* _markAsUsed */ false)); bool success = parser.parse(_argc, _argv, isatty(fileno(stdin))); if (!success) return false; - g_hasOutput = g_hasOutput || parser.hasOutput(); + m_hasOutput = m_hasOutput || parser.hasOutput(); m_options = parser.options(); return true; @@ -626,7 +618,7 @@ bool CommandLineInterface::compile() for (auto const& error: m_compiler->errors()) { - g_hasOutput = true; + m_hasOutput = true; formatter.printErrorInformation(*error); } @@ -635,7 +627,7 @@ bool CommandLineInterface::compile() } catch (CompilerError const& _exception) { - g_hasOutput = true; + m_hasOutput = true; formatter.printExceptionInformation(_exception, "Compiler error"); return false; } @@ -669,7 +661,7 @@ bool CommandLineInterface::compile() serr() << "Documentation parsing error: " << *boost::get_error_info(_error) << endl; else { - g_hasOutput = true; + m_hasOutput = true; formatter.printExceptionInformation(_error, _error.typeName()); } @@ -985,7 +977,7 @@ bool CommandLineInterface::assemble( for (auto const& error: stack.errors()) { - g_hasOutput = true; + m_hasOutput = true; formatter.printErrorInformation(*error); } if (!Error::containsOnlyWarnings(stack.errors())) @@ -1134,7 +1126,7 @@ void CommandLineInterface::outputCompilationResults() handleNatspec(false, contract); } // end of contracts iteration - if (!g_hasOutput) + if (!m_hasOutput) { if (!m_options.output.dir.empty()) sout() << "Compiler run successful. Artifact(s) can be found in directory " << m_options.output.dir << "." << endl; diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 26638af1e..2819ec603 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -38,7 +39,13 @@ namespace solidity::frontend class CommandLineInterface { public: - explicit CommandLineInterface(CommandLineOptions const& _options = CommandLineOptions{}): + explicit CommandLineInterface( + std::ostream& _sout, + std::ostream& _serr, + CommandLineOptions const& _options = CommandLineOptions{} + ): + m_sout(_sout), + m_serr(_serr), m_options(_options) {} @@ -106,6 +113,17 @@ private: /// @arg _json json string to be written void createJson(std::string const& _fileName, std::string const& _json); + /// Returns the stream that should receive normal output. Sets m_hasOutput to true if the + /// stream has ever been used unless @arg _markAsUsed is set to false. + std::ostream& sout(bool _markAsUsed = true); + + /// Returns the stream that should receive error output. Sets m_hasOutput to true if the + /// stream has ever been used unless @arg _markAsUsed is set to false. + std::ostream& serr(bool _markAsUsed = true); + + std::ostream& m_sout; + std::ostream& m_serr; + bool m_hasOutput = false; bool m_error = false; ///< If true, some error occurred. FileReader m_fileReader; std::unique_ptr m_compiler; diff --git a/solc/main.cpp b/solc/main.cpp index ae56066ed..3b844c8a9 100644 --- a/solc/main.cpp +++ b/solc/main.cpp @@ -54,7 +54,7 @@ static void setDefaultOrCLocale() int main(int argc, char** argv) { setDefaultOrCLocale(); - solidity::frontend::CommandLineInterface cli; + solidity::frontend::CommandLineInterface cli(cout, cerr); if (!cli.parseArguments(argc, argv)) return 1; if (!cli.processInput()) From 4c479b56891c437c9c6abde580c5c6eafa206293 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 16 Jun 2021 21:12:27 +0200 Subject: [PATCH 14/98] CommandLineInterface: Accept the standard input stream as a parameter --- solc/CommandLineInterface.cpp | 5 +++-- solc/CommandLineInterface.h | 3 +++ solc/main.cpp | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 466ca5bdc..a553e0491 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -99,6 +99,7 @@ ostream& CommandLineInterface::serr(bool _markAsUsed) return m_serr; } +#define cin #define cout #define cerr @@ -415,7 +416,7 @@ bool CommandLineInterface::readInputFilesAndConfigureFileReader() } if (m_options.input.addStdin) - m_fileReader.setSource(g_stdinFileName, readUntilEnd(cin)); + m_fileReader.setSource(g_stdinFileName, readUntilEnd(m_sin)); if (m_fileReader.sourceCodes().size() == 0) { @@ -513,7 +514,7 @@ bool CommandLineInterface::processInput() { string input; if (m_options.input.standardJsonFile.empty()) - input = readUntilEnd(cin); + input = readUntilEnd(m_sin); else { try diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 2819ec603..57674355a 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -40,10 +40,12 @@ class CommandLineInterface { public: explicit CommandLineInterface( + std::istream& _sin, std::ostream& _sout, std::ostream& _serr, CommandLineOptions const& _options = CommandLineOptions{} ): + m_sin(_sin), m_sout(_sout), m_serr(_serr), m_options(_options) @@ -121,6 +123,7 @@ private: /// stream has ever been used unless @arg _markAsUsed is set to false. std::ostream& serr(bool _markAsUsed = true); + std::istream& m_sin; std::ostream& m_sout; std::ostream& m_serr; bool m_hasOutput = false; diff --git a/solc/main.cpp b/solc/main.cpp index 3b844c8a9..4c40f47f1 100644 --- a/solc/main.cpp +++ b/solc/main.cpp @@ -54,7 +54,7 @@ static void setDefaultOrCLocale() int main(int argc, char** argv) { setDefaultOrCLocale(); - solidity::frontend::CommandLineInterface cli(cout, cerr); + solidity::frontend::CommandLineInterface cli(cin, cout, cerr); if (!cli.parseArguments(argc, argv)) return 1; if (!cli.processInput()) From baced5d10e8b48851443628d3e9150b2c0584cb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 16 Jun 2021 22:50:32 +0200 Subject: [PATCH 15/98] Test for the --help option --- test/solc/CommandLineParser.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp index a13fd0ccd..430399c5c 100644 --- a/test/solc/CommandLineParser.cpp +++ b/test/solc/CommandLineParser.cpp @@ -102,6 +102,17 @@ BOOST_AUTO_TEST_CASE(no_options) BOOST_TEST((parsedOptions.value() == expectedOptions)); } +BOOST_AUTO_TEST_CASE(help) +{ + stringstream sout, serr; + optional parsedOptions = parseCommandLine({"solc", "--help"}, sout, serr); + + BOOST_TEST(serr.str() == ""); + BOOST_TEST(boost::starts_with(sout.str(), "solc, the Solidity commandline compiler.")); + BOOST_TEST(sout.str().find("Usage: solc [options] [input_file...]") != string::npos); + BOOST_TEST(!parsedOptions.has_value()); +} + BOOST_AUTO_TEST_CASE(cli_mode_options) { for (InputMode inputMode: {InputMode::Compiler, InputMode::CompilerWithASTImport}) From 3bad7aff02a5e787c199b31e5bc325059b06f3d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 17 Jun 2021 16:20:32 +0200 Subject: [PATCH 16/98] CommandLineParser: Remove g_arg aliases --- solc/CommandLineParser.cpp | 336 ++++++++++++++++--------------------- 1 file changed, 143 insertions(+), 193 deletions(-) diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 263637907..dae4b597c 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -139,54 +139,6 @@ static string const g_strColor = "color"; static string const g_strNoColor = "no-color"; static string const g_strErrorIds = "error-codes"; -static string const g_argAbi = g_strAbi; -static string const g_argPrettyJson = g_strPrettyJson; -static string const g_argAllowPaths = g_strAllowPaths; -static string const g_argBasePath = g_strBasePath; -static string const g_argAsm = g_strAsm; -static string const g_argAsmJson = g_strAsmJson; -static string const g_argAssemble = g_strAssemble; -static string const g_argAstCompactJson = g_strAstCompactJson; -static string const g_argBinary = g_strBinary; -static string const g_argBinaryRuntime = g_strBinaryRuntime; -static string const g_argCombinedJson = g_strCombinedJson; -static string const g_argCompactJSON = g_strCompactJSON; -static string const g_argErrorRecovery = g_strErrorRecovery; -static string const g_argGas = g_strGas; -static string const g_argHelp = g_strHelp; -static string const g_argImportAst = g_strImportAst; -static string const g_argInputFile = g_strInputFile; -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; -static string const g_argMetadata = g_strMetadata; -static string const g_argMetadataHash = g_strMetadataHash; -static string const g_argMetadataLiteral = g_strMetadataLiteral; -static string const g_argModelCheckerContracts = g_strModelCheckerContracts; -static string const g_argModelCheckerEngine = g_strModelCheckerEngine; -static string const g_argModelCheckerTargets = g_strModelCheckerTargets; -static string const g_argModelCheckerTimeout = g_strModelCheckerTimeout; -static string const g_argNatspecDev = g_strNatspecDev; -static string const g_argNatspecUser = g_strNatspecUser; -static string const g_argOpcodes = g_strOpcodes; -static string const g_argOptimize = g_strOptimize; -static string const g_argOptimizeRuns = g_strOptimizeRuns; -static string const g_argOutputDir = g_strOutputDir; -static string const g_argSignatureHashes = g_strSignatureHashes; -static string const g_argStandardJSON = g_strStandardJSON; -static string const g_argStorageLayout = g_strStorageLayout; -static string const g_argStrictAssembly = g_strStrictAssembly; -static string const g_argVersion = g_strVersion; -static string const g_argIgnoreMissingFiles = g_strIgnoreMissingFiles; -static string const g_argColor = g_strColor; -static string const g_argNoColor = g_strNoColor; -static string const g_argErrorIds = g_strErrorIds; - /// Possible arguments to for --combined-json static set const g_combinedJsonArgs { @@ -361,9 +313,9 @@ bool CommandLineOptions::operator==(CommandLineOptions const& _other) const noex bool CommandLineParser::parseInputPathsAndRemappings() { - m_options.input.ignoreMissingFiles = (m_args.count(g_argIgnoreMissingFiles) > 0); - if (m_args.count(g_argInputFile)) - for (string path: m_args[g_argInputFile].as>()) + m_options.input.ignoreMissingFiles = (m_args.count(g_strIgnoreMissingFiles) > 0); + if (m_args.count(g_strInputFile)) + for (string path: m_args[g_strInputFile].as>()) { auto eq = find(path.begin(), path.end(), '='); if (eq != path.end()) @@ -505,36 +457,36 @@ at standard output or in files in the output directory, if specified. Imports are automatically read from the filesystem, but it is also possible to remap paths using the context:prefix=path syntax. Example: -solc --)" + g_argBinary + R"( -o /tmp/solcoutput dapp-bin=/usr/local/lib/dapp-bin contract.sol +solc --)" + g_strBinary + R"( -o /tmp/solcoutput dapp-bin=/usr/local/lib/dapp-bin contract.sol General Information)").c_str(), po::options_description::m_default_line_length, po::options_description::m_default_line_length - 23 ); desc.add_options() - (g_argHelp.c_str(), "Show help message and exit.") - (g_argVersion.c_str(), "Show version and exit.") + (g_strHelp.c_str(), "Show help message and exit.") + (g_strVersion.c_str(), "Show version and exit.") (g_strLicense.c_str(), "Show licensing information and exit.") ; po::options_description inputOptions("Input Options"); inputOptions.add_options() ( - g_argBasePath.c_str(), + g_strBasePath.c_str(), po::value()->value_name("path"), "Use the given path as the root of the source tree instead of the root of the filesystem." ) ( - g_argAllowPaths.c_str(), + g_strAllowPaths.c_str(), po::value()->value_name("path(s)"), "Allow a given path for imports. A list of paths can be supplied by separating them with a comma." ) ( - g_argIgnoreMissingFiles.c_str(), + g_strIgnoreMissingFiles.c_str(), "Ignore missing files." ) ( - g_argErrorRecovery.c_str(), + g_strErrorRecovery.c_str(), "Enables additional parser error recovery." ) ; @@ -543,7 +495,7 @@ General Information)").c_str(), po::options_description outputOptions("Output Options"); outputOptions.add_options() ( - (g_argOutputDir + ",o").c_str(), + (g_strOutputDir + ",o").c_str(), po::value()->value_name("path"), "If given, creates one file per component and contract/file at the specified directory." ) @@ -577,38 +529,38 @@ General Information)").c_str(), po::options_description alternativeInputModes("Alternative Input Modes"); alternativeInputModes.add_options() ( - g_argStandardJSON.c_str(), + g_strStandardJSON.c_str(), "Switch to Standard JSON input / output mode, ignoring all options. " "It reads from standard input, if no input file was given, otherwise it reads from the provided input file. The result will be written to standard output." ) ( - g_argLink.c_str(), - ("Switch to linker mode, ignoring all options apart from --" + g_argLibraries + " " + g_strLink.c_str(), + ("Switch to linker mode, ignoring all options apart from --" + g_strLibraries + " " "and modify binaries in place.").c_str() ) ( - g_argAssemble.c_str(), + g_strAssemble.c_str(), ("Switch to assembly mode, ignoring all options except " - "--" + g_argMachine + ", --" + g_strYulDialect + ", --" + g_argOptimize + " and --" + g_strYulOptimizations + " " + "--" + g_strMachine + ", --" + g_strYulDialect + ", --" + g_strOptimize + " and --" + g_strYulOptimizations + " " "and assumes input is assembly.").c_str() ) ( - g_argYul.c_str(), + g_strYul.c_str(), ("Switch to Yul mode, ignoring all options except " - "--" + g_argMachine + ", --" + g_strYulDialect + ", --" + g_argOptimize + " and --" + g_strYulOptimizations + " " + "--" + g_strMachine + ", --" + g_strYulDialect + ", --" + g_strOptimize + " and --" + g_strYulOptimizations + " " "and assumes input is Yul.").c_str() ) ( - g_argStrictAssembly.c_str(), + g_strStrictAssembly.c_str(), ("Switch to strict assembly mode, ignoring all options except " - "--" + g_argMachine + ", --" + g_strYulDialect + ", --" + g_argOptimize + " and --" + g_strYulOptimizations + " " + "--" + g_strMachine + ", --" + g_strYulDialect + ", --" + g_strOptimize + " and --" + g_strYulOptimizations + " " "and assumes input is strict assembly.").c_str() ) ( - g_argImportAst.c_str(), + g_strImportAst.c_str(), ("Import ASTs to be compiled, assumes input holds the AST in compact JSON format. " - "Supported Inputs is the output of the --" + g_argStandardJSON + " or the one produced by " - "--" + g_argCombinedJson + " " + g_strAst + "," + g_strCompactJSON).c_str() + "Supported Inputs is the output of the --" + g_strStandardJSON + " or the one produced by " + "--" + g_strCombinedJson + " " + g_strAst + "," + g_strCompactJSON).c_str() ) ; desc.add(alternativeInputModes); @@ -616,7 +568,7 @@ General Information)").c_str(), po::options_description assemblyModeOptions("Assembly Mode Options"); assemblyModeOptions.add_options() ( - g_argMachine.c_str(), + g_strMachine.c_str(), po::value()->value_name(boost::join(g_machineArgs, ",")), "Target machine in assembly or Yul mode." ) @@ -631,7 +583,7 @@ General Information)").c_str(), po::options_description linkerModeOptions("Linker Mode Options"); linkerModeOptions.add_options() ( - g_argLibraries.c_str(), + g_strLibraries.c_str(), po::value>()->value_name("libs"), "Direct string or file containing library addresses. Syntax: " "=
[, or whitespace] ...\n" @@ -643,19 +595,19 @@ General Information)").c_str(), po::options_description outputFormatting("Output Formatting"); outputFormatting.add_options() ( - g_argPrettyJson.c_str(), + g_strPrettyJson.c_str(), "Output JSON in pretty format. Currently it only works with the combined JSON output." ) ( - g_argColor.c_str(), + g_strColor.c_str(), "Force colored output." ) ( - g_argNoColor.c_str(), + g_strNoColor.c_str(), "Explicitly disable colored output, disabling terminal auto-detection." ) ( - g_argErrorIds.c_str(), + g_strErrorIds.c_str(), "Output error codes." ) ; @@ -663,32 +615,32 @@ General Information)").c_str(), po::options_description outputComponents("Output Components"); outputComponents.add_options() - (g_argAstCompactJson.c_str(), "AST of all source files in a compact JSON format.") - (g_argAsm.c_str(), "EVM assembly of the contracts.") - (g_argAsmJson.c_str(), "EVM assembly of the contracts in JSON format.") - (g_argOpcodes.c_str(), "Opcodes of the contracts.") - (g_argBinary.c_str(), "Binary of the contracts in hex.") - (g_argBinaryRuntime.c_str(), "Binary of the runtime part of the contracts in hex.") - (g_argAbi.c_str(), "ABI specification of the contracts.") - (g_argIR.c_str(), "Intermediate Representation (IR) of all contracts (EXPERIMENTAL).") - (g_argIROptimized.c_str(), "Optimized intermediate Representation (IR) of all contracts (EXPERIMENTAL).") - (g_argEwasm.c_str(), "Ewasm text representation of all contracts (EXPERIMENTAL).") - (g_argSignatureHashes.c_str(), "Function signature hashes of the contracts.") - (g_argNatspecUser.c_str(), "Natspec user documentation of all contracts.") - (g_argNatspecDev.c_str(), "Natspec developer documentation of all contracts.") - (g_argMetadata.c_str(), "Combined Metadata JSON whose Swarm hash is stored on-chain.") - (g_argStorageLayout.c_str(), "Slots, offsets and types of the contract's state variables.") + (g_strAstCompactJson.c_str(), "AST of all source files in a compact JSON format.") + (g_strAsm.c_str(), "EVM assembly of the contracts.") + (g_strAsmJson.c_str(), "EVM assembly of the contracts in JSON format.") + (g_strOpcodes.c_str(), "Opcodes of the contracts.") + (g_strBinary.c_str(), "Binary of the contracts in hex.") + (g_strBinaryRuntime.c_str(), "Binary of the runtime part of the contracts in hex.") + (g_strAbi.c_str(), "ABI specification of the contracts.") + (g_strIR.c_str(), "Intermediate Representation (IR) of all contracts (EXPERIMENTAL).") + (g_strIROptimized.c_str(), "Optimized intermediate Representation (IR) of all contracts (EXPERIMENTAL).") + (g_strEwasm.c_str(), "Ewasm text representation of all contracts (EXPERIMENTAL).") + (g_strSignatureHashes.c_str(), "Function signature hashes of the contracts.") + (g_strNatspecUser.c_str(), "Natspec user documentation of all contracts.") + (g_strNatspecDev.c_str(), "Natspec developer documentation of all contracts.") + (g_strMetadata.c_str(), "Combined Metadata JSON whose Swarm hash is stored on-chain.") + (g_strStorageLayout.c_str(), "Slots, offsets and types of the contract's state variables.") ; desc.add(outputComponents); po::options_description extraOutput("Extra Output"); extraOutput.add_options() ( - g_argGas.c_str(), + g_strGas.c_str(), "Print an estimate of the maximal gas usage for each function." ) ( - g_argCombinedJson.c_str(), + g_strCombinedJson.c_str(), po::value()->value_name(boost::join(g_combinedJsonArgs, ",")), "Output a single json document containing the specified information." ) @@ -698,12 +650,12 @@ General Information)").c_str(), po::options_description metadataOptions("Metadata Options"); metadataOptions.add_options() ( - g_argMetadataHash.c_str(), + g_strMetadataHash.c_str(), po::value()->value_name(boost::join(g_metadataHashArgs, ",")), "Choose hash method for the bytecode metadata or disable it." ) ( - g_argMetadataLiteral.c_str(), + g_strMetadataLiteral.c_str(), "Store referenced sources as literal data in the metadata output." ) ; @@ -712,11 +664,11 @@ General Information)").c_str(), po::options_description optimizerOptions("Optimizer Options"); optimizerOptions.add_options() ( - g_argOptimize.c_str(), + g_strOptimize.c_str(), "Enable bytecode optimizer." ) ( - g_argOptimizeRuns.c_str(), + g_strOptimizeRuns.c_str(), // TODO: The type in OptimiserSettings is size_t but we only accept values up to 2**32-1 // on the CLI and in Standard JSON. We should just switch to uint32_t everywhere. po::value()->value_name("n")->default_value(static_cast(OptimiserSettings{}.expectedExecutionsPerDeployment)), @@ -725,7 +677,7 @@ General Information)").c_str(), ) ( g_strOptimizeYul.c_str(), - ("Legacy option, ignored. Use the general --" + g_argOptimize + " to enable Yul optimizer.").c_str() + ("Legacy option, ignored. Use the general --" + g_strOptimize + " to enable Yul optimizer.").c_str() ) ( g_strNoOptimizeYul.c_str(), @@ -771,11 +723,11 @@ General Information)").c_str(), desc.add(smtCheckerOptions); po::options_description allOptions = desc; - allOptions.add_options()(g_argInputFile.c_str(), po::value>(), "input file"); + allOptions.add_options()(g_strInputFile.c_str(), po::value>(), "input file"); // All positional options should be interpreted as input files po::positional_options_description filesPositions; - filesPositions.add(g_argInputFile.c_str(), -1); + filesPositions.add(g_strInputFile.c_str(), -1); // parse the compiler arguments try @@ -791,40 +743,38 @@ General Information)").c_str(), return false; } - if (!checkMutuallyExclusive(m_args, g_argColor, g_argNoColor)) + if (!checkMutuallyExclusive(m_args, g_strColor, g_strNoColor)) return false; array const conflictingWithStopAfter{ - g_argBinary, - g_argIR, - g_argIROptimized, - g_argEwasm, - g_argGas, - g_argAsm, - g_argAsmJson, - g_argOpcodes + g_strBinary, + g_strIR, + g_strIROptimized, + g_strEwasm, + g_strGas, + g_strAsm, + g_strAsmJson, + g_strOpcodes }; for (auto& option: conflictingWithStopAfter) if (!checkMutuallyExclusive(m_args, g_strStopAfter, option)) return false; - if (m_args.count(g_argColor) > 0) + if (m_args.count(g_strColor) > 0) m_options.formatting.coloredOutput = true; - else if (m_args.count(g_argNoColor) > 0) + else if (m_args.count(g_strNoColor) > 0) m_options.formatting.coloredOutput = false; - else - solAssert(!m_options.formatting.coloredOutput.has_value(), ""); - m_options.formatting.withErrorIds = m_args.count(g_argErrorIds); + m_options.formatting.withErrorIds = m_args.count(g_strErrorIds); - if (m_args.count(g_argHelp) || (interactiveTerminal && _argc == 1)) + if (m_args.count(g_strHelp) || (interactiveTerminal && _argc == 1)) { sout() << desc; return false; } - if (m_args.count(g_argVersion)) + if (m_args.count(g_strVersion)) printVersionAndExit(); if (m_args.count(g_strLicense)) @@ -850,43 +800,43 @@ General Information)").c_str(), if (!parseCombinedJsonOption()) return false; - if (m_args.count(g_argOutputDir)) - m_options.output.dir = m_args.at(g_argOutputDir).as(); + if (m_args.count(g_strOutputDir)) + m_options.output.dir = m_args.at(g_strOutputDir).as(); m_options.output.overwriteFiles = (m_args.count(g_strOverwrite) > 0); - m_options.formatting.prettyJson = (m_args.count(g_argPrettyJson) > 0); + m_options.formatting.prettyJson = (m_args.count(g_strPrettyJson) > 0); static_assert( sizeof(m_options.compiler.outputs) == 15 * sizeof(bool), "Remember to update code below if you add/remove fields." ); - m_options.compiler.outputs.astCompactJson = (m_args.count(g_argAstCompactJson) > 0); - m_options.compiler.outputs.asm_ = (m_args.count(g_argAsm) > 0); - m_options.compiler.outputs.asmJson = (m_args.count(g_argAsmJson) > 0); - m_options.compiler.outputs.opcodes = (m_args.count(g_argOpcodes) > 0); - m_options.compiler.outputs.binary = (m_args.count(g_argBinary) > 0); - m_options.compiler.outputs.binaryRuntime = (m_args.count(g_argBinaryRuntime) > 0); - m_options.compiler.outputs.abi = (m_args.count(g_argAbi) > 0); - m_options.compiler.outputs.ir = (m_args.count(g_argIR) > 0); - m_options.compiler.outputs.irOptimized = (m_args.count(g_argIROptimized) > 0); - m_options.compiler.outputs.ewasm = (m_args.count(g_argEwasm) > 0); - m_options.compiler.outputs.signatureHashes = (m_args.count(g_argSignatureHashes) > 0); - m_options.compiler.outputs.natspecUser = (m_args.count(g_argNatspecUser) > 0); - m_options.compiler.outputs.natspecDev = (m_args.count(g_argNatspecDev) > 0); - m_options.compiler.outputs.metadata = (m_args.count(g_argMetadata) > 0); - m_options.compiler.outputs.storageLayout = (m_args.count(g_argStorageLayout) > 0); + m_options.compiler.outputs.astCompactJson = (m_args.count(g_strAstCompactJson) > 0); + m_options.compiler.outputs.asm_ = (m_args.count(g_strAsm) > 0); + m_options.compiler.outputs.asmJson = (m_args.count(g_strAsmJson) > 0); + m_options.compiler.outputs.opcodes = (m_args.count(g_strOpcodes) > 0); + m_options.compiler.outputs.binary = (m_args.count(g_strBinary) > 0); + m_options.compiler.outputs.binaryRuntime = (m_args.count(g_strBinaryRuntime) > 0); + m_options.compiler.outputs.abi = (m_args.count(g_strAbi) > 0); + m_options.compiler.outputs.ir = (m_args.count(g_strIR) > 0); + m_options.compiler.outputs.irOptimized = (m_args.count(g_strIROptimized) > 0); + m_options.compiler.outputs.ewasm = (m_args.count(g_strEwasm) > 0); + m_options.compiler.outputs.signatureHashes = (m_args.count(g_strSignatureHashes) > 0); + m_options.compiler.outputs.natspecUser = (m_args.count(g_strNatspecUser) > 0); + m_options.compiler.outputs.natspecDev = (m_args.count(g_strNatspecDev) > 0); + m_options.compiler.outputs.metadata = (m_args.count(g_strMetadata) > 0); + m_options.compiler.outputs.storageLayout = (m_args.count(g_strStorageLayout) > 0); - m_options.compiler.estimateGas = (m_args.count(g_argGas) > 0); + m_options.compiler.estimateGas = (m_args.count(g_strGas) > 0); po::notify(m_args); - if (m_args.count(g_argBasePath)) - m_options.input.basePath = m_args[g_argBasePath].as(); + if (m_args.count(g_strBasePath)) + m_options.input.basePath = m_args[g_strBasePath].as(); - if (m_args.count(g_argAllowPaths)) + if (m_args.count(g_strAllowPaths)) { vector paths; - for (string const& path: boost::split(paths, m_args[g_argAllowPaths].as(), boost::is_any_of(","))) + for (string const& path: boost::split(paths, m_args[g_strAllowPaths].as(), boost::is_any_of(","))) { auto filesystem_path = boost::filesystem::path(path); // If the given path had a trailing slash, the Boost filesystem @@ -911,12 +861,12 @@ General Information)").c_str(), } vector const exclusiveModes = { - g_argStandardJSON, - g_argLink, - g_argAssemble, - g_argStrictAssembly, - g_argYul, - g_argImportAst, + g_strStandardJSON, + g_strLink, + g_strAssemble, + g_strStrictAssembly, + g_strYul, + g_strImportAst, }; if (countEnabledOptions(exclusiveModes) > 1) { @@ -925,18 +875,18 @@ General Information)").c_str(), return false; } - if (m_args.count(g_argStandardJSON)) + if (m_args.count(g_strStandardJSON)) { m_options.input.mode = InputMode::StandardJson; vector inputFiles; - if (m_args.count(g_argInputFile)) - inputFiles = m_args[g_argInputFile].as>(); + if (m_args.count(g_strInputFile)) + inputFiles = m_args[g_strInputFile].as>(); if (inputFiles.size() == 1) m_options.input.standardJsonFile = inputFiles[0]; else if (inputFiles.size() > 1) { - serr() << "If --" << g_argStandardJSON << " is used, only zero or one input files are supported." << endl; + serr() << "If --" << g_strStandardJSON << " is used, only zero or one input files are supported." << endl; return false; } @@ -946,8 +896,8 @@ General Information)").c_str(), if (!parseInputPathsAndRemappings()) return false; - if (m_args.count(g_argLibraries)) - for (string const& library: m_args[g_argLibraries].as>()) + if (m_args.count(g_strLibraries)) + for (string const& library: m_args[g_strLibraries].as>()) if (!parseLibraryOption(library)) return false; @@ -963,15 +913,15 @@ General Information)").c_str(), m_options.output.evmVersion = *versionOption; } - if (m_args.count(g_argAssemble) || m_args.count(g_argStrictAssembly) || m_args.count(g_argYul)) + if (m_args.count(g_strAssemble) || m_args.count(g_strStrictAssembly) || m_args.count(g_strYul)) { m_options.input.mode = InputMode::Assembler; vector const nonAssemblyModeOptions = { // TODO: The list is not complete. Add more. - g_argOutputDir, - g_argGas, - g_argCombinedJson, + g_strOutputDir, + g_strGas, + g_strCombinedJson, g_strOptimizeYul, g_strNoOptimizeYul, }; @@ -983,7 +933,7 @@ General Information)").c_str(), serr() << "The following options are invalid in assembly mode: "; serr() << joinOptionNames(enabledOptions) << "."; if (m_args.count(g_strOptimizeYul) || m_args.count(g_strNoOptimizeYul)) - serr() << " Optimization is disabled by default and can be enabled with --" << g_argOptimize << "." << endl; + serr() << " Optimization is disabled by default and can be enabled with --" << g_strOptimize << "." << endl; serr() << endl; return false; } @@ -991,8 +941,8 @@ General Information)").c_str(), // switch to assembly mode using Input = yul::AssemblyStack::Language; using Machine = yul::AssemblyStack::Machine; - m_options.assembly.inputLanguage = m_args.count(g_argYul) ? Input::Yul : (m_args.count(g_argStrictAssembly) ? Input::StrictAssembly : Input::Assembly); - m_options.optimizer.enabled = (m_args.count(g_argOptimize) > 0); + m_options.assembly.inputLanguage = m_args.count(g_strYul) ? Input::Yul : (m_args.count(g_strStrictAssembly) ? Input::StrictAssembly : Input::Assembly); + m_options.optimizer.enabled = (m_args.count(g_strOptimize) > 0); m_options.optimizer.noOptimizeYul = (m_args.count(g_strNoOptimizeYul) > 0); if (m_args.count(g_strYulOptimizations)) @@ -1016,16 +966,16 @@ General Information)").c_str(), m_options.optimizer.yulSteps = m_args[g_strYulOptimizations].as(); } - if (m_args.count(g_argMachine)) + if (m_args.count(g_strMachine)) { - string machine = m_args[g_argMachine].as(); + string machine = m_args[g_strMachine].as(); if (machine == g_strEVM) m_options.assembly.targetMachine = Machine::EVM; else if (machine == g_strEwasm) m_options.assembly.targetMachine = Machine::Ewasm; else { - serr() << "Invalid option for --" << g_argMachine << ": " << machine << endl; + serr() << "Invalid option for --" << g_strMachine << ": " << machine << endl; return false; } } @@ -1042,7 +992,7 @@ General Information)").c_str(), if (m_options.assembly.targetMachine != Machine::Ewasm) { serr() << "If you select Ewasm as --" << g_strYulDialect << ", "; - serr() << "--" << g_argMachine << " has to be Ewasm as well." << endl; + serr() << "--" << g_strMachine << " has to be Ewasm as well." << endl; return false; } } @@ -1073,22 +1023,22 @@ General Information)").c_str(), return true; } - else if (countEnabledOptions({g_strYulDialect, g_argMachine}) >= 1) + else if (countEnabledOptions({g_strYulDialect, g_strMachine}) >= 1) { - serr() << "--" << g_strYulDialect << " and --" << g_argMachine << " "; + serr() << "--" << g_strYulDialect << " and --" << g_strMachine << " "; serr() << "are only valid in assembly mode." << endl; return false; } - if (m_args.count(g_argLink)) + if (m_args.count(g_strLink)) { m_options.input.mode = InputMode::Linker; return true; } - if (m_args.count(g_argMetadataHash)) + if (m_args.count(g_strMetadataHash)) { - string hashStr = m_args[g_argMetadataHash].as(); + string hashStr = m_args[g_strMetadataHash].as(); if (hashStr == g_strIPFS) m_options.metadata.hash = CompilerStack::MetadataHash::IPFS; else if (hashStr == g_strSwarm) @@ -1097,60 +1047,60 @@ General Information)").c_str(), m_options.metadata.hash = CompilerStack::MetadataHash::None; else { - serr() << "Invalid option for --" << g_argMetadataHash << ": " << hashStr << endl; + serr() << "Invalid option for --" << g_strMetadataHash << ": " << hashStr << endl; return false; } } - if (m_args.count(g_argModelCheckerContracts)) + if (m_args.count(g_strModelCheckerContracts)) { - string contractsStr = m_args[g_argModelCheckerContracts].as(); + string contractsStr = m_args[g_strModelCheckerContracts].as(); optional contracts = ModelCheckerContracts::fromString(contractsStr); if (!contracts) { - serr() << "Invalid option for --" << g_argModelCheckerContracts << ": " << contractsStr << endl; + serr() << "Invalid option for --" << g_strModelCheckerContracts << ": " << contractsStr << endl; return false; } m_options.modelChecker.settings.contracts = move(*contracts); } - if (m_args.count(g_argModelCheckerEngine)) + if (m_args.count(g_strModelCheckerEngine)) { - string engineStr = m_args[g_argModelCheckerEngine].as(); + string engineStr = m_args[g_strModelCheckerEngine].as(); optional engine = ModelCheckerEngine::fromString(engineStr); if (!engine) { - serr() << "Invalid option for --" << g_argModelCheckerEngine << ": " << engineStr << endl; + serr() << "Invalid option for --" << g_strModelCheckerEngine << ": " << engineStr << endl; return false; } m_options.modelChecker.settings.engine = *engine; } - if (m_args.count(g_argModelCheckerTargets)) + if (m_args.count(g_strModelCheckerTargets)) { - string targetsStr = m_args[g_argModelCheckerTargets].as(); + string targetsStr = m_args[g_strModelCheckerTargets].as(); optional targets = ModelCheckerTargets::fromString(targetsStr); if (!targets) { - serr() << "Invalid option for --" << g_argModelCheckerTargets << ": " << targetsStr << endl; + serr() << "Invalid option for --" << g_strModelCheckerTargets << ": " << targetsStr << endl; return false; } m_options.modelChecker.settings.targets = *targets; } - if (m_args.count(g_argModelCheckerTimeout)) - m_options.modelChecker.settings.timeout = m_args[g_argModelCheckerTimeout].as(); + if (m_args.count(g_strModelCheckerTimeout)) + m_options.modelChecker.settings.timeout = m_args[g_strModelCheckerTimeout].as(); - m_options.metadata.literalSources = (m_args.count(g_argMetadataLiteral) > 0); + m_options.metadata.literalSources = (m_args.count(g_strMetadataLiteral) > 0); m_options.modelChecker.initialize = - m_args.count(g_argModelCheckerContracts) || - m_args.count(g_argModelCheckerEngine) || - m_args.count(g_argModelCheckerTargets) || - m_args.count(g_argModelCheckerTimeout); - m_options.output.experimentalViaIR = (m_args.count(g_argExperimentalViaIR) > 0); - m_options.optimizer.expectedExecutionsPerDeployment = m_args[g_argOptimizeRuns].as(); + m_args.count(g_strModelCheckerContracts) || + m_args.count(g_strModelCheckerEngine) || + m_args.count(g_strModelCheckerTargets) || + m_args.count(g_strModelCheckerTimeout); + m_options.output.experimentalViaIR = (m_args.count(g_strExperimentalViaIR) > 0); + m_options.optimizer.expectedExecutionsPerDeployment = m_args[g_strOptimizeRuns].as(); - m_options.optimizer.enabled = (m_args.count(g_argOptimize) > 0); + m_options.optimizer.enabled = (m_args.count(g_strOptimize) > 0); m_options.optimizer.noOptimizeYul = (m_args.count(g_strNoOptimizeYul) > 0); OptimiserSettings settings = m_options.optimizer.enabled ? OptimiserSettings::standard() : OptimiserSettings::minimal(); @@ -1177,10 +1127,10 @@ General Information)").c_str(), m_options.optimizer.yulSteps = m_args[g_strYulOptimizations].as(); } - if (m_args.count(g_argImportAst) > 0) + if (m_args.count(g_strImportAst) > 0) m_options.input.mode = InputMode::CompilerWithASTImport; else - m_options.input.errorRecovery = (m_args.count(g_argErrorRecovery) > 0); + m_options.input.errorRecovery = (m_args.count(g_strErrorRecovery) > 0); solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); return true; @@ -1193,14 +1143,14 @@ bool CommandLineParser::hasOutput() bool CommandLineParser::parseCombinedJsonOption() { - if (!m_args.count(g_argCombinedJson)) + if (!m_args.count(g_strCombinedJson)) return true; set requests; - for (string const& item: boost::split(requests, m_args[g_argCombinedJson].as(), boost::is_any_of(","))) + for (string const& item: boost::split(requests, m_args[g_strCombinedJson].as(), boost::is_any_of(","))) if (!g_combinedJsonArgs.count(item)) { - serr() << "Invalid option to --" << g_argCombinedJson << ": " << item << endl; + serr() << "Invalid option to --" << g_strCombinedJson << ": " << item << endl; return false; } From f9f32c910d998a959b3b416e6f99521d1df1e425 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Wed, 16 Jun 2021 12:38:34 +0200 Subject: [PATCH 17/98] Fix source location for ``if`` statements. --- Changelog.md | 1 + libyul/AsmParser.cpp | 1 + .../combined_json_generated_sources/output | 10 +++++----- .../optimizer_inliner_dynamic_reference/output | 4 ++-- .../output | 4 ++-- .../cmdlineTests/standard_generatedSources/output.json | 2 +- .../standard_optimizer_generatedSources/output.json | 2 +- test/cmdlineTests/strict_asm_optimizer_steps/output | 4 ++-- test/libyul/objectCompiler/jump_tags.yul | 6 +++--- 9 files changed, 18 insertions(+), 16 deletions(-) diff --git a/Changelog.md b/Changelog.md index d7307c981..9638d597a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -18,6 +18,7 @@ Bugfixes: * Type Checker: Fix internal error and prevent static calls to unimplemented modifiers. * Yul Code Generator: Fix internal compiler error when using a long literal with bitwise negation. * Yul Code Generator: Fix source location references for calls to builtin functions. + * Yul Parser: Fix source location references for ``if`` statements. ### 0.8.6 (2021-06-22) diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 27955fe3b..6553dccfe 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -107,6 +107,7 @@ Statement Parser::parseStatement() advance(); _if.condition = make_unique(parseExpression()); _if.body = parseBlock(); + _if.debugData = updateLocationEndFrom(_if.debugData, _if.body.debugData->location); return Statement{move(_if)}; } case Token::Switch: diff --git a/test/cmdlineTests/combined_json_generated_sources/output b/test/cmdlineTests/combined_json_generated_sources/output index 9165832ca..3cf5cfb68 100644 --- a/test/cmdlineTests/combined_json_generated_sources/output +++ b/test/cmdlineTests/combined_json_generated_sources/output @@ -103,7 +103,7 @@ "src": "127:35:1" }, "nodeType": "YulIf", - "src": "124:2:1" + "src": "124:122:1" }, { "nodeType": "YulAssignment", @@ -188,7 +188,7 @@ "src": "297:30:1" }, "nodeType": "YulIf", - "src": "294:2:1" + "src": "294:117:1" }, { "nodeType": "YulAssignment", @@ -316,7 +316,7 @@ "src": "461:41:1" }, "nodeType": "YulIf", - "src": "458:2:1" + "src": "458:128:1" } ] }, @@ -431,7 +431,7 @@ "src": "712:32:1" }, "nodeType": "YulIf", - "src": "709:2:1" + "src": "709:119:1" }, { "nodeType": "YulBlock", @@ -542,7 +542,7 @@ "src": "914:30:1" }, "nodeType": "YulIf", - "src": "911:2:1" + "src": "911:117:1" }, { "nodeType": "YulAssignment", diff --git a/test/cmdlineTests/optimizer_inliner_dynamic_reference/output b/test/cmdlineTests/optimizer_inliner_dynamic_reference/output index 91000a7b7..ba32c364b 100644 --- a/test/cmdlineTests/optimizer_inliner_dynamic_reference/output +++ b/test/cmdlineTests/optimizer_inliner_dynamic_reference/output @@ -162,7 +162,7 @@ sub_0: assembly { dup3 /* "#utility.yul":257:270 */ gt - /* "#utility.yul":254:256 */ + /* "#utility.yul":254:390 */ iszero tag_30 jumpi @@ -188,7 +188,7 @@ sub_0: assembly { 0x00 /* "#utility.yul":365:380 */ revert - /* "#utility.yul":254:256 */ + /* "#utility.yul":254:390 */ tag_30: pop /* "#utility.yul":406:415 */ diff --git a/test/cmdlineTests/optimizer_inliner_dynamic_reference_constructor/output b/test/cmdlineTests/optimizer_inliner_dynamic_reference_constructor/output index e82ba9f72..83f06edad 100644 --- a/test/cmdlineTests/optimizer_inliner_dynamic_reference_constructor/output +++ b/test/cmdlineTests/optimizer_inliner_dynamic_reference_constructor/output @@ -167,7 +167,7 @@ sub_0: assembly { dup3 /* "#utility.yul":257:270 */ gt - /* "#utility.yul":254:256 */ + /* "#utility.yul":254:390 */ iszero tag_26 jumpi @@ -193,7 +193,7 @@ sub_0: assembly { 0x00 /* "#utility.yul":365:380 */ revert - /* "#utility.yul":254:256 */ + /* "#utility.yul":254:390 */ tag_26: pop /* "#utility.yul":406:415 */ diff --git a/test/cmdlineTests/standard_generatedSources/output.json b/test/cmdlineTests/standard_generatedSources/output.json index 507bc25ed..b09f6750f 100644 --- a/test/cmdlineTests/standard_generatedSources/output.json +++ b/test/cmdlineTests/standard_generatedSources/output.json @@ -1,4 +1,4 @@ -{"contracts":{"a.sol":{"A":{"evm":{"bytecode":{"generatedSources":[],"object":""},"deployedBytecode":{"generatedSources":[{"ast":{"nodeType":"YulBlock","src":"0:4001:1","statements":[{"body":{"nodeType":"YulBlock","src":"126:620:1","statements":[{"nodeType":"YulAssignment","src":"136:90:1","value":{"arguments":[{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"218:6:1"}],"functionName":{"name":"array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulIdentifier","src":"161:56:1"},"nodeType":"YulFunctionCall","src":"161:64:1"}],"functionName":{"name":"allocate_memory","nodeType":"YulIdentifier","src":"145:15:1"},"nodeType":"YulFunctionCall","src":"145:81:1"},"variableNames":[{"name":"array","nodeType":"YulIdentifier","src":"136:5:1"}]},{"nodeType":"YulVariableDeclaration","src":"235:16:1","value":{"name":"array","nodeType":"YulIdentifier","src":"246:5:1"},"variables":[{"name":"dst","nodeType":"YulTypedName","src":"239:3:1","type":""}]},{"expression":{"arguments":[{"name":"array","nodeType":"YulIdentifier","src":"268:5:1"},{"name":"length","nodeType":"YulIdentifier","src":"275:6:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"261:6:1"},"nodeType":"YulFunctionCall","src":"261:21:1"},"nodeType":"YulExpressionStatement","src":"261:21:1"},{"nodeType":"YulAssignment","src":"291:23:1","value":{"arguments":[{"name":"array","nodeType":"YulIdentifier","src":"302:5:1"},{"kind":"number","nodeType":"YulLiteral","src":"309:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"298:3:1"},"nodeType":"YulFunctionCall","src":"298:16:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"291:3:1"}]},{"nodeType":"YulVariableDeclaration","src":"324:17:1","value":{"name":"offset","nodeType":"YulIdentifier","src":"335:6:1"},"variables":[{"name":"src","nodeType":"YulTypedName","src":"328:3:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"390:103:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef","nodeType":"YulIdentifier","src":"404:77:1"},"nodeType":"YulFunctionCall","src":"404:79:1"},"nodeType":"YulExpressionStatement","src":"404:79:1"}]},"condition":{"arguments":[{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"360:3:1"},{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"369:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"377:4:1","type":"","value":"0x20"}],"functionName":{"name":"mul","nodeType":"YulIdentifier","src":"365:3:1"},"nodeType":"YulFunctionCall","src":"365:17:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"356:3:1"},"nodeType":"YulFunctionCall","src":"356:27:1"},{"name":"end","nodeType":"YulIdentifier","src":"385:3:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"353:2:1"},"nodeType":"YulFunctionCall","src":"353:36:1"},"nodeType":"YulIf","src":"350:2:1"},{"body":{"nodeType":"YulBlock","src":"562:178:1","statements":[{"nodeType":"YulVariableDeclaration","src":"577:21:1","value":{"name":"src","nodeType":"YulIdentifier","src":"595:3:1"},"variables":[{"name":"elementPos","nodeType":"YulTypedName","src":"581:10:1","type":""}]},{"expression":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"619:3:1"},{"arguments":[{"name":"elementPos","nodeType":"YulIdentifier","src":"645:10:1"},{"name":"end","nodeType":"YulIdentifier","src":"657:3:1"}],"functionName":{"name":"abi_decode_t_uint256","nodeType":"YulIdentifier","src":"624:20:1"},"nodeType":"YulFunctionCall","src":"624:37:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"612:6:1"},"nodeType":"YulFunctionCall","src":"612:50:1"},"nodeType":"YulExpressionStatement","src":"612:50:1"},{"nodeType":"YulAssignment","src":"675:21:1","value":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"686:3:1"},{"kind":"number","nodeType":"YulLiteral","src":"691:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"682:3:1"},"nodeType":"YulFunctionCall","src":"682:14:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"675:3:1"}]},{"nodeType":"YulAssignment","src":"709:21:1","value":{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"720:3:1"},{"kind":"number","nodeType":"YulLiteral","src":"725:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"716:3:1"},"nodeType":"YulFunctionCall","src":"716:14:1"},"variableNames":[{"name":"src","nodeType":"YulIdentifier","src":"709:3:1"}]}]},"condition":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"524:1:1"},{"name":"length","nodeType":"YulIdentifier","src":"527:6:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"521:2:1"},"nodeType":"YulFunctionCall","src":"521:13:1"},"nodeType":"YulForLoop","post":{"nodeType":"YulBlock","src":"535:18:1","statements":[{"nodeType":"YulAssignment","src":"537:14:1","value":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"546:1:1"},{"kind":"number","nodeType":"YulLiteral","src":"549:1:1","type":"","value":"1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"542:3:1"},"nodeType":"YulFunctionCall","src":"542:9:1"},"variableNames":[{"name":"i","nodeType":"YulIdentifier","src":"537:1:1"}]}]},"pre":{"nodeType":"YulBlock","src":"506:14:1","statements":[{"nodeType":"YulVariableDeclaration","src":"508:10:1","value":{"kind":"number","nodeType":"YulLiteral","src":"517:1:1","type":"","value":"0"},"variables":[{"name":"i","nodeType":"YulTypedName","src":"512:1:1","type":""}]}]},"src":"502:238:1"}]},"name":"abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"offset","nodeType":"YulTypedName","src":"96:6:1","type":""},{"name":"length","nodeType":"YulTypedName","src":"104:6:1","type":""},{"name":"end","nodeType":"YulTypedName","src":"112:3:1","type":""}],"returnVariables":[{"name":"array","nodeType":"YulTypedName","src":"120:5:1","type":""}],"src":"24:722:1"},{"body":{"nodeType":"YulBlock","src":"846:293:1","statements":[{"body":{"nodeType":"YulBlock","src":"895:83:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d","nodeType":"YulIdentifier","src":"897:77:1"},"nodeType":"YulFunctionCall","src":"897:79:1"},"nodeType":"YulExpressionStatement","src":"897:79:1"}]},"condition":{"arguments":[{"arguments":[{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"874:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"882:4:1","type":"","value":"0x1f"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"870:3:1"},"nodeType":"YulFunctionCall","src":"870:17:1"},{"name":"end","nodeType":"YulIdentifier","src":"889:3:1"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"866:3:1"},"nodeType":"YulFunctionCall","src":"866:27:1"}],"functionName":{"name":"iszero","nodeType":"YulIdentifier","src":"859:6:1"},"nodeType":"YulFunctionCall","src":"859:35:1"},"nodeType":"YulIf","src":"856:2:1"},{"nodeType":"YulVariableDeclaration","src":"987:34:1","value":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1014:6:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1001:12:1"},"nodeType":"YulFunctionCall","src":"1001:20:1"},"variables":[{"name":"length","nodeType":"YulTypedName","src":"991:6:1","type":""}]},{"nodeType":"YulAssignment","src":"1030:103:1","value":{"arguments":[{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1106:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"1114:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1102:3:1"},"nodeType":"YulFunctionCall","src":"1102:17:1"},{"name":"length","nodeType":"YulIdentifier","src":"1121:6:1"},{"name":"end","nodeType":"YulIdentifier","src":"1129:3:1"}],"functionName":{"name":"abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulIdentifier","src":"1039:62:1"},"nodeType":"YulFunctionCall","src":"1039:94:1"},"variableNames":[{"name":"array","nodeType":"YulIdentifier","src":"1030:5:1"}]}]},"name":"abi_decode_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"offset","nodeType":"YulTypedName","src":"824:6:1","type":""},{"name":"end","nodeType":"YulTypedName","src":"832:3:1","type":""}],"returnVariables":[{"name":"array","nodeType":"YulTypedName","src":"840:5:1","type":""}],"src":"769:370:1"},{"body":{"nodeType":"YulBlock","src":"1197:87:1","statements":[{"nodeType":"YulAssignment","src":"1207:29:1","value":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1229:6:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1216:12:1"},"nodeType":"YulFunctionCall","src":"1216:20:1"},"variableNames":[{"name":"value","nodeType":"YulIdentifier","src":"1207:5:1"}]},{"expression":{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"1272:5:1"}],"functionName":{"name":"validator_revert_t_uint256","nodeType":"YulIdentifier","src":"1245:26:1"},"nodeType":"YulFunctionCall","src":"1245:33:1"},"nodeType":"YulExpressionStatement","src":"1245:33:1"}]},"name":"abi_decode_t_uint256","nodeType":"YulFunctionDefinition","parameters":[{"name":"offset","nodeType":"YulTypedName","src":"1175:6:1","type":""},{"name":"end","nodeType":"YulTypedName","src":"1183:3:1","type":""}],"returnVariables":[{"name":"value","nodeType":"YulTypedName","src":"1191:5:1","type":""}],"src":"1145:139:1"},{"body":{"nodeType":"YulBlock","src":"1381:448:1","statements":[{"body":{"nodeType":"YulBlock","src":"1427:83:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b","nodeType":"YulIdentifier","src":"1429:77:1"},"nodeType":"YulFunctionCall","src":"1429:79:1"},"nodeType":"YulExpressionStatement","src":"1429:79:1"}]},"condition":{"arguments":[{"arguments":[{"name":"dataEnd","nodeType":"YulIdentifier","src":"1402:7:1"},{"name":"headStart","nodeType":"YulIdentifier","src":"1411:9:1"}],"functionName":{"name":"sub","nodeType":"YulIdentifier","src":"1398:3:1"},"nodeType":"YulFunctionCall","src":"1398:23:1"},{"kind":"number","nodeType":"YulLiteral","src":"1423:2:1","type":"","value":"32"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"1394:3:1"},"nodeType":"YulFunctionCall","src":"1394:32:1"},"nodeType":"YulIf","src":"1391:2:1"},{"nodeType":"YulBlock","src":"1520:302:1","statements":[{"nodeType":"YulVariableDeclaration","src":"1535:45:1","value":{"arguments":[{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1566:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"1577:1:1","type":"","value":"0"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1562:3:1"},"nodeType":"YulFunctionCall","src":"1562:17:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1549:12:1"},"nodeType":"YulFunctionCall","src":"1549:31:1"},"variables":[{"name":"offset","nodeType":"YulTypedName","src":"1539:6:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"1627:83:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db","nodeType":"YulIdentifier","src":"1629:77:1"},"nodeType":"YulFunctionCall","src":"1629:79:1"},"nodeType":"YulExpressionStatement","src":"1629:79:1"}]},"condition":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1599:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"1607:18:1","type":"","value":"0xffffffffffffffff"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"1596:2:1"},"nodeType":"YulFunctionCall","src":"1596:30:1"},"nodeType":"YulIf","src":"1593:2:1"},{"nodeType":"YulAssignment","src":"1724:88:1","value":{"arguments":[{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1784:9:1"},{"name":"offset","nodeType":"YulIdentifier","src":"1795:6:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1780:3:1"},"nodeType":"YulFunctionCall","src":"1780:22:1"},{"name":"dataEnd","nodeType":"YulIdentifier","src":"1804:7:1"}],"functionName":{"name":"abi_decode_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulIdentifier","src":"1734:45:1"},"nodeType":"YulFunctionCall","src":"1734:78:1"},"variableNames":[{"name":"value0","nodeType":"YulIdentifier","src":"1724:6:1"}]}]}]},"name":"abi_decode_tuple_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"1351:9:1","type":""},{"name":"dataEnd","nodeType":"YulTypedName","src":"1362:7:1","type":""}],"returnVariables":[{"name":"value0","nodeType":"YulTypedName","src":"1374:6:1","type":""}],"src":"1290:539:1"},{"body":{"nodeType":"YulBlock","src":"1900:53:1","statements":[{"expression":{"arguments":[{"name":"pos","nodeType":"YulIdentifier","src":"1917:3:1"},{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"1940:5:1"}],"functionName":{"name":"cleanup_t_uint256","nodeType":"YulIdentifier","src":"1922:17:1"},"nodeType":"YulFunctionCall","src":"1922:24:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1910:6:1"},"nodeType":"YulFunctionCall","src":"1910:37:1"},"nodeType":"YulExpressionStatement","src":"1910:37:1"}]},"name":"abi_encode_t_uint256_to_t_uint256_fromStack","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"1888:5:1","type":""},{"name":"pos","nodeType":"YulTypedName","src":"1895:3:1","type":""}],"src":"1835:118:1"},{"body":{"nodeType":"YulBlock","src":"2057:124:1","statements":[{"nodeType":"YulAssignment","src":"2067:26:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"2079:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"2090:2:1","type":"","value":"32"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2075:3:1"},"nodeType":"YulFunctionCall","src":"2075:18:1"},"variableNames":[{"name":"tail","nodeType":"YulIdentifier","src":"2067:4:1"}]},{"expression":{"arguments":[{"name":"value0","nodeType":"YulIdentifier","src":"2147:6:1"},{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"2160:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"2171:1:1","type":"","value":"0"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2156:3:1"},"nodeType":"YulFunctionCall","src":"2156:17:1"}],"functionName":{"name":"abi_encode_t_uint256_to_t_uint256_fromStack","nodeType":"YulIdentifier","src":"2103:43:1"},"nodeType":"YulFunctionCall","src":"2103:71:1"},"nodeType":"YulExpressionStatement","src":"2103:71:1"}]},"name":"abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"2029:9:1","type":""},{"name":"value0","nodeType":"YulTypedName","src":"2041:6:1","type":""}],"returnVariables":[{"name":"tail","nodeType":"YulTypedName","src":"2052:4:1","type":""}],"src":"1959:222:1"},{"body":{"nodeType":"YulBlock","src":"2228:88:1","statements":[{"nodeType":"YulAssignment","src":"2238:30:1","value":{"arguments":[],"functionName":{"name":"allocate_unbounded","nodeType":"YulIdentifier","src":"2248:18:1"},"nodeType":"YulFunctionCall","src":"2248:20:1"},"variableNames":[{"name":"memPtr","nodeType":"YulIdentifier","src":"2238:6:1"}]},{"expression":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"2297:6:1"},{"name":"size","nodeType":"YulIdentifier","src":"2305:4:1"}],"functionName":{"name":"finalize_allocation","nodeType":"YulIdentifier","src":"2277:19:1"},"nodeType":"YulFunctionCall","src":"2277:33:1"},"nodeType":"YulExpressionStatement","src":"2277:33:1"}]},"name":"allocate_memory","nodeType":"YulFunctionDefinition","parameters":[{"name":"size","nodeType":"YulTypedName","src":"2212:4:1","type":""}],"returnVariables":[{"name":"memPtr","nodeType":"YulTypedName","src":"2221:6:1","type":""}],"src":"2187:129:1"},{"body":{"nodeType":"YulBlock","src":"2362:35:1","statements":[{"nodeType":"YulAssignment","src":"2372:19:1","value":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"2388:2:1","type":"","value":"64"}],"functionName":{"name":"mload","nodeType":"YulIdentifier","src":"2382:5:1"},"nodeType":"YulFunctionCall","src":"2382:9:1"},"variableNames":[{"name":"memPtr","nodeType":"YulIdentifier","src":"2372:6:1"}]}]},"name":"allocate_unbounded","nodeType":"YulFunctionDefinition","returnVariables":[{"name":"memPtr","nodeType":"YulTypedName","src":"2355:6:1","type":""}],"src":"2322:75:1"},{"body":{"nodeType":"YulBlock","src":"2485:229:1","statements":[{"body":{"nodeType":"YulBlock","src":"2590:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"2592:16:1"},"nodeType":"YulFunctionCall","src":"2592:18:1"},"nodeType":"YulExpressionStatement","src":"2592:18:1"}]},"condition":{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"2562:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"2570:18:1","type":"","value":"0xffffffffffffffff"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"2559:2:1"},"nodeType":"YulFunctionCall","src":"2559:30:1"},"nodeType":"YulIf","src":"2556:2:1"},{"nodeType":"YulAssignment","src":"2622:25:1","value":{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"2634:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"2642:4:1","type":"","value":"0x20"}],"functionName":{"name":"mul","nodeType":"YulIdentifier","src":"2630:3:1"},"nodeType":"YulFunctionCall","src":"2630:17:1"},"variableNames":[{"name":"size","nodeType":"YulIdentifier","src":"2622:4:1"}]},{"nodeType":"YulAssignment","src":"2684:23:1","value":{"arguments":[{"name":"size","nodeType":"YulIdentifier","src":"2696:4:1"},{"kind":"number","nodeType":"YulLiteral","src":"2702:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2692:3:1"},"nodeType":"YulFunctionCall","src":"2692:15:1"},"variableNames":[{"name":"size","nodeType":"YulIdentifier","src":"2684:4:1"}]}]},"name":"array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"length","nodeType":"YulTypedName","src":"2469:6:1","type":""}],"returnVariables":[{"name":"size","nodeType":"YulTypedName","src":"2480:4:1","type":""}],"src":"2403:311:1"},{"body":{"nodeType":"YulBlock","src":"2765:32:1","statements":[{"nodeType":"YulAssignment","src":"2775:16:1","value":{"name":"value","nodeType":"YulIdentifier","src":"2786:5:1"},"variableNames":[{"name":"cleaned","nodeType":"YulIdentifier","src":"2775:7:1"}]}]},"name":"cleanup_t_uint256","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"2747:5:1","type":""}],"returnVariables":[{"name":"cleaned","nodeType":"YulTypedName","src":"2757:7:1","type":""}],"src":"2720:77:1"},{"body":{"nodeType":"YulBlock","src":"2846:238:1","statements":[{"nodeType":"YulVariableDeclaration","src":"2856:58:1","value":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"2878:6:1"},{"arguments":[{"name":"size","nodeType":"YulIdentifier","src":"2908:4:1"}],"functionName":{"name":"round_up_to_mul_of_32","nodeType":"YulIdentifier","src":"2886:21:1"},"nodeType":"YulFunctionCall","src":"2886:27:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2874:3:1"},"nodeType":"YulFunctionCall","src":"2874:40:1"},"variables":[{"name":"newFreePtr","nodeType":"YulTypedName","src":"2860:10:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"3025:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"3027:16:1"},"nodeType":"YulFunctionCall","src":"3027:18:1"},"nodeType":"YulExpressionStatement","src":"3027:18:1"}]},"condition":{"arguments":[{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"2968:10:1"},{"kind":"number","nodeType":"YulLiteral","src":"2980:18:1","type":"","value":"0xffffffffffffffff"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"2965:2:1"},"nodeType":"YulFunctionCall","src":"2965:34:1"},{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"3004:10:1"},{"name":"memPtr","nodeType":"YulIdentifier","src":"3016:6:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"3001:2:1"},"nodeType":"YulFunctionCall","src":"3001:22:1"}],"functionName":{"name":"or","nodeType":"YulIdentifier","src":"2962:2:1"},"nodeType":"YulFunctionCall","src":"2962:62:1"},"nodeType":"YulIf","src":"2959:2:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3063:2:1","type":"","value":"64"},{"name":"newFreePtr","nodeType":"YulIdentifier","src":"3067:10:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"3056:6:1"},"nodeType":"YulFunctionCall","src":"3056:22:1"},"nodeType":"YulExpressionStatement","src":"3056:22:1"}]},"name":"finalize_allocation","nodeType":"YulFunctionDefinition","parameters":[{"name":"memPtr","nodeType":"YulTypedName","src":"2832:6:1","type":""},{"name":"size","nodeType":"YulTypedName","src":"2840:4:1","type":""}],"src":"2803:281:1"},{"body":{"nodeType":"YulBlock","src":"3118:152:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3135:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3138:77:1","type":"","value":"35408467139433450592217433187231851964531694900788300625387963629091585785856"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"3128:6:1"},"nodeType":"YulFunctionCall","src":"3128:88:1"},"nodeType":"YulExpressionStatement","src":"3128:88:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3232:1:1","type":"","value":"4"},{"kind":"number","nodeType":"YulLiteral","src":"3235:4:1","type":"","value":"0x41"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"3225:6:1"},"nodeType":"YulFunctionCall","src":"3225:15:1"},"nodeType":"YulExpressionStatement","src":"3225:15:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3256:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3259:4:1","type":"","value":"0x24"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3249:6:1"},"nodeType":"YulFunctionCall","src":"3249:15:1"},"nodeType":"YulExpressionStatement","src":"3249:15:1"}]},"name":"panic_error_0x41","nodeType":"YulFunctionDefinition","src":"3090:180:1"},{"body":{"nodeType":"YulBlock","src":"3365:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3382:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3385:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3375:6:1"},"nodeType":"YulFunctionCall","src":"3375:12:1"},"nodeType":"YulExpressionStatement","src":"3375:12:1"}]},"name":"revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d","nodeType":"YulFunctionDefinition","src":"3276:117:1"},{"body":{"nodeType":"YulBlock","src":"3488:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3505:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3508:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3498:6:1"},"nodeType":"YulFunctionCall","src":"3498:12:1"},"nodeType":"YulExpressionStatement","src":"3498:12:1"}]},"name":"revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef","nodeType":"YulFunctionDefinition","src":"3399:117:1"},{"body":{"nodeType":"YulBlock","src":"3611:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3628:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3631:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3621:6:1"},"nodeType":"YulFunctionCall","src":"3621:12:1"},"nodeType":"YulExpressionStatement","src":"3621:12:1"}]},"name":"revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db","nodeType":"YulFunctionDefinition","src":"3522:117:1"},{"body":{"nodeType":"YulBlock","src":"3734:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3751:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3754:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3744:6:1"},"nodeType":"YulFunctionCall","src":"3744:12:1"},"nodeType":"YulExpressionStatement","src":"3744:12:1"}]},"name":"revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b","nodeType":"YulFunctionDefinition","src":"3645:117:1"},{"body":{"nodeType":"YulBlock","src":"3816:54:1","statements":[{"nodeType":"YulAssignment","src":"3826:38:1","value":{"arguments":[{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"3844:5:1"},{"kind":"number","nodeType":"YulLiteral","src":"3851:2:1","type":"","value":"31"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"3840:3:1"},"nodeType":"YulFunctionCall","src":"3840:14:1"},{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3860:2:1","type":"","value":"31"}],"functionName":{"name":"not","nodeType":"YulIdentifier","src":"3856:3:1"},"nodeType":"YulFunctionCall","src":"3856:7:1"}],"functionName":{"name":"and","nodeType":"YulIdentifier","src":"3836:3:1"},"nodeType":"YulFunctionCall","src":"3836:28:1"},"variableNames":[{"name":"result","nodeType":"YulIdentifier","src":"3826:6:1"}]}]},"name":"round_up_to_mul_of_32","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"3799:5:1","type":""}],"returnVariables":[{"name":"result","nodeType":"YulTypedName","src":"3809:6:1","type":""}],"src":"3768:102:1"},{"body":{"nodeType":"YulBlock","src":"3919:79:1","statements":[{"body":{"nodeType":"YulBlock","src":"3976:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3985:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3988:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3978:6:1"},"nodeType":"YulFunctionCall","src":"3978:12:1"},"nodeType":"YulExpressionStatement","src":"3978:12:1"}]},"condition":{"arguments":[{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"3942:5:1"},{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"3967:5:1"}],"functionName":{"name":"cleanup_t_uint256","nodeType":"YulIdentifier","src":"3949:17:1"},"nodeType":"YulFunctionCall","src":"3949:24:1"}],"functionName":{"name":"eq","nodeType":"YulIdentifier","src":"3939:2:1"},"nodeType":"YulFunctionCall","src":"3939:35:1"}],"functionName":{"name":"iszero","nodeType":"YulIdentifier","src":"3932:6:1"},"nodeType":"YulFunctionCall","src":"3932:43:1"},"nodeType":"YulIf","src":"3929:2:1"}]},"name":"validator_revert_t_uint256","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"3912:5:1","type":""}],"src":"3876:122:1"}]},"contents":"{ +{"contracts":{"a.sol":{"A":{"evm":{"bytecode":{"generatedSources":[],"object":""},"deployedBytecode":{"generatedSources":[{"ast":{"nodeType":"YulBlock","src":"0:4001:1","statements":[{"body":{"nodeType":"YulBlock","src":"126:620:1","statements":[{"nodeType":"YulAssignment","src":"136:90:1","value":{"arguments":[{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"218:6:1"}],"functionName":{"name":"array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulIdentifier","src":"161:56:1"},"nodeType":"YulFunctionCall","src":"161:64:1"}],"functionName":{"name":"allocate_memory","nodeType":"YulIdentifier","src":"145:15:1"},"nodeType":"YulFunctionCall","src":"145:81:1"},"variableNames":[{"name":"array","nodeType":"YulIdentifier","src":"136:5:1"}]},{"nodeType":"YulVariableDeclaration","src":"235:16:1","value":{"name":"array","nodeType":"YulIdentifier","src":"246:5:1"},"variables":[{"name":"dst","nodeType":"YulTypedName","src":"239:3:1","type":""}]},{"expression":{"arguments":[{"name":"array","nodeType":"YulIdentifier","src":"268:5:1"},{"name":"length","nodeType":"YulIdentifier","src":"275:6:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"261:6:1"},"nodeType":"YulFunctionCall","src":"261:21:1"},"nodeType":"YulExpressionStatement","src":"261:21:1"},{"nodeType":"YulAssignment","src":"291:23:1","value":{"arguments":[{"name":"array","nodeType":"YulIdentifier","src":"302:5:1"},{"kind":"number","nodeType":"YulLiteral","src":"309:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"298:3:1"},"nodeType":"YulFunctionCall","src":"298:16:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"291:3:1"}]},{"nodeType":"YulVariableDeclaration","src":"324:17:1","value":{"name":"offset","nodeType":"YulIdentifier","src":"335:6:1"},"variables":[{"name":"src","nodeType":"YulTypedName","src":"328:3:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"390:103:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef","nodeType":"YulIdentifier","src":"404:77:1"},"nodeType":"YulFunctionCall","src":"404:79:1"},"nodeType":"YulExpressionStatement","src":"404:79:1"}]},"condition":{"arguments":[{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"360:3:1"},{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"369:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"377:4:1","type":"","value":"0x20"}],"functionName":{"name":"mul","nodeType":"YulIdentifier","src":"365:3:1"},"nodeType":"YulFunctionCall","src":"365:17:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"356:3:1"},"nodeType":"YulFunctionCall","src":"356:27:1"},{"name":"end","nodeType":"YulIdentifier","src":"385:3:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"353:2:1"},"nodeType":"YulFunctionCall","src":"353:36:1"},"nodeType":"YulIf","src":"350:143:1"},{"body":{"nodeType":"YulBlock","src":"562:178:1","statements":[{"nodeType":"YulVariableDeclaration","src":"577:21:1","value":{"name":"src","nodeType":"YulIdentifier","src":"595:3:1"},"variables":[{"name":"elementPos","nodeType":"YulTypedName","src":"581:10:1","type":""}]},{"expression":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"619:3:1"},{"arguments":[{"name":"elementPos","nodeType":"YulIdentifier","src":"645:10:1"},{"name":"end","nodeType":"YulIdentifier","src":"657:3:1"}],"functionName":{"name":"abi_decode_t_uint256","nodeType":"YulIdentifier","src":"624:20:1"},"nodeType":"YulFunctionCall","src":"624:37:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"612:6:1"},"nodeType":"YulFunctionCall","src":"612:50:1"},"nodeType":"YulExpressionStatement","src":"612:50:1"},{"nodeType":"YulAssignment","src":"675:21:1","value":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"686:3:1"},{"kind":"number","nodeType":"YulLiteral","src":"691:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"682:3:1"},"nodeType":"YulFunctionCall","src":"682:14:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"675:3:1"}]},{"nodeType":"YulAssignment","src":"709:21:1","value":{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"720:3:1"},{"kind":"number","nodeType":"YulLiteral","src":"725:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"716:3:1"},"nodeType":"YulFunctionCall","src":"716:14:1"},"variableNames":[{"name":"src","nodeType":"YulIdentifier","src":"709:3:1"}]}]},"condition":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"524:1:1"},{"name":"length","nodeType":"YulIdentifier","src":"527:6:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"521:2:1"},"nodeType":"YulFunctionCall","src":"521:13:1"},"nodeType":"YulForLoop","post":{"nodeType":"YulBlock","src":"535:18:1","statements":[{"nodeType":"YulAssignment","src":"537:14:1","value":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"546:1:1"},{"kind":"number","nodeType":"YulLiteral","src":"549:1:1","type":"","value":"1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"542:3:1"},"nodeType":"YulFunctionCall","src":"542:9:1"},"variableNames":[{"name":"i","nodeType":"YulIdentifier","src":"537:1:1"}]}]},"pre":{"nodeType":"YulBlock","src":"506:14:1","statements":[{"nodeType":"YulVariableDeclaration","src":"508:10:1","value":{"kind":"number","nodeType":"YulLiteral","src":"517:1:1","type":"","value":"0"},"variables":[{"name":"i","nodeType":"YulTypedName","src":"512:1:1","type":""}]}]},"src":"502:238:1"}]},"name":"abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"offset","nodeType":"YulTypedName","src":"96:6:1","type":""},{"name":"length","nodeType":"YulTypedName","src":"104:6:1","type":""},{"name":"end","nodeType":"YulTypedName","src":"112:3:1","type":""}],"returnVariables":[{"name":"array","nodeType":"YulTypedName","src":"120:5:1","type":""}],"src":"24:722:1"},{"body":{"nodeType":"YulBlock","src":"846:293:1","statements":[{"body":{"nodeType":"YulBlock","src":"895:83:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d","nodeType":"YulIdentifier","src":"897:77:1"},"nodeType":"YulFunctionCall","src":"897:79:1"},"nodeType":"YulExpressionStatement","src":"897:79:1"}]},"condition":{"arguments":[{"arguments":[{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"874:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"882:4:1","type":"","value":"0x1f"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"870:3:1"},"nodeType":"YulFunctionCall","src":"870:17:1"},{"name":"end","nodeType":"YulIdentifier","src":"889:3:1"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"866:3:1"},"nodeType":"YulFunctionCall","src":"866:27:1"}],"functionName":{"name":"iszero","nodeType":"YulIdentifier","src":"859:6:1"},"nodeType":"YulFunctionCall","src":"859:35:1"},"nodeType":"YulIf","src":"856:122:1"},{"nodeType":"YulVariableDeclaration","src":"987:34:1","value":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1014:6:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1001:12:1"},"nodeType":"YulFunctionCall","src":"1001:20:1"},"variables":[{"name":"length","nodeType":"YulTypedName","src":"991:6:1","type":""}]},{"nodeType":"YulAssignment","src":"1030:103:1","value":{"arguments":[{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1106:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"1114:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1102:3:1"},"nodeType":"YulFunctionCall","src":"1102:17:1"},{"name":"length","nodeType":"YulIdentifier","src":"1121:6:1"},{"name":"end","nodeType":"YulIdentifier","src":"1129:3:1"}],"functionName":{"name":"abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulIdentifier","src":"1039:62:1"},"nodeType":"YulFunctionCall","src":"1039:94:1"},"variableNames":[{"name":"array","nodeType":"YulIdentifier","src":"1030:5:1"}]}]},"name":"abi_decode_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"offset","nodeType":"YulTypedName","src":"824:6:1","type":""},{"name":"end","nodeType":"YulTypedName","src":"832:3:1","type":""}],"returnVariables":[{"name":"array","nodeType":"YulTypedName","src":"840:5:1","type":""}],"src":"769:370:1"},{"body":{"nodeType":"YulBlock","src":"1197:87:1","statements":[{"nodeType":"YulAssignment","src":"1207:29:1","value":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1229:6:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1216:12:1"},"nodeType":"YulFunctionCall","src":"1216:20:1"},"variableNames":[{"name":"value","nodeType":"YulIdentifier","src":"1207:5:1"}]},{"expression":{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"1272:5:1"}],"functionName":{"name":"validator_revert_t_uint256","nodeType":"YulIdentifier","src":"1245:26:1"},"nodeType":"YulFunctionCall","src":"1245:33:1"},"nodeType":"YulExpressionStatement","src":"1245:33:1"}]},"name":"abi_decode_t_uint256","nodeType":"YulFunctionDefinition","parameters":[{"name":"offset","nodeType":"YulTypedName","src":"1175:6:1","type":""},{"name":"end","nodeType":"YulTypedName","src":"1183:3:1","type":""}],"returnVariables":[{"name":"value","nodeType":"YulTypedName","src":"1191:5:1","type":""}],"src":"1145:139:1"},{"body":{"nodeType":"YulBlock","src":"1381:448:1","statements":[{"body":{"nodeType":"YulBlock","src":"1427:83:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b","nodeType":"YulIdentifier","src":"1429:77:1"},"nodeType":"YulFunctionCall","src":"1429:79:1"},"nodeType":"YulExpressionStatement","src":"1429:79:1"}]},"condition":{"arguments":[{"arguments":[{"name":"dataEnd","nodeType":"YulIdentifier","src":"1402:7:1"},{"name":"headStart","nodeType":"YulIdentifier","src":"1411:9:1"}],"functionName":{"name":"sub","nodeType":"YulIdentifier","src":"1398:3:1"},"nodeType":"YulFunctionCall","src":"1398:23:1"},{"kind":"number","nodeType":"YulLiteral","src":"1423:2:1","type":"","value":"32"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"1394:3:1"},"nodeType":"YulFunctionCall","src":"1394:32:1"},"nodeType":"YulIf","src":"1391:119:1"},{"nodeType":"YulBlock","src":"1520:302:1","statements":[{"nodeType":"YulVariableDeclaration","src":"1535:45:1","value":{"arguments":[{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1566:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"1577:1:1","type":"","value":"0"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1562:3:1"},"nodeType":"YulFunctionCall","src":"1562:17:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1549:12:1"},"nodeType":"YulFunctionCall","src":"1549:31:1"},"variables":[{"name":"offset","nodeType":"YulTypedName","src":"1539:6:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"1627:83:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db","nodeType":"YulIdentifier","src":"1629:77:1"},"nodeType":"YulFunctionCall","src":"1629:79:1"},"nodeType":"YulExpressionStatement","src":"1629:79:1"}]},"condition":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1599:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"1607:18:1","type":"","value":"0xffffffffffffffff"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"1596:2:1"},"nodeType":"YulFunctionCall","src":"1596:30:1"},"nodeType":"YulIf","src":"1593:117:1"},{"nodeType":"YulAssignment","src":"1724:88:1","value":{"arguments":[{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1784:9:1"},{"name":"offset","nodeType":"YulIdentifier","src":"1795:6:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1780:3:1"},"nodeType":"YulFunctionCall","src":"1780:22:1"},{"name":"dataEnd","nodeType":"YulIdentifier","src":"1804:7:1"}],"functionName":{"name":"abi_decode_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulIdentifier","src":"1734:45:1"},"nodeType":"YulFunctionCall","src":"1734:78:1"},"variableNames":[{"name":"value0","nodeType":"YulIdentifier","src":"1724:6:1"}]}]}]},"name":"abi_decode_tuple_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"1351:9:1","type":""},{"name":"dataEnd","nodeType":"YulTypedName","src":"1362:7:1","type":""}],"returnVariables":[{"name":"value0","nodeType":"YulTypedName","src":"1374:6:1","type":""}],"src":"1290:539:1"},{"body":{"nodeType":"YulBlock","src":"1900:53:1","statements":[{"expression":{"arguments":[{"name":"pos","nodeType":"YulIdentifier","src":"1917:3:1"},{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"1940:5:1"}],"functionName":{"name":"cleanup_t_uint256","nodeType":"YulIdentifier","src":"1922:17:1"},"nodeType":"YulFunctionCall","src":"1922:24:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1910:6:1"},"nodeType":"YulFunctionCall","src":"1910:37:1"},"nodeType":"YulExpressionStatement","src":"1910:37:1"}]},"name":"abi_encode_t_uint256_to_t_uint256_fromStack","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"1888:5:1","type":""},{"name":"pos","nodeType":"YulTypedName","src":"1895:3:1","type":""}],"src":"1835:118:1"},{"body":{"nodeType":"YulBlock","src":"2057:124:1","statements":[{"nodeType":"YulAssignment","src":"2067:26:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"2079:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"2090:2:1","type":"","value":"32"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2075:3:1"},"nodeType":"YulFunctionCall","src":"2075:18:1"},"variableNames":[{"name":"tail","nodeType":"YulIdentifier","src":"2067:4:1"}]},{"expression":{"arguments":[{"name":"value0","nodeType":"YulIdentifier","src":"2147:6:1"},{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"2160:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"2171:1:1","type":"","value":"0"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2156:3:1"},"nodeType":"YulFunctionCall","src":"2156:17:1"}],"functionName":{"name":"abi_encode_t_uint256_to_t_uint256_fromStack","nodeType":"YulIdentifier","src":"2103:43:1"},"nodeType":"YulFunctionCall","src":"2103:71:1"},"nodeType":"YulExpressionStatement","src":"2103:71:1"}]},"name":"abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"2029:9:1","type":""},{"name":"value0","nodeType":"YulTypedName","src":"2041:6:1","type":""}],"returnVariables":[{"name":"tail","nodeType":"YulTypedName","src":"2052:4:1","type":""}],"src":"1959:222:1"},{"body":{"nodeType":"YulBlock","src":"2228:88:1","statements":[{"nodeType":"YulAssignment","src":"2238:30:1","value":{"arguments":[],"functionName":{"name":"allocate_unbounded","nodeType":"YulIdentifier","src":"2248:18:1"},"nodeType":"YulFunctionCall","src":"2248:20:1"},"variableNames":[{"name":"memPtr","nodeType":"YulIdentifier","src":"2238:6:1"}]},{"expression":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"2297:6:1"},{"name":"size","nodeType":"YulIdentifier","src":"2305:4:1"}],"functionName":{"name":"finalize_allocation","nodeType":"YulIdentifier","src":"2277:19:1"},"nodeType":"YulFunctionCall","src":"2277:33:1"},"nodeType":"YulExpressionStatement","src":"2277:33:1"}]},"name":"allocate_memory","nodeType":"YulFunctionDefinition","parameters":[{"name":"size","nodeType":"YulTypedName","src":"2212:4:1","type":""}],"returnVariables":[{"name":"memPtr","nodeType":"YulTypedName","src":"2221:6:1","type":""}],"src":"2187:129:1"},{"body":{"nodeType":"YulBlock","src":"2362:35:1","statements":[{"nodeType":"YulAssignment","src":"2372:19:1","value":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"2388:2:1","type":"","value":"64"}],"functionName":{"name":"mload","nodeType":"YulIdentifier","src":"2382:5:1"},"nodeType":"YulFunctionCall","src":"2382:9:1"},"variableNames":[{"name":"memPtr","nodeType":"YulIdentifier","src":"2372:6:1"}]}]},"name":"allocate_unbounded","nodeType":"YulFunctionDefinition","returnVariables":[{"name":"memPtr","nodeType":"YulTypedName","src":"2355:6:1","type":""}],"src":"2322:75:1"},{"body":{"nodeType":"YulBlock","src":"2485:229:1","statements":[{"body":{"nodeType":"YulBlock","src":"2590:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"2592:16:1"},"nodeType":"YulFunctionCall","src":"2592:18:1"},"nodeType":"YulExpressionStatement","src":"2592:18:1"}]},"condition":{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"2562:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"2570:18:1","type":"","value":"0xffffffffffffffff"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"2559:2:1"},"nodeType":"YulFunctionCall","src":"2559:30:1"},"nodeType":"YulIf","src":"2556:56:1"},{"nodeType":"YulAssignment","src":"2622:25:1","value":{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"2634:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"2642:4:1","type":"","value":"0x20"}],"functionName":{"name":"mul","nodeType":"YulIdentifier","src":"2630:3:1"},"nodeType":"YulFunctionCall","src":"2630:17:1"},"variableNames":[{"name":"size","nodeType":"YulIdentifier","src":"2622:4:1"}]},{"nodeType":"YulAssignment","src":"2684:23:1","value":{"arguments":[{"name":"size","nodeType":"YulIdentifier","src":"2696:4:1"},{"kind":"number","nodeType":"YulLiteral","src":"2702:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2692:3:1"},"nodeType":"YulFunctionCall","src":"2692:15:1"},"variableNames":[{"name":"size","nodeType":"YulIdentifier","src":"2684:4:1"}]}]},"name":"array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"length","nodeType":"YulTypedName","src":"2469:6:1","type":""}],"returnVariables":[{"name":"size","nodeType":"YulTypedName","src":"2480:4:1","type":""}],"src":"2403:311:1"},{"body":{"nodeType":"YulBlock","src":"2765:32:1","statements":[{"nodeType":"YulAssignment","src":"2775:16:1","value":{"name":"value","nodeType":"YulIdentifier","src":"2786:5:1"},"variableNames":[{"name":"cleaned","nodeType":"YulIdentifier","src":"2775:7:1"}]}]},"name":"cleanup_t_uint256","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"2747:5:1","type":""}],"returnVariables":[{"name":"cleaned","nodeType":"YulTypedName","src":"2757:7:1","type":""}],"src":"2720:77:1"},{"body":{"nodeType":"YulBlock","src":"2846:238:1","statements":[{"nodeType":"YulVariableDeclaration","src":"2856:58:1","value":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"2878:6:1"},{"arguments":[{"name":"size","nodeType":"YulIdentifier","src":"2908:4:1"}],"functionName":{"name":"round_up_to_mul_of_32","nodeType":"YulIdentifier","src":"2886:21:1"},"nodeType":"YulFunctionCall","src":"2886:27:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2874:3:1"},"nodeType":"YulFunctionCall","src":"2874:40:1"},"variables":[{"name":"newFreePtr","nodeType":"YulTypedName","src":"2860:10:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"3025:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"3027:16:1"},"nodeType":"YulFunctionCall","src":"3027:18:1"},"nodeType":"YulExpressionStatement","src":"3027:18:1"}]},"condition":{"arguments":[{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"2968:10:1"},{"kind":"number","nodeType":"YulLiteral","src":"2980:18:1","type":"","value":"0xffffffffffffffff"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"2965:2:1"},"nodeType":"YulFunctionCall","src":"2965:34:1"},{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"3004:10:1"},{"name":"memPtr","nodeType":"YulIdentifier","src":"3016:6:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"3001:2:1"},"nodeType":"YulFunctionCall","src":"3001:22:1"}],"functionName":{"name":"or","nodeType":"YulIdentifier","src":"2962:2:1"},"nodeType":"YulFunctionCall","src":"2962:62:1"},"nodeType":"YulIf","src":"2959:88:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3063:2:1","type":"","value":"64"},{"name":"newFreePtr","nodeType":"YulIdentifier","src":"3067:10:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"3056:6:1"},"nodeType":"YulFunctionCall","src":"3056:22:1"},"nodeType":"YulExpressionStatement","src":"3056:22:1"}]},"name":"finalize_allocation","nodeType":"YulFunctionDefinition","parameters":[{"name":"memPtr","nodeType":"YulTypedName","src":"2832:6:1","type":""},{"name":"size","nodeType":"YulTypedName","src":"2840:4:1","type":""}],"src":"2803:281:1"},{"body":{"nodeType":"YulBlock","src":"3118:152:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3135:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3138:77:1","type":"","value":"35408467139433450592217433187231851964531694900788300625387963629091585785856"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"3128:6:1"},"nodeType":"YulFunctionCall","src":"3128:88:1"},"nodeType":"YulExpressionStatement","src":"3128:88:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3232:1:1","type":"","value":"4"},{"kind":"number","nodeType":"YulLiteral","src":"3235:4:1","type":"","value":"0x41"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"3225:6:1"},"nodeType":"YulFunctionCall","src":"3225:15:1"},"nodeType":"YulExpressionStatement","src":"3225:15:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3256:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3259:4:1","type":"","value":"0x24"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3249:6:1"},"nodeType":"YulFunctionCall","src":"3249:15:1"},"nodeType":"YulExpressionStatement","src":"3249:15:1"}]},"name":"panic_error_0x41","nodeType":"YulFunctionDefinition","src":"3090:180:1"},{"body":{"nodeType":"YulBlock","src":"3365:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3382:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3385:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3375:6:1"},"nodeType":"YulFunctionCall","src":"3375:12:1"},"nodeType":"YulExpressionStatement","src":"3375:12:1"}]},"name":"revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d","nodeType":"YulFunctionDefinition","src":"3276:117:1"},{"body":{"nodeType":"YulBlock","src":"3488:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3505:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3508:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3498:6:1"},"nodeType":"YulFunctionCall","src":"3498:12:1"},"nodeType":"YulExpressionStatement","src":"3498:12:1"}]},"name":"revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef","nodeType":"YulFunctionDefinition","src":"3399:117:1"},{"body":{"nodeType":"YulBlock","src":"3611:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3628:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3631:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3621:6:1"},"nodeType":"YulFunctionCall","src":"3621:12:1"},"nodeType":"YulExpressionStatement","src":"3621:12:1"}]},"name":"revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db","nodeType":"YulFunctionDefinition","src":"3522:117:1"},{"body":{"nodeType":"YulBlock","src":"3734:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3751:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3754:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3744:6:1"},"nodeType":"YulFunctionCall","src":"3744:12:1"},"nodeType":"YulExpressionStatement","src":"3744:12:1"}]},"name":"revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b","nodeType":"YulFunctionDefinition","src":"3645:117:1"},{"body":{"nodeType":"YulBlock","src":"3816:54:1","statements":[{"nodeType":"YulAssignment","src":"3826:38:1","value":{"arguments":[{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"3844:5:1"},{"kind":"number","nodeType":"YulLiteral","src":"3851:2:1","type":"","value":"31"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"3840:3:1"},"nodeType":"YulFunctionCall","src":"3840:14:1"},{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3860:2:1","type":"","value":"31"}],"functionName":{"name":"not","nodeType":"YulIdentifier","src":"3856:3:1"},"nodeType":"YulFunctionCall","src":"3856:7:1"}],"functionName":{"name":"and","nodeType":"YulIdentifier","src":"3836:3:1"},"nodeType":"YulFunctionCall","src":"3836:28:1"},"variableNames":[{"name":"result","nodeType":"YulIdentifier","src":"3826:6:1"}]}]},"name":"round_up_to_mul_of_32","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"3799:5:1","type":""}],"returnVariables":[{"name":"result","nodeType":"YulTypedName","src":"3809:6:1","type":""}],"src":"3768:102:1"},{"body":{"nodeType":"YulBlock","src":"3919:79:1","statements":[{"body":{"nodeType":"YulBlock","src":"3976:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3985:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3988:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3978:6:1"},"nodeType":"YulFunctionCall","src":"3978:12:1"},"nodeType":"YulExpressionStatement","src":"3978:12:1"}]},"condition":{"arguments":[{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"3942:5:1"},{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"3967:5:1"}],"functionName":{"name":"cleanup_t_uint256","nodeType":"YulIdentifier","src":"3949:17:1"},"nodeType":"YulFunctionCall","src":"3949:24:1"}],"functionName":{"name":"eq","nodeType":"YulIdentifier","src":"3939:2:1"},"nodeType":"YulFunctionCall","src":"3939:35:1"}],"functionName":{"name":"iszero","nodeType":"YulIdentifier","src":"3932:6:1"},"nodeType":"YulFunctionCall","src":"3932:43:1"},"nodeType":"YulIf","src":"3929:63:1"}]},"name":"validator_revert_t_uint256","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"3912:5:1","type":""}],"src":"3876:122:1"}]},"contents":"{ // uint256[] function abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr(offset, length, end) -> array { diff --git a/test/cmdlineTests/standard_optimizer_generatedSources/output.json b/test/cmdlineTests/standard_optimizer_generatedSources/output.json index 9e8c34405..439101cc4 100644 --- a/test/cmdlineTests/standard_optimizer_generatedSources/output.json +++ b/test/cmdlineTests/standard_optimizer_generatedSources/output.json @@ -1,4 +1,4 @@ -{"contracts":{"a.sol":{"A":{"evm":{"bytecode":{"generatedSources":[],"object":""},"deployedBytecode":{"generatedSources":[{"ast":{"nodeType":"YulBlock","src":"0:1456:1","statements":[{"nodeType":"YulBlock","src":"6:3:1","statements":[]},{"body":{"nodeType":"YulBlock","src":"109:1031:1","statements":[{"nodeType":"YulVariableDeclaration","src":"119:12:1","value":{"kind":"number","nodeType":"YulLiteral","src":"129:2:1","type":"","value":"32"},"variables":[{"name":"_1","nodeType":"YulTypedName","src":"123:2:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"176:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"185:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"188:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"178:6:1"},"nodeType":"YulFunctionCall","src":"178:12:1"},"nodeType":"YulExpressionStatement","src":"178:12:1"}]},"condition":{"arguments":[{"arguments":[{"name":"dataEnd","nodeType":"YulIdentifier","src":"151:7:1"},{"name":"headStart","nodeType":"YulIdentifier","src":"160:9:1"}],"functionName":{"name":"sub","nodeType":"YulIdentifier","src":"147:3:1"},"nodeType":"YulFunctionCall","src":"147:23:1"},{"name":"_1","nodeType":"YulIdentifier","src":"172:2:1"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"143:3:1"},"nodeType":"YulFunctionCall","src":"143:32:1"},"nodeType":"YulIf","src":"140:2:1"},{"nodeType":"YulVariableDeclaration","src":"201:37:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"228:9:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"215:12:1"},"nodeType":"YulFunctionCall","src":"215:23:1"},"variables":[{"name":"offset","nodeType":"YulTypedName","src":"205:6:1","type":""}]},{"nodeType":"YulVariableDeclaration","src":"247:28:1","value":{"kind":"number","nodeType":"YulLiteral","src":"257:18:1","type":"","value":"0xffffffffffffffff"},"variables":[{"name":"_2","nodeType":"YulTypedName","src":"251:2:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"302:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"311:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"314:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"304:6:1"},"nodeType":"YulFunctionCall","src":"304:12:1"},"nodeType":"YulExpressionStatement","src":"304:12:1"}]},"condition":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"290:6:1"},{"name":"_2","nodeType":"YulIdentifier","src":"298:2:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"287:2:1"},"nodeType":"YulFunctionCall","src":"287:14:1"},"nodeType":"YulIf","src":"284:2:1"},{"nodeType":"YulVariableDeclaration","src":"327:32:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"341:9:1"},{"name":"offset","nodeType":"YulIdentifier","src":"352:6:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"337:3:1"},"nodeType":"YulFunctionCall","src":"337:22:1"},"variables":[{"name":"_3","nodeType":"YulTypedName","src":"331:2:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"407:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"416:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"419:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"409:6:1"},"nodeType":"YulFunctionCall","src":"409:12:1"},"nodeType":"YulExpressionStatement","src":"409:12:1"}]},"condition":{"arguments":[{"arguments":[{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"386:2:1"},{"kind":"number","nodeType":"YulLiteral","src":"390:4:1","type":"","value":"0x1f"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"382:3:1"},"nodeType":"YulFunctionCall","src":"382:13:1"},{"name":"dataEnd","nodeType":"YulIdentifier","src":"397:7:1"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"378:3:1"},"nodeType":"YulFunctionCall","src":"378:27:1"}],"functionName":{"name":"iszero","nodeType":"YulIdentifier","src":"371:6:1"},"nodeType":"YulFunctionCall","src":"371:35:1"},"nodeType":"YulIf","src":"368:2:1"},{"nodeType":"YulVariableDeclaration","src":"432:26:1","value":{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"455:2:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"442:12:1"},"nodeType":"YulFunctionCall","src":"442:16:1"},"variables":[{"name":"_4","nodeType":"YulTypedName","src":"436:2:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"481:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"483:16:1"},"nodeType":"YulFunctionCall","src":"483:18:1"},"nodeType":"YulExpressionStatement","src":"483:18:1"}]},"condition":{"arguments":[{"name":"_4","nodeType":"YulIdentifier","src":"473:2:1"},{"name":"_2","nodeType":"YulIdentifier","src":"477:2:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"470:2:1"},"nodeType":"YulFunctionCall","src":"470:10:1"},"nodeType":"YulIf","src":"467:2:1"},{"nodeType":"YulVariableDeclaration","src":"512:20:1","value":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"526:1:1","type":"","value":"5"},{"name":"_4","nodeType":"YulIdentifier","src":"529:2:1"}],"functionName":{"name":"shl","nodeType":"YulIdentifier","src":"522:3:1"},"nodeType":"YulFunctionCall","src":"522:10:1"},"variables":[{"name":"_5","nodeType":"YulTypedName","src":"516:2:1","type":""}]},{"nodeType":"YulVariableDeclaration","src":"541:23:1","value":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"561:2:1","type":"","value":"64"}],"functionName":{"name":"mload","nodeType":"YulIdentifier","src":"555:5:1"},"nodeType":"YulFunctionCall","src":"555:9:1"},"variables":[{"name":"memPtr","nodeType":"YulTypedName","src":"545:6:1","type":""}]},{"nodeType":"YulVariableDeclaration","src":"573:56:1","value":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"595:6:1"},{"arguments":[{"arguments":[{"name":"_5","nodeType":"YulIdentifier","src":"611:2:1"},{"kind":"number","nodeType":"YulLiteral","src":"615:2:1","type":"","value":"63"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"607:3:1"},"nodeType":"YulFunctionCall","src":"607:11:1"},{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"624:2:1","type":"","value":"31"}],"functionName":{"name":"not","nodeType":"YulIdentifier","src":"620:3:1"},"nodeType":"YulFunctionCall","src":"620:7:1"}],"functionName":{"name":"and","nodeType":"YulIdentifier","src":"603:3:1"},"nodeType":"YulFunctionCall","src":"603:25:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"591:3:1"},"nodeType":"YulFunctionCall","src":"591:38:1"},"variables":[{"name":"newFreePtr","nodeType":"YulTypedName","src":"577:10:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"688:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"690:16:1"},"nodeType":"YulFunctionCall","src":"690:18:1"},"nodeType":"YulExpressionStatement","src":"690:18:1"}]},"condition":{"arguments":[{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"647:10:1"},{"name":"_2","nodeType":"YulIdentifier","src":"659:2:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"644:2:1"},"nodeType":"YulFunctionCall","src":"644:18:1"},{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"667:10:1"},{"name":"memPtr","nodeType":"YulIdentifier","src":"679:6:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"664:2:1"},"nodeType":"YulFunctionCall","src":"664:22:1"}],"functionName":{"name":"or","nodeType":"YulIdentifier","src":"641:2:1"},"nodeType":"YulFunctionCall","src":"641:46:1"},"nodeType":"YulIf","src":"638:2:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"726:2:1","type":"","value":"64"},{"name":"newFreePtr","nodeType":"YulIdentifier","src":"730:10:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"719:6:1"},"nodeType":"YulFunctionCall","src":"719:22:1"},"nodeType":"YulExpressionStatement","src":"719:22:1"},{"nodeType":"YulVariableDeclaration","src":"750:17:1","value":{"name":"memPtr","nodeType":"YulIdentifier","src":"761:6:1"},"variables":[{"name":"dst","nodeType":"YulTypedName","src":"754:3:1","type":""}]},{"expression":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"783:6:1"},{"name":"_4","nodeType":"YulIdentifier","src":"791:2:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"776:6:1"},"nodeType":"YulFunctionCall","src":"776:18:1"},"nodeType":"YulExpressionStatement","src":"776:18:1"},{"nodeType":"YulAssignment","src":"803:22:1","value":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"814:6:1"},{"name":"_1","nodeType":"YulIdentifier","src":"822:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"810:3:1"},"nodeType":"YulFunctionCall","src":"810:15:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"803:3:1"}]},{"nodeType":"YulVariableDeclaration","src":"834:22:1","value":{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"849:2:1"},{"name":"_1","nodeType":"YulIdentifier","src":"853:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"845:3:1"},"nodeType":"YulFunctionCall","src":"845:11:1"},"variables":[{"name":"src","nodeType":"YulTypedName","src":"838:3:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"902:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"911:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"914:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"904:6:1"},"nodeType":"YulFunctionCall","src":"904:12:1"},"nodeType":"YulExpressionStatement","src":"904:12:1"}]},"condition":{"arguments":[{"arguments":[{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"879:2:1"},{"name":"_5","nodeType":"YulIdentifier","src":"883:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"875:3:1"},"nodeType":"YulFunctionCall","src":"875:11:1"},{"name":"_1","nodeType":"YulIdentifier","src":"888:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"871:3:1"},"nodeType":"YulFunctionCall","src":"871:20:1"},{"name":"dataEnd","nodeType":"YulIdentifier","src":"893:7:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"868:2:1"},"nodeType":"YulFunctionCall","src":"868:33:1"},"nodeType":"YulIf","src":"865:2:1"},{"nodeType":"YulVariableDeclaration","src":"927:10:1","value":{"kind":"number","nodeType":"YulLiteral","src":"936:1:1","type":"","value":"0"},"variables":[{"name":"i","nodeType":"YulTypedName","src":"931:1:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"991:118:1","statements":[{"expression":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"1012:3:1"},{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"1030:3:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1017:12:1"},"nodeType":"YulFunctionCall","src":"1017:17:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1005:6:1"},"nodeType":"YulFunctionCall","src":"1005:30:1"},"nodeType":"YulExpressionStatement","src":"1005:30:1"},{"nodeType":"YulAssignment","src":"1048:19:1","value":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"1059:3:1"},{"name":"_1","nodeType":"YulIdentifier","src":"1064:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1055:3:1"},"nodeType":"YulFunctionCall","src":"1055:12:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"1048:3:1"}]},{"nodeType":"YulAssignment","src":"1080:19:1","value":{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"1091:3:1"},{"name":"_1","nodeType":"YulIdentifier","src":"1096:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1087:3:1"},"nodeType":"YulFunctionCall","src":"1087:12:1"},"variableNames":[{"name":"src","nodeType":"YulIdentifier","src":"1080:3:1"}]}]},"condition":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"957:1:1"},{"name":"_4","nodeType":"YulIdentifier","src":"960:2:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"954:2:1"},"nodeType":"YulFunctionCall","src":"954:9:1"},"nodeType":"YulForLoop","post":{"nodeType":"YulBlock","src":"964:18:1","statements":[{"nodeType":"YulAssignment","src":"966:14:1","value":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"975:1:1"},{"kind":"number","nodeType":"YulLiteral","src":"978:1:1","type":"","value":"1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"971:3:1"},"nodeType":"YulFunctionCall","src":"971:9:1"},"variableNames":[{"name":"i","nodeType":"YulIdentifier","src":"966:1:1"}]}]},"pre":{"nodeType":"YulBlock","src":"950:3:1","statements":[]},"src":"946:163:1"},{"nodeType":"YulAssignment","src":"1118:16:1","value":{"name":"memPtr","nodeType":"YulIdentifier","src":"1128:6:1"},"variableNames":[{"name":"value0","nodeType":"YulIdentifier","src":"1118:6:1"}]}]},"name":"abi_decode_tuple_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"75:9:1","type":""},{"name":"dataEnd","nodeType":"YulTypedName","src":"86:7:1","type":""}],"returnVariables":[{"name":"value0","nodeType":"YulTypedName","src":"98:6:1","type":""}],"src":"14:1126:1"},{"body":{"nodeType":"YulBlock","src":"1246:76:1","statements":[{"nodeType":"YulAssignment","src":"1256:26:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1268:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"1279:2:1","type":"","value":"32"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1264:3:1"},"nodeType":"YulFunctionCall","src":"1264:18:1"},"variableNames":[{"name":"tail","nodeType":"YulIdentifier","src":"1256:4:1"}]},{"expression":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1298:9:1"},{"name":"value0","nodeType":"YulIdentifier","src":"1309:6:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1291:6:1"},"nodeType":"YulFunctionCall","src":"1291:25:1"},"nodeType":"YulExpressionStatement","src":"1291:25:1"}]},"name":"abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"1215:9:1","type":""},{"name":"value0","nodeType":"YulTypedName","src":"1226:6:1","type":""}],"returnVariables":[{"name":"tail","nodeType":"YulTypedName","src":"1237:4:1","type":""}],"src":"1145:177:1"},{"body":{"nodeType":"YulBlock","src":"1359:95:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1376:1:1","type":"","value":"0"},{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1383:3:1","type":"","value":"224"},{"kind":"number","nodeType":"YulLiteral","src":"1388:10:1","type":"","value":"0x4e487b71"}],"functionName":{"name":"shl","nodeType":"YulIdentifier","src":"1379:3:1"},"nodeType":"YulFunctionCall","src":"1379:20:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1369:6:1"},"nodeType":"YulFunctionCall","src":"1369:31:1"},"nodeType":"YulExpressionStatement","src":"1369:31:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1416:1:1","type":"","value":"4"},{"kind":"number","nodeType":"YulLiteral","src":"1419:4:1","type":"","value":"0x41"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1409:6:1"},"nodeType":"YulFunctionCall","src":"1409:15:1"},"nodeType":"YulExpressionStatement","src":"1409:15:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1440:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"1443:4:1","type":"","value":"0x24"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"1433:6:1"},"nodeType":"YulFunctionCall","src":"1433:15:1"},"nodeType":"YulExpressionStatement","src":"1433:15:1"}]},"name":"panic_error_0x41","nodeType":"YulFunctionDefinition","src":"1327:127:1"}]},"contents":"{ +{"contracts":{"a.sol":{"A":{"evm":{"bytecode":{"generatedSources":[],"object":""},"deployedBytecode":{"generatedSources":[{"ast":{"nodeType":"YulBlock","src":"0:1456:1","statements":[{"nodeType":"YulBlock","src":"6:3:1","statements":[]},{"body":{"nodeType":"YulBlock","src":"109:1031:1","statements":[{"nodeType":"YulVariableDeclaration","src":"119:12:1","value":{"kind":"number","nodeType":"YulLiteral","src":"129:2:1","type":"","value":"32"},"variables":[{"name":"_1","nodeType":"YulTypedName","src":"123:2:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"176:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"185:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"188:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"178:6:1"},"nodeType":"YulFunctionCall","src":"178:12:1"},"nodeType":"YulExpressionStatement","src":"178:12:1"}]},"condition":{"arguments":[{"arguments":[{"name":"dataEnd","nodeType":"YulIdentifier","src":"151:7:1"},{"name":"headStart","nodeType":"YulIdentifier","src":"160:9:1"}],"functionName":{"name":"sub","nodeType":"YulIdentifier","src":"147:3:1"},"nodeType":"YulFunctionCall","src":"147:23:1"},{"name":"_1","nodeType":"YulIdentifier","src":"172:2:1"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"143:3:1"},"nodeType":"YulFunctionCall","src":"143:32:1"},"nodeType":"YulIf","src":"140:52:1"},{"nodeType":"YulVariableDeclaration","src":"201:37:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"228:9:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"215:12:1"},"nodeType":"YulFunctionCall","src":"215:23:1"},"variables":[{"name":"offset","nodeType":"YulTypedName","src":"205:6:1","type":""}]},{"nodeType":"YulVariableDeclaration","src":"247:28:1","value":{"kind":"number","nodeType":"YulLiteral","src":"257:18:1","type":"","value":"0xffffffffffffffff"},"variables":[{"name":"_2","nodeType":"YulTypedName","src":"251:2:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"302:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"311:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"314:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"304:6:1"},"nodeType":"YulFunctionCall","src":"304:12:1"},"nodeType":"YulExpressionStatement","src":"304:12:1"}]},"condition":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"290:6:1"},{"name":"_2","nodeType":"YulIdentifier","src":"298:2:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"287:2:1"},"nodeType":"YulFunctionCall","src":"287:14:1"},"nodeType":"YulIf","src":"284:34:1"},{"nodeType":"YulVariableDeclaration","src":"327:32:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"341:9:1"},{"name":"offset","nodeType":"YulIdentifier","src":"352:6:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"337:3:1"},"nodeType":"YulFunctionCall","src":"337:22:1"},"variables":[{"name":"_3","nodeType":"YulTypedName","src":"331:2:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"407:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"416:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"419:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"409:6:1"},"nodeType":"YulFunctionCall","src":"409:12:1"},"nodeType":"YulExpressionStatement","src":"409:12:1"}]},"condition":{"arguments":[{"arguments":[{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"386:2:1"},{"kind":"number","nodeType":"YulLiteral","src":"390:4:1","type":"","value":"0x1f"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"382:3:1"},"nodeType":"YulFunctionCall","src":"382:13:1"},{"name":"dataEnd","nodeType":"YulIdentifier","src":"397:7:1"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"378:3:1"},"nodeType":"YulFunctionCall","src":"378:27:1"}],"functionName":{"name":"iszero","nodeType":"YulIdentifier","src":"371:6:1"},"nodeType":"YulFunctionCall","src":"371:35:1"},"nodeType":"YulIf","src":"368:55:1"},{"nodeType":"YulVariableDeclaration","src":"432:26:1","value":{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"455:2:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"442:12:1"},"nodeType":"YulFunctionCall","src":"442:16:1"},"variables":[{"name":"_4","nodeType":"YulTypedName","src":"436:2:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"481:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"483:16:1"},"nodeType":"YulFunctionCall","src":"483:18:1"},"nodeType":"YulExpressionStatement","src":"483:18:1"}]},"condition":{"arguments":[{"name":"_4","nodeType":"YulIdentifier","src":"473:2:1"},{"name":"_2","nodeType":"YulIdentifier","src":"477:2:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"470:2:1"},"nodeType":"YulFunctionCall","src":"470:10:1"},"nodeType":"YulIf","src":"467:36:1"},{"nodeType":"YulVariableDeclaration","src":"512:20:1","value":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"526:1:1","type":"","value":"5"},{"name":"_4","nodeType":"YulIdentifier","src":"529:2:1"}],"functionName":{"name":"shl","nodeType":"YulIdentifier","src":"522:3:1"},"nodeType":"YulFunctionCall","src":"522:10:1"},"variables":[{"name":"_5","nodeType":"YulTypedName","src":"516:2:1","type":""}]},{"nodeType":"YulVariableDeclaration","src":"541:23:1","value":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"561:2:1","type":"","value":"64"}],"functionName":{"name":"mload","nodeType":"YulIdentifier","src":"555:5:1"},"nodeType":"YulFunctionCall","src":"555:9:1"},"variables":[{"name":"memPtr","nodeType":"YulTypedName","src":"545:6:1","type":""}]},{"nodeType":"YulVariableDeclaration","src":"573:56:1","value":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"595:6:1"},{"arguments":[{"arguments":[{"name":"_5","nodeType":"YulIdentifier","src":"611:2:1"},{"kind":"number","nodeType":"YulLiteral","src":"615:2:1","type":"","value":"63"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"607:3:1"},"nodeType":"YulFunctionCall","src":"607:11:1"},{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"624:2:1","type":"","value":"31"}],"functionName":{"name":"not","nodeType":"YulIdentifier","src":"620:3:1"},"nodeType":"YulFunctionCall","src":"620:7:1"}],"functionName":{"name":"and","nodeType":"YulIdentifier","src":"603:3:1"},"nodeType":"YulFunctionCall","src":"603:25:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"591:3:1"},"nodeType":"YulFunctionCall","src":"591:38:1"},"variables":[{"name":"newFreePtr","nodeType":"YulTypedName","src":"577:10:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"688:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"690:16:1"},"nodeType":"YulFunctionCall","src":"690:18:1"},"nodeType":"YulExpressionStatement","src":"690:18:1"}]},"condition":{"arguments":[{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"647:10:1"},{"name":"_2","nodeType":"YulIdentifier","src":"659:2:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"644:2:1"},"nodeType":"YulFunctionCall","src":"644:18:1"},{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"667:10:1"},{"name":"memPtr","nodeType":"YulIdentifier","src":"679:6:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"664:2:1"},"nodeType":"YulFunctionCall","src":"664:22:1"}],"functionName":{"name":"or","nodeType":"YulIdentifier","src":"641:2:1"},"nodeType":"YulFunctionCall","src":"641:46:1"},"nodeType":"YulIf","src":"638:72:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"726:2:1","type":"","value":"64"},{"name":"newFreePtr","nodeType":"YulIdentifier","src":"730:10:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"719:6:1"},"nodeType":"YulFunctionCall","src":"719:22:1"},"nodeType":"YulExpressionStatement","src":"719:22:1"},{"nodeType":"YulVariableDeclaration","src":"750:17:1","value":{"name":"memPtr","nodeType":"YulIdentifier","src":"761:6:1"},"variables":[{"name":"dst","nodeType":"YulTypedName","src":"754:3:1","type":""}]},{"expression":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"783:6:1"},{"name":"_4","nodeType":"YulIdentifier","src":"791:2:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"776:6:1"},"nodeType":"YulFunctionCall","src":"776:18:1"},"nodeType":"YulExpressionStatement","src":"776:18:1"},{"nodeType":"YulAssignment","src":"803:22:1","value":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"814:6:1"},{"name":"_1","nodeType":"YulIdentifier","src":"822:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"810:3:1"},"nodeType":"YulFunctionCall","src":"810:15:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"803:3:1"}]},{"nodeType":"YulVariableDeclaration","src":"834:22:1","value":{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"849:2:1"},{"name":"_1","nodeType":"YulIdentifier","src":"853:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"845:3:1"},"nodeType":"YulFunctionCall","src":"845:11:1"},"variables":[{"name":"src","nodeType":"YulTypedName","src":"838:3:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"902:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"911:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"914:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"904:6:1"},"nodeType":"YulFunctionCall","src":"904:12:1"},"nodeType":"YulExpressionStatement","src":"904:12:1"}]},"condition":{"arguments":[{"arguments":[{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"879:2:1"},{"name":"_5","nodeType":"YulIdentifier","src":"883:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"875:3:1"},"nodeType":"YulFunctionCall","src":"875:11:1"},{"name":"_1","nodeType":"YulIdentifier","src":"888:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"871:3:1"},"nodeType":"YulFunctionCall","src":"871:20:1"},{"name":"dataEnd","nodeType":"YulIdentifier","src":"893:7:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"868:2:1"},"nodeType":"YulFunctionCall","src":"868:33:1"},"nodeType":"YulIf","src":"865:53:1"},{"nodeType":"YulVariableDeclaration","src":"927:10:1","value":{"kind":"number","nodeType":"YulLiteral","src":"936:1:1","type":"","value":"0"},"variables":[{"name":"i","nodeType":"YulTypedName","src":"931:1:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"991:118:1","statements":[{"expression":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"1012:3:1"},{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"1030:3:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1017:12:1"},"nodeType":"YulFunctionCall","src":"1017:17:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1005:6:1"},"nodeType":"YulFunctionCall","src":"1005:30:1"},"nodeType":"YulExpressionStatement","src":"1005:30:1"},{"nodeType":"YulAssignment","src":"1048:19:1","value":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"1059:3:1"},{"name":"_1","nodeType":"YulIdentifier","src":"1064:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1055:3:1"},"nodeType":"YulFunctionCall","src":"1055:12:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"1048:3:1"}]},{"nodeType":"YulAssignment","src":"1080:19:1","value":{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"1091:3:1"},{"name":"_1","nodeType":"YulIdentifier","src":"1096:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1087:3:1"},"nodeType":"YulFunctionCall","src":"1087:12:1"},"variableNames":[{"name":"src","nodeType":"YulIdentifier","src":"1080:3:1"}]}]},"condition":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"957:1:1"},{"name":"_4","nodeType":"YulIdentifier","src":"960:2:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"954:2:1"},"nodeType":"YulFunctionCall","src":"954:9:1"},"nodeType":"YulForLoop","post":{"nodeType":"YulBlock","src":"964:18:1","statements":[{"nodeType":"YulAssignment","src":"966:14:1","value":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"975:1:1"},{"kind":"number","nodeType":"YulLiteral","src":"978:1:1","type":"","value":"1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"971:3:1"},"nodeType":"YulFunctionCall","src":"971:9:1"},"variableNames":[{"name":"i","nodeType":"YulIdentifier","src":"966:1:1"}]}]},"pre":{"nodeType":"YulBlock","src":"950:3:1","statements":[]},"src":"946:163:1"},{"nodeType":"YulAssignment","src":"1118:16:1","value":{"name":"memPtr","nodeType":"YulIdentifier","src":"1128:6:1"},"variableNames":[{"name":"value0","nodeType":"YulIdentifier","src":"1118:6:1"}]}]},"name":"abi_decode_tuple_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"75:9:1","type":""},{"name":"dataEnd","nodeType":"YulTypedName","src":"86:7:1","type":""}],"returnVariables":[{"name":"value0","nodeType":"YulTypedName","src":"98:6:1","type":""}],"src":"14:1126:1"},{"body":{"nodeType":"YulBlock","src":"1246:76:1","statements":[{"nodeType":"YulAssignment","src":"1256:26:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1268:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"1279:2:1","type":"","value":"32"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1264:3:1"},"nodeType":"YulFunctionCall","src":"1264:18:1"},"variableNames":[{"name":"tail","nodeType":"YulIdentifier","src":"1256:4:1"}]},{"expression":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1298:9:1"},{"name":"value0","nodeType":"YulIdentifier","src":"1309:6:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1291:6:1"},"nodeType":"YulFunctionCall","src":"1291:25:1"},"nodeType":"YulExpressionStatement","src":"1291:25:1"}]},"name":"abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"1215:9:1","type":""},{"name":"value0","nodeType":"YulTypedName","src":"1226:6:1","type":""}],"returnVariables":[{"name":"tail","nodeType":"YulTypedName","src":"1237:4:1","type":""}],"src":"1145:177:1"},{"body":{"nodeType":"YulBlock","src":"1359:95:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1376:1:1","type":"","value":"0"},{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1383:3:1","type":"","value":"224"},{"kind":"number","nodeType":"YulLiteral","src":"1388:10:1","type":"","value":"0x4e487b71"}],"functionName":{"name":"shl","nodeType":"YulIdentifier","src":"1379:3:1"},"nodeType":"YulFunctionCall","src":"1379:20:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1369:6:1"},"nodeType":"YulFunctionCall","src":"1369:31:1"},"nodeType":"YulExpressionStatement","src":"1369:31:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1416:1:1","type":"","value":"4"},{"kind":"number","nodeType":"YulLiteral","src":"1419:4:1","type":"","value":"0x41"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1409:6:1"},"nodeType":"YulFunctionCall","src":"1409:15:1"},"nodeType":"YulExpressionStatement","src":"1409:15:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1440:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"1443:4:1","type":"","value":"0x24"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"1433:6:1"},"nodeType":"YulFunctionCall","src":"1433:15:1"},"nodeType":"YulExpressionStatement","src":"1433:15:1"}]},"name":"panic_error_0x41","nodeType":"YulFunctionDefinition","src":"1327:127:1"}]},"contents":"{ { } function abi_decode_tuple_t_array$_t_uint256_$dyn_memory_ptr(headStart, dataEnd) -> value0 { diff --git a/test/cmdlineTests/strict_asm_optimizer_steps/output b/test/cmdlineTests/strict_asm_optimizer_steps/output index 1ed63ba66..bf9f34e7a 100644 --- a/test/cmdlineTests/strict_asm_optimizer_steps/output +++ b/test/cmdlineTests/strict_asm_optimizer_steps/output @@ -36,7 +36,7 @@ Text representation: mstore /* "strict_asm_optimizer_steps/input.yul":61:72 */ callvalue - /* "strict_asm_optimizer_steps/input.yul":58:60 */ + /* "strict_asm_optimizer_steps/input.yul":58:89 */ iszero tag_1 jumpi @@ -46,7 +46,7 @@ Text representation: dup1 /* "strict_asm_optimizer_steps/input.yul":75:87 */ revert - /* "strict_asm_optimizer_steps/input.yul":58:60 */ + /* "strict_asm_optimizer_steps/input.yul":58:89 */ tag_1: /* "strict_asm_optimizer_steps/input.yul":138:162 */ dataSize(sub_0) diff --git a/test/libyul/objectCompiler/jump_tags.yul b/test/libyul/objectCompiler/jump_tags.yul index 93aea8ea0..b78173d18 100644 --- a/test/libyul/objectCompiler/jump_tags.yul +++ b/test/libyul/objectCompiler/jump_tags.yul @@ -28,14 +28,14 @@ object "Contract" { // tag_5: // /* "source":78:79 */ // dup1 -// /* "source":75:77 */ +// /* "source":75:89 */ // iszero // tag_7 // jumpi // /* "source":82:87 */ // pop // jump(tag_6) -// /* "source":75:77 */ +// /* "source":75:89 */ // tag_7: // /* "source":90:102 */ // tag_8 @@ -64,4 +64,4 @@ object "Contract" { // tag_9: // Bytecode: 6026565b600b6001600e565b5b565b8015601857506024565b602260028201600e565b505b565b602e6001600e565b // Opcodes: PUSH1 0x26 JUMP JUMPDEST PUSH1 0xB PUSH1 0x1 PUSH1 0xE JUMP JUMPDEST JUMPDEST JUMP JUMPDEST DUP1 ISZERO PUSH1 0x18 JUMPI POP PUSH1 0x24 JUMP JUMPDEST PUSH1 0x22 PUSH1 0x2 DUP3 ADD PUSH1 0xE JUMP JUMPDEST POP JUMPDEST JUMP JUMPDEST PUSH1 0x2E PUSH1 0x1 PUSH1 0xE JUMP JUMPDEST -// SourceMappings: 33:21:0:-:0;;;48:4;50:1;48:4;:::i;:::-;33:21;:::o;59:45::-;78:1;75:2;;;82:5;;;75:2;90:12;99:1;96;92:9;90:12;:::i;:::-;59:45;;:::o;:::-;109:4;111:1;109:4;:::i;:::- +// SourceMappings: 33:21:0:-:0;;;48:4;50:1;48:4;:::i;:::-;33:21;:::o;59:45::-;78:1;75:14;;;82:5;;;75:14;90:12;99:1;96;92:9;90:12;:::i;:::-;59:45;;:::o;:::-;109:4;111:1;109:4;:::i;:::- From 510d74c1cd4e993f3d7e763e3380e42d8f3911ea Mon Sep 17 00:00:00 2001 From: Tony <78368735+maAPPsDEV@users.noreply.github.com> Date: Wed, 7 Jul 2021 19:29:44 -0400 Subject: [PATCH 18/98] Fix a Typo describing Layout in Storage --- docs/internals/layout_in_storage.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/internals/layout_in_storage.rst b/docs/internals/layout_in_storage.rst index a9de8f6c9..b1b89a6a3 100644 --- a/docs/internals/layout_in_storage.rst +++ b/docs/internals/layout_in_storage.rst @@ -127,7 +127,7 @@ The type of the value is ``uint256``, so it uses a single slot. ------------------------ ``bytes`` and ``string`` are encoded identically. -In general, the encoding is similar to ``byte1[]``, in the sense that there is a slot for the array itself and +In general, the encoding is similar to ``bytes1[]``, in the sense that there is a slot for the array itself and a data area that is computed using a ``keccak256`` hash of that slot's position. However, for short values (shorter than 32 bytes) the array elements are stored together with the length in the same slot. From 0a6fc89da2e09b0ccf23196b7a97e71013aba7e0 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 7 Jul 2021 20:20:06 +0200 Subject: [PATCH 19/98] Fix negation function name collision. --- libsolidity/codegen/YulUtilFunctions.cpp | 2 +- .../externalContracts/prbmath_signed.sol | 6 +++--- .../semanticTests/viaYul/negation_bug.yul | 13 +++++++++++++ 3 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 test/libsolidity/semanticTests/viaYul/negation_bug.yul diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index d7c31a72b..bd86dc9c2 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -4029,7 +4029,7 @@ string YulUtilFunctions::negateNumberWrappingFunction(Type const& _type) IntegerType const& type = dynamic_cast(_type); solAssert(type.isSigned(), "Expected signed type!"); - string const functionName = "negate_" + _type.identifier(); + string const functionName = "negate_wrapping_" + _type.identifier(); return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (value) -> ret { diff --git a/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol b/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol index 2936cf86f..77be50301 100644 --- a/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol +++ b/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol @@ -50,11 +50,11 @@ contract test { // compileViaYul: also // ---- // constructor() -// gas irOptimized: 1947094 +// gas irOptimized: 1965559 // gas legacy: 2602700 // gas legacyOptimized: 1874490 // div(int256,int256): 3141592653589793238, 88714123 -> 35412542528203691288251815328 -// gas irOptimized: 22222 +// gas irOptimized: 22244 // gas legacy: 22767 // gas legacyOptimized: 22282 // exp(int256): 3141592653589793238 -> 23140692632779268978 @@ -82,7 +82,7 @@ contract test { // gas legacy: 22807 // gas legacyOptimized: 22295 // pow(int256,uint256): 3141592653589793238, 5 -> 306019684785281453040 -// gas irOptimized: 22863 +// gas irOptimized: 22861 // gas legacy: 23508 // gas legacyOptimized: 22921 // sqrt(int256): 3141592653589793238 -> 1772453850905516027 diff --git a/test/libsolidity/semanticTests/viaYul/negation_bug.yul b/test/libsolidity/semanticTests/viaYul/negation_bug.yul new file mode 100644 index 000000000..6174eadd1 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/negation_bug.yul @@ -0,0 +1,13 @@ +contract C { + function f() public pure { + -(int8(0)); + unchecked { + // Used to incorrectly use the checked unary negation function and revert. + (-(type(int8).min)); + } + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> From 9a0da17a6d6509e53c25a3d2b4301f2fc3c26e94 Mon Sep 17 00:00:00 2001 From: Marenz Date: Wed, 7 Jul 2021 13:34:33 +0200 Subject: [PATCH 20/98] Use more readable and maintainable multiline strings in py test script --- test/scripts/test_isolate_tests.py | 71 ++++++++++++++++-------------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/test/scripts/test_isolate_tests.py b/test/scripts/test_isolate_tests.py index 15c375a82..a62a528a2 100644 --- a/test/scripts/test_isolate_tests.py +++ b/test/scripts/test_isolate_tests.py @@ -2,6 +2,8 @@ import unittest +from textwrap import dedent, indent + from unittest_helpers import FIXTURE_DIR, load_fixture # NOTE: This test file file only works with scripts/ added to PYTHONPATH so pylint can't find the imports @@ -9,54 +11,59 @@ from unittest_helpers import FIXTURE_DIR, load_fixture from isolate_tests import extract_docs_cases # pragma pylint: enable=import-error - CODE_BLOCK_RST_PATH = FIXTURE_DIR / 'code_block.rst' CODE_BLOCK_RST_CONTENT = load_fixture(CODE_BLOCK_RST_PATH) CODE_BLOCK_WITH_DIRECTIVES_RST_PATH = FIXTURE_DIR / 'code_block_with_directives.rst' CODE_BLOCK_WITH_DIRECTIVES_RST_CONTENT = load_fixture(CODE_BLOCK_WITH_DIRECTIVES_RST_PATH) +def formatCase(text): + """Formats code to contain only one indentation and terminate with a \n""" + return indent(dedent(text.lstrip("\n")), " ") + "\n" class TestExtractDocsCases(unittest.TestCase): def setUp(self): self.maxDiff = 10000 - def test_solidity_block(self): - expected_cases = [ - " // SPDX-License-Identifier: GPL-3.0\n" - " pragma solidity >=0.7.0 <0.9.0;\n" - "\n" - " contract C {\n" - " function foo() public view {}\n" - " }\n" - "\n" - "\n", - " contract C {}\n" - "\n", - ] + def test_solidity_block(self): + expected_cases = [formatCase(case) for case in [ + """ + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.7.0 <0.9.0; + + contract C { + function foo() public view {} + } + + """, + """ + contract C {} + """, + ]] self.assertEqual(extract_docs_cases(CODE_BLOCK_RST_PATH), expected_cases) def test_solidity_block_with_directives(self): - expected_cases = [ - " // SPDX-License-Identifier: GPL-3.0\n" - " pragma solidity >=0.7.0 <0.9.0;\n" - "\n" - " contract C {\n" - " function foo() public view {}\n" - " }\n" - "\n" - "\n", + expected_cases = [formatCase(case) for case in [ + """ + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.7.0 <0.9.0; - " contract C {}\n" - "\n", + contract C { + function foo() public view {} + } - " contract D {}\n" - " :linenos:\n" - "\n", - - " contract E {}\n" - "\n", - ] + """, + """ + contract C {} + """, + """ + contract D {} + :linenos: + """, + """ + contract E {} + """, + ]] self.assertEqual(extract_docs_cases(CODE_BLOCK_WITH_DIRECTIVES_RST_PATH), expected_cases) From ddccd73a4b8f7748f808bf0302773c6be671f253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 8 Jul 2021 13:04:00 +0200 Subject: [PATCH 21/98] Fix outdated references to `byte[]` type (it's now `bytes1[]`) --- docs/assembly.rst | 2 +- docs/internals/layout_in_memory.rst | 2 +- docs/types/reference-types.rst | 6 +++--- docs/types/value-types.rst | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/assembly.rst b/docs/assembly.rst index 4e35ca702..9da8ebb8b 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -234,7 +234,7 @@ This means that the allocatable memory starts at ``0x80``, which is the initial of the free memory pointer. Elements in memory arrays in Solidity always occupy multiples of 32 bytes (this is -even true for ``byte[]``, but not for ``bytes`` and ``string``). Multi-dimensional memory +even true for ``bytes1[]``, but not for ``bytes`` and ``string``). Multi-dimensional memory arrays are pointers to memory arrays. The length of a dynamic array is stored at the first slot of the array and followed by the array elements. diff --git a/docs/internals/layout_in_memory.rst b/docs/internals/layout_in_memory.rst index 66623572f..8c20b6252 100644 --- a/docs/internals/layout_in_memory.rst +++ b/docs/internals/layout_in_memory.rst @@ -19,7 +19,7 @@ Solidity always places new objects at the free memory pointer and memory is never freed (this might change in the future). Elements in memory arrays in Solidity always occupy multiples of 32 bytes (this -is even true for ``byte[]``, but not for ``bytes`` and ``string``). +is even true for ``bytes1[]``, but not for ``bytes`` and ``string``). Multi-dimensional memory arrays are pointers to memory arrays. The length of a dynamic array is stored at the first slot of the array and followed by the array elements. diff --git a/docs/types/reference-types.rst b/docs/types/reference-types.rst index b6a36fc6e..7b1b541f3 100644 --- a/docs/types/reference-types.rst +++ b/docs/types/reference-types.rst @@ -139,7 +139,7 @@ a reference to it. ``bytes`` and ``string`` as Arrays ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Variables of type ``bytes`` and ``string`` are special arrays. A ``bytes`` is similar to ``byte[]``, +Variables of type ``bytes`` and ``string`` are special arrays. The ``bytes`` type is similar to ``bytes1[]``, but it is packed tightly in calldata and memory. ``string`` is equal to ``bytes`` but does not allow length or index access. @@ -148,8 +148,8 @@ third-party string libraries. You can also compare two strings by their keccak25 ``keccak256(abi.encodePacked(s1)) == keccak256(abi.encodePacked(s2))`` and concatenate two strings using ``bytes.concat(bytes(s1), bytes(s2))``. -You should use ``bytes`` over ``byte[]`` because it is cheaper, -since ``byte[]`` adds 31 padding bytes between the elements. As a general rule, +You should use ``bytes`` over ``bytes1[]`` because it is cheaper, +since ``bytes1[]`` adds 31 padding bytes between the elements. As a general rule, use ``bytes`` for arbitrary-length raw byte data and ``string`` for arbitrary-length string (UTF-8) data. If you can limit the length to a certain number of bytes, always use one of the value types ``bytes1`` to ``bytes32`` because they are much cheaper. diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index d6a30d106..1e2c3abc2 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -398,7 +398,7 @@ Members: * ``.length`` yields the fixed length of the byte array (read-only). .. note:: - The type ``byte[]`` is an array of bytes, but due to padding rules, it wastes + The type ``bytes1[]`` is an array of bytes, but due to padding rules, it wastes 31 bytes of space for each element (except in storage). It is better to use the ``bytes`` type instead. From a1ca12a0a8c7932feeaade55c6fefc28394f7277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 8 Jul 2021 13:00:02 +0200 Subject: [PATCH 22/98] Clarify that non-byte array struct members are omitted by getters --- docs/contracts/visibility-and-getters.rst | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/docs/contracts/visibility-and-getters.rst b/docs/contracts/visibility-and-getters.rst index a865f54d4..f04945cfd 100644 --- a/docs/contracts/visibility-and-getters.rst +++ b/docs/contracts/visibility-and-getters.rst @@ -188,16 +188,24 @@ The next example is more complex: uint a; bytes3 b; mapping (uint => uint) map; + uint[3] c; + uint[] d; + bytes e; } mapping (uint => mapping(bool => Data[])) public data; } -It generates a function of the following form. The mapping in the struct is omitted -because there is no good way to provide the key for the mapping: +It generates a function of the following form. The mapping and arrays (with the +exception of byte arrays) in the struct are omitted because there is no good way +to select individual struct members or provide a key for the mapping: .. code-block:: solidity - function data(uint arg1, bool arg2, uint arg3) public returns (uint a, bytes3 b) { + function data(uint arg1, bool arg2, uint arg3) + public + returns (uint a, bytes3 b, bytes memory e) + { a = data[arg1][arg2][arg3].a; b = data[arg1][arg2][arg3].b; + e = data[arg1][arg2][arg3].e; } From d844d84b516879d1d662bebf94a43f2c81a54ae3 Mon Sep 17 00:00:00 2001 From: Marenz Date: Mon, 5 Jul 2021 19:38:41 +0200 Subject: [PATCH 23/98] Test yul code blocks in documentation. --- docs/internals/optimizer.rst | 20 ++-- docs/yul.rst | 15 +-- scripts/common_cmdline.sh | 11 +- scripts/isolate_tests.py | 101 ++++++++++++------ test/cmdlineTests.sh | 2 +- test/scripts/fixtures/code_block.rst | 22 ++++ .../fixtures/code_block_with_directives.rst | 23 ++++ test/scripts/test_isolate_tests.py | 57 +++++++++- 8 files changed, 199 insertions(+), 52 deletions(-) diff --git a/docs/internals/optimizer.rst b/docs/internals/optimizer.rst index c1107d1b4..142a89602 100644 --- a/docs/internals/optimizer.rst +++ b/docs/internals/optimizer.rst @@ -519,7 +519,7 @@ compact again at the end. ExpressionSplitter ^^^^^^^^^^^^^^^^^^ -The expression splitter turns expressions like ``add(mload(x), mul(mload(y), 0x20))`` +The expression splitter turns expressions like ``add(mload(0x123), mul(mload(0x456), 0x20))`` into a sequence of declarations of unique variables that are assigned sub-expressions of that expression so that each function call has only variables or literals as arguments. @@ -529,9 +529,9 @@ The above would be transformed into .. code-block:: yul { - let _1 := mload(y) + let _1 := mload(0x123) let _2 := mul(_1, 0x20) - let _3 := mload(x) + let _3 := mload(0x456) let z := add(_3, _2) } @@ -633,7 +633,7 @@ The SSA transform converts this snippet to the following: { let a_1 := 1 - a := a_1 + let a := a_1 let a_2 := mload(a_1) a := a_2 let a_3 := sload(a_2) @@ -1186,16 +1186,18 @@ The SSA transform rewrites .. code-block:: yul - a := E + let a := calldataload(0) mstore(a, 1) to .. code-block:: yul - let a_1 := E - a := a_1 + let a_1 := calldataload(0) + let a := a_1 mstore(a_1, 1) + let a_2 := calldataload(0x20) + a := a_2 The problem is that instead of ``a``, the variable ``a_1`` is used whenever ``a`` was referenced. The SSA transform changes statements @@ -1204,9 +1206,11 @@ snippet is turned into .. code-block:: yul - a := E + let a := calldataload(0) let a_1 := a mstore(a_1, 1) + a := calldataload(0x20) + let a_2 := a This is a very simple equivalence transform, but when we now run the Common Subexpression Eliminator, it will replace all occurrences of ``a_1`` diff --git a/docs/yul.rst b/docs/yul.rst index 55e8b824f..093bf6597 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -198,7 +198,8 @@ has to be specified after a colon: .. code-block:: yul - let x := and("abc":uint32, add(3:uint256, 2:uint256)) + // This will not compile (u32 and u256 type not implemented yet) + let x := and("abc":u32, add(3:u256, 2:u256)) Function Calls @@ -212,10 +213,9 @@ they have to be assigned to local variables. .. code-block:: yul + function f(x, y) -> a, b { /* ... */ } mstore(0x80, add(mload(0x80), 3)) - // Here, the user-defined function `f` returns - // two values. The definition of the function - // is missing from the example. + // Here, the user-defined function `f` returns two values. let x, y := f(1, mload(0)) For built-in functions of the EVM, functional expressions @@ -271,9 +271,10 @@ that returns multiple values. .. code-block:: yul + // This will not compile (u32 and u256 type not implemented yet) { - let zero:uint32 := 0:uint32 - let v:uint256, t:uint32 := f() + let zero:u32 := 0:u32 + let v:u256, t:u32 := f() let x, y := g() } @@ -314,7 +315,7 @@ you need multiple alternatives. .. code-block:: yul - if eq(value, 0) { revert(0, 0) } + if lt(calldatasize(), 4) { revert(0, 0) } The curly braces for the body are required. diff --git a/scripts/common_cmdline.sh b/scripts/common_cmdline.sh index 2637fdedb..88dab9cd0 100644 --- a/scripts/common_cmdline.sh +++ b/scripts/common_cmdline.sh @@ -19,6 +19,7 @@ # (c) 2016-2019 solidity contributors. # ------------------------------------------------------------------------------ +YULARGS=(--strict-assembly) FULLARGS=(--optimize --ignore-missing --combined-json "abi,asm,ast,bin,bin-runtime,compact-format,devdoc,hashes,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc") OLDARGS=(--optimize --combined-json "abi,asm,ast,bin,bin-runtime,devdoc,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc") function compileFull() @@ -53,10 +54,18 @@ function compileFull() local stderr_path; stderr_path=$(mktemp) + if [ "${files: -4}" == ".yul" ] + then + args=("${YULARGS[@]}") + fi + set +e "$SOLC" "${args[@]}" "${files[@]}" >/dev/null 2>"$stderr_path" local exit_code=$? - local errors; errors=$(grep -v -E 'Warning: This is a pre-release compiler version|Warning: Experimental features are turned on|pragma experimental ABIEncoderV2|^ +--> |^ +\||^[0-9]+ +\|' < "$stderr_path") + local errors; errors=$(grep -v -E \ + -e 'Warning: This is a pre-release compiler version|Warning: Experimental features are turned on|pragma experimental ABIEncoderV2|^ +--> |^ +\||^[0-9]+ +\| ' \ + -e 'Warning: Yul is still experimental. Please use the output with care.' < "$stderr_path") + set -e rm "$stderr_path" diff --git a/scripts/isolate_tests.py b/scripts/isolate_tests.py index fdb398c4b..f3a121f3d 100755 --- a/scripts/isolate_tests.py +++ b/scripts/isolate_tests.py @@ -10,8 +10,9 @@ import sys import re import os import hashlib -from os.path import join, isfile, split, basename +from os.path import join, isfile, basename from argparse import ArgumentParser +from textwrap import indent, dedent def extract_test_cases(path): with open(path, encoding="utf8", errors='ignore', mode='r', newline='') as file: @@ -36,11 +37,42 @@ def extract_test_cases(path): return tests -# Extract code examples based on a start marker +def extract_solidity_docs_cases(path): + tests = extract_docs_cases(path, [".. code-block:: solidity", '::']) + + codeStart = "(// SPDX-License-Identifier:|pragma solidity|contract.*{|library.*{|interface.*{)" + + # Filter out tests that are not supposed to be compilable. + return [ + test.lstrip("\n") + for test in tests + if re.search(r'^\s{4}' + codeStart, test, re.MULTILINE) is not None + ] + +def extract_yul_docs_cases(path): + tests = extract_docs_cases(path, [".. code-block:: yul"]) + + def wrap_in_object(code): + for line in code.splitlines(): + line = line.lstrip() + if line.startswith("//"): + continue + if not line.startswith("object") and not line.startswith("{"): + return indent("{{\n{}\n}}\n\n".format(code.rstrip()), " ") + break + + return code + + return [ + wrap_in_object(test) + for test in tests + if test.strip() != "" + ] + +# Extract code examples based on the 'beginMarker' parameter # up until we reach EOF or a line that is not empty and doesn't start with 4 # spaces. -def extract_docs_cases(path): - beginMarkers = ['.. code-block:: solidity', '::'] +def extract_docs_cases(path, beginMarkers): immediatelyAfterMarker = False insideBlock = False tests = [] @@ -59,48 +91,45 @@ def extract_docs_cases(path): if line == '' or line.startswith(" "): tests[-1] += line + "\n" immediatelyAfterMarker = False - else: - insideBlock = False - elif any(map(line.lower().startswith, beginMarkers)): + continue + + insideBlock = False + if any(map(line.lower().startswith, beginMarkers)): insideBlock = True immediatelyAfterMarker = True tests += [''] - codeStart = "(// SPDX-License-Identifier:|pragma solidity|contract.*{|library.*{|interface.*{)" + return tests - for test in tests: - if re.search(r'^\s{0,3}' + codeStart, test, re.MULTILINE): - print("Indentation error in " + path + ":") - print(test) - exit(1) - - # Filter out tests that are not supposed to be compilable. - return [ - test.lstrip("\n") - for test in tests - if re.search(r'^\s{4}' + codeStart, test, re.MULTILINE) is not None - ] - -def write_cases(f, tests): +def write_cases(f, solidityTests, yulTests): cleaned_filename = f.replace(".","_").replace("-","_").replace(" ","_").lower() - for test in tests: + for language, test in [("sol", t) for t in solidityTests] + [("yul", t) for t in yulTests]: # When code examples are extracted they are indented by 8 spaces, which violates the style guide, # so before checking remove 4 spaces from each line. - remainder = re.sub(r'^ {4}', '', test, 0, re.MULTILINE) - sol_filename = 'test_%s_%s.sol' % (hashlib.sha256(test.encode("utf-8")).hexdigest(), cleaned_filename) + remainder = dedent(test) + sol_filename = 'test_%s_%s.%s' % (hashlib.sha256(test.encode("utf-8")).hexdigest(), cleaned_filename, language) with open(sol_filename, mode='w', encoding='utf8', newline='') as fi: fi.write(remainder) -def extract_and_write(path): +def extract_and_write(path, language): + assert language in ["solidity", "yul", ""] + yulCases = [] + cases = [] + if path.lower().endswith('.rst'): - cases = extract_docs_cases(path) + if language in ("solidity", ""): + cases = extract_solidity_docs_cases(path) + + if language in ("yul", ""): + yulCases = extract_yul_docs_cases(path) elif path.endswith('.sol'): - with open(path, mode='r', encoding='utf8', newline='') as f: - cases = [f.read()] + if language in ("solidity", ""): + with open(path, mode='r', encoding='utf8', newline='') as f: + cases = [f.read()] else: cases = extract_test_cases(path) - write_cases(basename(path), cases) + write_cases(basename(path), cases, yulCases) if __name__ == '__main__': script_description = ( @@ -110,11 +139,19 @@ if __name__ == '__main__': parser = ArgumentParser(description=script_description) parser.add_argument(dest='path', help='Path to file or directory to look for code in.') + parser.add_argument( + '-l', '--language', + dest='language', + choices=["yul", "solidity"], + default="", + action='store', + help="Extract only code blocks in the given language" + ) options = parser.parse_args() path = options.path if isfile(path): - extract_and_write(path) + extract_and_write(path, options.language) else: for root, subdirs, files in os.walk(path): if '_build' in subdirs: @@ -125,4 +162,4 @@ if __name__ == '__main__': if basename(f) == "invalid_utf8_sequence.sol": continue # ignore the test with broken utf-8 encoding path = join(root, f) - extract_and_write(path) + extract_and_write(path, options.language) diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index 7a14fe6c8..a864ce17a 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -364,7 +364,7 @@ SOLTMPDIR=$(mktemp -d) "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ developmentVersion=$("$REPO_ROOT/scripts/get_version.sh") - for f in *.sol + for f in *.yul *.sol do # The contributors guide uses syntax tests, but we cannot # really handle them here. diff --git a/test/scripts/fixtures/code_block.rst b/test/scripts/fixtures/code_block.rst index f97967b93..a0bb7a91e 100644 --- a/test/scripts/fixtures/code_block.rst +++ b/test/scripts/fixtures/code_block.rst @@ -20,3 +20,25 @@ Some text contract C {} More text. + +.. code-block:: yul + + let x := add(1, 5) + +.. code-block:: yul + + // Yul code wrapped in object + { + { + let y := mul(3, 5) + } + } + +.. code-block:: yul + // Yul code wrapped in named object + object "Test" { + { + let y := mul(6, 9) + } + } + diff --git a/test/scripts/fixtures/code_block_with_directives.rst b/test/scripts/fixtures/code_block_with_directives.rst index ff52d5b0e..fba8ed7dd 100644 --- a/test/scripts/fixtures/code_block_with_directives.rst +++ b/test/scripts/fixtures/code_block_with_directives.rst @@ -46,3 +46,26 @@ Sphinx does not complain about these. contract E {} More text. + +.. code-block:: yul + + :force: + let x := add(1, 5) + +.. code-block:: yul + + :linenos: + :language: Yul + // Yul code wrapped in object + { + let y := mul(3, 5) + } + +.. code-block:: yul + + // Yul code wrapped in named object + object "Test" { + let y := mul(3, 5) + :linenos: + } + diff --git a/test/scripts/test_isolate_tests.py b/test/scripts/test_isolate_tests.py index a62a528a2..16874b747 100644 --- a/test/scripts/test_isolate_tests.py +++ b/test/scripts/test_isolate_tests.py @@ -8,7 +8,7 @@ from unittest_helpers import FIXTURE_DIR, load_fixture # NOTE: This test file file only works with scripts/ added to PYTHONPATH so pylint can't find the imports # pragma pylint: disable=import-error -from isolate_tests import extract_docs_cases +from isolate_tests import extract_solidity_docs_cases, extract_yul_docs_cases # pragma pylint: enable=import-error CODE_BLOCK_RST_PATH = FIXTURE_DIR / 'code_block.rst' @@ -41,7 +41,7 @@ class TestExtractDocsCases(unittest.TestCase): """, ]] - self.assertEqual(extract_docs_cases(CODE_BLOCK_RST_PATH), expected_cases) + self.assertEqual(extract_solidity_docs_cases(CODE_BLOCK_RST_PATH), expected_cases) def test_solidity_block_with_directives(self): expected_cases = [formatCase(case) for case in [ @@ -66,4 +66,55 @@ class TestExtractDocsCases(unittest.TestCase): """, ]] - self.assertEqual(extract_docs_cases(CODE_BLOCK_WITH_DIRECTIVES_RST_PATH), expected_cases) + self.assertEqual(extract_solidity_docs_cases(CODE_BLOCK_WITH_DIRECTIVES_RST_PATH), expected_cases) + + def test_yul_block(self): + expected_cases = [formatCase(case) for case in [ + """ + { + let x := add(1, 5) + } + """, + """ + // Yul code wrapped in object + { + { + let y := mul(3, 5) + } + } + """, + """ + // Yul code wrapped in named object + object "Test" { + { + let y := mul(6, 9) + } + } + """, + ]] + + self.assertEqual(extract_yul_docs_cases(CODE_BLOCK_RST_PATH), expected_cases) + + def test_yul_block_with_directives(self): + expected_cases = [formatCase(case) for case in [ + """ + { + let x := add(1, 5) + } + """, + """ + // Yul code wrapped in object + { + let y := mul(3, 5) + } + """, + """ + // Yul code wrapped in named object + object "Test" { + let y := mul(3, 5) + :linenos: + } + """, + ]] + + self.assertEqual(extract_yul_docs_cases(CODE_BLOCK_WITH_DIRECTIVES_RST_PATH), expected_cases) From 43cde4e1750d713b262a6cd966db1a1debd160b7 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Mon, 14 Jun 2021 12:03:53 +0200 Subject: [PATCH 24/98] Adds missing include header (for DebugData). --- libyul/AsmParser.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libyul/AsmParser.h b/libyul/AsmParser.h index 896cb6d51..d010dc99d 100644 --- a/libyul/AsmParser.h +++ b/libyul/AsmParser.h @@ -23,6 +23,7 @@ #pragma once +#include #include #include From 132fa46faa80641088dec7e53c4cb99023b47ba1 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Wed, 16 Jun 2021 12:38:34 +0200 Subject: [PATCH 25/98] Yul: Adds parsing @src comment in AsmParser to customize the AST's sourcer locations. --- libsolidity/interface/CompilerStack.cpp | 14 +- libsolidity/interface/CompilerStack.h | 4 + libyul/AsmParser.cpp | 102 ++++++- libyul/AsmParser.h | 38 ++- scripts/error_codes.py | 3 + test/libyul/Parser.cpp | 369 +++++++++++++++++++++++- 6 files changed, 513 insertions(+), 17 deletions(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 0aeb88d1e..6f86c3cb5 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -80,7 +80,6 @@ #include #include -#include #include @@ -927,6 +926,19 @@ map CompilerStack::sourceIndices() const return indices; } +map> CompilerStack::indicesToCharStreams() const +{ + map> result; + unsigned index = 0; + for (auto const& s: m_sources) + result[index++] = s.second.scanner->charStream(); + + // NB: CompilerContext::yulUtilityFileName() does not have a source, + result[index++] = shared_ptr{}; + + return result; +} + Json::Value const& CompilerStack::contractABI(string const& _contractName) const { if (m_stackState < AnalysisPerformed) diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 44daa6381..9459d7cfa 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -239,6 +239,10 @@ public: /// by sourceNames(). std::map sourceIndices() const; + /// @returns the reverse mapping of source indices to their respective + /// CharStream instances. + std::map> indicesToCharStreams() const; + /// @returns the previously used scanner, useful for counting lines during error reporting. langutil::Scanner const& scanner(std::string const& _sourceName) const; diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 6553dccfe..69c43563b 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -29,9 +29,12 @@ #include #include +#include + #include #include +#include using namespace std; using namespace solidity; @@ -53,6 +56,18 @@ shared_ptr updateLocationEndFrom( return make_shared(updatedLocation); } +optional toInt(string const& _value) +{ + try + { + return stoi(_value); + } + catch (...) + { + return nullopt; + } +} + } unique_ptr Parser::parse(std::shared_ptr const& _scanner, bool _reuseScanner) @@ -65,6 +80,8 @@ unique_ptr Parser::parse(std::shared_ptr const& _scanner, bool _ try { m_scanner = _scanner; + if (m_charStreamMap) + fetchSourceLocationFromComment(); auto block = make_unique(parseBlock()); if (!_reuseScanner) expectToken(Token::EOS); @@ -78,6 +95,56 @@ unique_ptr Parser::parse(std::shared_ptr const& _scanner, bool _ return nullptr; } +langutil::Token Parser::advance() +{ + auto const token = ParserBase::advance(); + if (m_useSourceLocationFrom == UseSourceLocationFrom::Comments) + fetchSourceLocationFromComment(); + return token; +} + +void Parser::fetchSourceLocationFromComment() +{ + solAssert(m_charStreamMap.has_value(), ""); + + if (m_scanner->currentCommentLiteral().empty()) + return; + + static regex const lineRE = std::regex( + R"~~~((^|\s*)@src\s+(-1|\d+):(-1|\d+):(-1|\d+)(\s+|$))~~~", + std::regex_constants::ECMAScript | std::regex_constants::optimize + ); + + string const text = m_scanner->currentCommentLiteral(); + auto from = sregex_iterator(text.begin(), text.end(), lineRE); + auto to = sregex_iterator(); + + for (auto const& matchResult: ranges::make_subrange(from, to)) + { + solAssert(matchResult.size() == 6, ""); + + auto const sourceIndex = toInt(matchResult[2].str()); + auto const start = toInt(matchResult[3].str()); + auto const end = toInt(matchResult[4].str()); + + auto const commentLocation = m_scanner->currentCommentLocation(); + m_locationOverride = SourceLocation{}; + if (!sourceIndex || !start || !end) + m_errorReporter.syntaxError(6367_error, commentLocation, "Invalid value in source location mapping. Could not parse location specification."); + else if (!((start < 0 && end < 0) || (start >= 0 && *start <= *end))) + m_errorReporter.syntaxError(5798_error, commentLocation, "Invalid value in source location mapping. Start offset larger than end offset."); + else if (!(sourceIndex >= 0 && m_charStreamMap->count(static_cast(*sourceIndex)))) + m_errorReporter.syntaxError(2674_error, commentLocation, "Invalid source mapping. Source index not defined via @use-src."); + else if (sourceIndex >= 0) + { + shared_ptr charStream = m_charStreamMap->at(static_cast(*sourceIndex)); + solAssert(charStream, ""); + + m_locationOverride = SourceLocation{*start, *end, charStream}; + } + } +} + Block Parser::parseBlock() { RecursionGuard recursionGuard(*this); @@ -85,7 +152,8 @@ Block Parser::parseBlock() expectToken(Token::LBrace); while (currentToken() != Token::RBrace) block.statements.emplace_back(parseStatement()); - block.debugData = updateLocationEndFrom(block.debugData, currentLocation()); + if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) + block.debugData = updateLocationEndFrom(block.debugData, currentLocation()); advance(); return block; } @@ -107,7 +175,8 @@ Statement Parser::parseStatement() advance(); _if.condition = make_unique(parseExpression()); _if.body = parseBlock(); - _if.debugData = updateLocationEndFrom(_if.debugData, _if.body.debugData->location); + if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) + _if.debugData = updateLocationEndFrom(_if.debugData, _if.body.debugData->location); return Statement{move(_if)}; } case Token::Switch: @@ -125,7 +194,8 @@ Statement Parser::parseStatement() fatalParserError(4904_error, "Case not allowed after default case."); if (_switch.cases.empty()) fatalParserError(2418_error, "Switch statement without any cases."); - _switch.debugData = updateLocationEndFrom(_switch.debugData, _switch.cases.back().body.debugData->location); + if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) + _switch.debugData = updateLocationEndFrom(_switch.debugData, _switch.cases.back().body.debugData->location); return Statement{move(_switch)}; } case Token::For: @@ -207,7 +277,8 @@ Statement Parser::parseStatement() expectToken(Token::AssemblyAssign); assignment.value = make_unique(parseExpression()); - assignment.debugData = updateLocationEndFrom(assignment.debugData, locationOf(*assignment.value)); + if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) + assignment.debugData = updateLocationEndFrom(assignment.debugData, locationOf(*assignment.value)); return Statement{move(assignment)}; } @@ -237,7 +308,8 @@ Case Parser::parseCase() else yulAssert(false, "Case or default case expected."); _case.body = parseBlock(); - _case.debugData = updateLocationEndFrom(_case.debugData, _case.body.debugData->location); + if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) + _case.debugData = updateLocationEndFrom(_case.debugData, _case.body.debugData->location); return _case; } @@ -257,7 +329,8 @@ ForLoop Parser::parseForLoop() forLoop.post = parseBlock(); m_currentForLoopComponent = ForLoopComponent::ForLoopBody; forLoop.body = parseBlock(); - forLoop.debugData = updateLocationEndFrom(forLoop.debugData, forLoop.body.debugData->location); + if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) + forLoop.debugData = updateLocationEndFrom(forLoop.debugData, forLoop.body.debugData->location); m_currentForLoopComponent = outerForLoopComponent; @@ -336,7 +409,8 @@ variant Parser::parseLiteralOrIdentifier() if (currentToken() == Token::Colon) { expectToken(Token::Colon); - literal.debugData = updateLocationEndFrom(literal.debugData, currentLocation()); + if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) + literal.debugData = updateLocationEndFrom(literal.debugData, currentLocation()); literal.type = expectAsmIdentifier(); } @@ -368,9 +442,10 @@ VariableDeclaration Parser::parseVariableDeclaration() { expectToken(Token::AssemblyAssign); varDecl.value = make_unique(parseExpression()); - varDecl.debugData = updateLocationEndFrom(varDecl.debugData, locationOf(*varDecl.value)); + if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) + varDecl.debugData = updateLocationEndFrom(varDecl.debugData, locationOf(*varDecl.value)); } - else + else if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) varDecl.debugData = updateLocationEndFrom(varDecl.debugData, varDecl.variables.back().debugData->location); return varDecl; @@ -417,7 +492,8 @@ FunctionDefinition Parser::parseFunctionDefinition() m_insideFunction = true; funDef.body = parseBlock(); m_insideFunction = preInsideFunction; - funDef.debugData = updateLocationEndFrom(funDef.debugData, funDef.body.debugData->location); + if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) + funDef.debugData = updateLocationEndFrom(funDef.debugData, funDef.body.debugData->location); m_currentForLoopComponent = outerForLoopComponent; return funDef; @@ -444,7 +520,8 @@ FunctionCall Parser::parseCall(variant&& _initialOp) ret.arguments.emplace_back(parseExpression()); } } - ret.debugData = updateLocationEndFrom(ret.debugData, currentLocation()); + if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) + ret.debugData = updateLocationEndFrom(ret.debugData, currentLocation()); expectToken(Token::RParen); return ret; } @@ -457,7 +534,8 @@ TypedName Parser::parseTypedName() if (currentToken() == Token::Colon) { expectToken(Token::Colon); - typedName.debugData = updateLocationEndFrom(typedName.debugData, currentLocation()); + if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) + typedName.debugData = updateLocationEndFrom(typedName.debugData, currentLocation()); typedName.type = expectAsmIdentifier(); } else diff --git a/libyul/AsmParser.h b/libyul/AsmParser.h index d010dc99d..ed4d249d5 100644 --- a/libyul/AsmParser.h +++ b/libyul/AsmParser.h @@ -46,6 +46,11 @@ public: None, ForLoopPre, ForLoopPost, ForLoopBody }; + enum class UseSourceLocationFrom + { + Scanner, LocationOverride, Comments, + }; + explicit Parser( langutil::ErrorReporter& _errorReporter, Dialect const& _dialect, @@ -53,7 +58,25 @@ public: ): ParserBase(_errorReporter), m_dialect(_dialect), - m_locationOverride(std::move(_locationOverride)) + m_locationOverride{_locationOverride ? *_locationOverride : langutil::SourceLocation{}}, + m_useSourceLocationFrom{ + _locationOverride ? + UseSourceLocationFrom::LocationOverride : + UseSourceLocationFrom::Scanner + } + {} + + /// Constructs a Yul parser that is using the source locations + /// from the comments (via @src). + explicit Parser( + langutil::ErrorReporter& _errorReporter, + Dialect const& _dialect, + std::map> _charStreamMap + ): + ParserBase(_errorReporter), + m_dialect(_dialect), + m_charStreamMap{std::move(_charStreamMap)}, + m_useSourceLocationFrom{UseSourceLocationFrom::Comments} {} /// Parses an inline assembly block starting with `{` and ending with `}`. @@ -64,9 +87,15 @@ public: protected: langutil::SourceLocation currentLocation() const override { - return m_locationOverride ? *m_locationOverride : ParserBase::currentLocation(); + if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) + return ParserBase::currentLocation(); + return m_locationOverride; } + langutil::Token advance() override; + + void fetchSourceLocationFromComment(); + /// Creates an inline assembly node with the current source location. template T createWithLocation() const { @@ -97,7 +126,10 @@ protected: private: Dialect const& m_dialect; - std::optional m_locationOverride; + + std::optional>> m_charStreamMap; + langutil::SourceLocation m_locationOverride; + UseSourceLocationFrom m_useSourceLocationFrom = UseSourceLocationFrom::Scanner; ForLoopComponent m_currentForLoopComponent = ForLoopComponent::None; bool m_insideFunction = false; }; diff --git a/scripts/error_codes.py b/scripts/error_codes.py index adb579ebb..ffb95b889 100755 --- a/scripts/error_codes.py +++ b/scripts/error_codes.py @@ -191,6 +191,9 @@ def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False): # white list of ids which are not covered by tests white_ids = { + "6367", # these three are temporarily whitelisted until both PRs are merged. + "5798", + "2674", "3805", # "This is a pre-release compiler version, please do not use it in production." # The warning may or may not exist in a compiler build. "4591", # "There are more than 256 warnings. Ignoring the rest." diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp index 6bb34064e..7fe43f842 100644 --- a/test/libyul/Parser.cpp +++ b/test/libyul/Parser.cpp @@ -51,12 +51,25 @@ namespace solidity::yul::test namespace { +string_view constexpr g_strAlternateSourceText = "{}"; + shared_ptr parse(string const& _source, Dialect const& _dialect, ErrorReporter& errorReporter) { try { auto scanner = make_shared(CharStream(_source, "")); - auto parserResult = yul::Parser(errorReporter, _dialect).parse(scanner, false); + map> indicesToCharStreams; + indicesToCharStreams[0] = scanner->charStream(); + indicesToCharStreams[1] = make_shared( + string(g_strAlternateSourceText.data(), g_strAlternateSourceText.size()), + "alternate.sol" + ); + + auto parserResult = yul::Parser( + errorReporter, + _dialect, + move(indicesToCharStreams) + ).parse(scanner, false); if (parserResult) { yul::AsmAnalysisInfo analysisInfo; @@ -187,7 +200,361 @@ BOOST_AUTO_TEST_CASE(default_types_set) ); } +#define CHECK_LOCATION(_actual, _sourceText, _start, _end) \ + do { \ + BOOST_CHECK_EQUAL((_sourceText), ((_actual).source ? (_actual).source->source() : "")); \ + BOOST_CHECK_EQUAL((_start), (_actual).start); \ + BOOST_CHECK_EQUAL((_end), (_actual).end); \ + } while (0) +BOOST_AUTO_TEST_CASE(customSourceLocations_empty_block) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = + "/// @src 0:234:543\n" + "{}\n"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + CHECK_LOCATION(result->debugData->location, sourceText, 234, 543); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_block_with_children) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = + "/// @src 0:234:543\n" + "{\n" + "let x:bool := true:bool\n" + "/// @src 0:123:432\n" + "let z:bool := true\n" + "let y := add(1, 2)\n" + "}\n"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + CHECK_LOCATION(result->debugData->location, sourceText, 234, 543); + BOOST_REQUIRE_EQUAL(3, result->statements.size()); + CHECK_LOCATION(locationOf(result->statements.at(0)), sourceText, 234, 543); + CHECK_LOCATION(locationOf(result->statements.at(1)), sourceText, 123, 432); + // [2] is inherited source location + CHECK_LOCATION(locationOf(result->statements.at(2)), sourceText, 123, 432); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_block_different_sources) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = + "/// @src 0:234:543\n" + "{\n" + "let x:bool := true:bool\n" + "/// @src 1:123:432\n" + "let z:bool := true\n" + "let y := add(1, 2)\n" + "}\n"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + CHECK_LOCATION(result->debugData->location, sourceText, 234, 543); + BOOST_REQUIRE_EQUAL(3, result->statements.size()); + CHECK_LOCATION(locationOf(result->statements.at(0)), sourceText, 234, 543); + CHECK_LOCATION(locationOf(result->statements.at(1)), g_strAlternateSourceText, 123, 432); + // [2] is inherited source location + CHECK_LOCATION(locationOf(result->statements.at(2)), g_strAlternateSourceText, 123, 432); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_block_nested) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = + "/// @src 0:234:543\n" + "{\n" + "let y := add(1, 2)\n" + "/// @src 0:343:434\n" + "switch y case 0 {} default {}\n" + "}\n"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + CHECK_LOCATION(result->debugData->location, sourceText, 234, 543); + BOOST_REQUIRE_EQUAL(2, result->statements.size()); + CHECK_LOCATION(locationOf(result->statements.at(1)), sourceText, 343, 434); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_block_switch_case) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = + "/// @src 0:234:543\n" + "{\n" + "let y := add(1, 2)\n" + "/// @src 0:343:434\n" + "switch y\n" + "/// @src 0:3141:59265\n" + "case 0 {\n" + " /// @src 0:271:828\n" + " let z := add(3, 4)\n" + "}\n" + "}\n"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + CHECK_LOCATION(result->debugData->location, sourceText, 234, 543); + + BOOST_REQUIRE_EQUAL(2, result->statements.size()); + BOOST_REQUIRE(holds_alternative(result->statements.at(1))); + auto const& switchStmt = get(result->statements.at(1)); + + CHECK_LOCATION(switchStmt.debugData->location, sourceText, 343, 434); + BOOST_REQUIRE_EQUAL(1, switchStmt.cases.size()); + CHECK_LOCATION(switchStmt.cases.at(0).debugData->location, sourceText, 3141, 59265); + + auto const& caseBody = switchStmt.cases.at(0).body; + BOOST_REQUIRE_EQUAL(1, caseBody.statements.size()); + CHECK_LOCATION(locationOf(caseBody.statements.at(0)), sourceText, 271, 828); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_inherit_into_outer_scope) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = + "/// @src 0:1:100\n" + "{\n" + "{\n" + "/// @src 0:123:432\n" + "let x:bool := true:bool\n" + "}\n" + "let z:bool := true\n" + "let y := add(1, 2)\n" + "}\n"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + + CHECK_LOCATION(result->debugData->location, sourceText, 1, 100); + + BOOST_REQUIRE_EQUAL(3, result->statements.size()); + CHECK_LOCATION(locationOf(result->statements.at(0)), sourceText, 1, 100); + + // First child element must be a block itself with one statement. + BOOST_REQUIRE(holds_alternative(result->statements.at(0))); + BOOST_REQUIRE_EQUAL(get(result->statements.at(0)).statements.size(), 1); + CHECK_LOCATION(locationOf(get(result->statements.at(0)).statements.at(0)), sourceText, 123, 432); + + // The next two elements have an inherited source location from the prior inner scope. + CHECK_LOCATION(locationOf(result->statements.at(1)), sourceText, 123, 432); + CHECK_LOCATION(locationOf(result->statements.at(2)), sourceText, 123, 432); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_assign_empty) +{ + // Tests single AST node (e.g. VariableDeclaration) with different source locations for each child. + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = + "{\n" + "/// @src 0:123:432\n" + "let a:bool\n" + "/// @src 1:1:10\n" + "a := true:bool\n" + "}\n"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); // should still parse + BOOST_REQUIRE_EQUAL(2, result->statements.size()); + CHECK_LOCATION(locationOf(result->statements.at(0)), sourceText, 123, 432); + CHECK_LOCATION(locationOf(result->statements.at(1)), g_strAlternateSourceText, 1, 10); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_source_index) +{ + // Tests single AST node (e.g. VariableDeclaration) with different source locations for each child. + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = + "{\n" + "/// @src 1:123:432\n" + "let a:bool := true:bool\n" + "/// @src 2345:0:8\n" + "let b:bool := true:bool\n" + "\n" + "}\n"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); // should still parse +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_1) +{ + // Tests single AST node (e.g. VariableDeclaration) with different source locations for each child. + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = + "{\n" + "/// @src 0:123:432\n" + "let x:bool \n" + "/// @src 0:234:2026\n" + ":= true:bool\n" + "}\n"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + + BOOST_REQUIRE_EQUAL(1, result->statements.size()); + CHECK_LOCATION(locationOf(result->statements.at(0)), sourceText, 123, 432); + BOOST_REQUIRE(holds_alternative(result->statements.at(0))); + VariableDeclaration const& varDecl = get(result->statements.at(0)); + CHECK_LOCATION(locationOf(*varDecl.value), sourceText, 234, 2026); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_2) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src 0:0:5 + { + let x := /// @src 1:2:3 + add(1, /// @src 0:4:8 + 2) + } + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + BOOST_REQUIRE_EQUAL(1, result->statements.size()); + CHECK_LOCATION(result->debugData->location, sourceText, 0, 5); + + // `let x := add(1, ` + BOOST_REQUIRE(holds_alternative(result->statements.at(0))); + VariableDeclaration const& varDecl = get(result->statements.at(0)); + CHECK_LOCATION(varDecl.debugData->location, sourceText, 0, 5); + BOOST_REQUIRE(!!varDecl.value); + BOOST_REQUIRE(holds_alternative(*varDecl.value)); + FunctionCall const& call = get(*varDecl.value); + CHECK_LOCATION(call.debugData->location, g_strAlternateSourceText, 2, 3); + + // `2` + BOOST_REQUIRE_EQUAL(2, call.arguments.size()); + CHECK_LOCATION(locationOf(call.arguments.at(1)), sourceText, 4, 8); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_3) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src 1:23:45 + { // Block + { // Block + sstore(0, 1) // FunctionCall + /// @src 0:420:680 + } + mstore(1, 2) // FunctionCall + } + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + BOOST_REQUIRE_EQUAL(2, result->statements.size()); + CHECK_LOCATION(result->debugData->location, g_strAlternateSourceText, 23, 45); + + BOOST_REQUIRE(holds_alternative(result->statements.at(0))); + Block const& innerBlock = get(result->statements.at(0)); + CHECK_LOCATION(innerBlock.debugData->location, g_strAlternateSourceText, 23, 45); + + BOOST_REQUIRE_EQUAL(1, innerBlock.statements.size()); + BOOST_REQUIRE(holds_alternative(result->statements.at(1))); + ExpressionStatement const& sstoreStmt = get(innerBlock.statements.at(0)); + BOOST_REQUIRE(holds_alternative(sstoreStmt.expression)); + FunctionCall const& sstoreCall = get(sstoreStmt.expression); + CHECK_LOCATION(sstoreCall.debugData->location, g_strAlternateSourceText, 23, 45); + + BOOST_REQUIRE(holds_alternative(result->statements.at(1))); + ExpressionStatement mstoreStmt = get(result->statements.at(1)); + BOOST_REQUIRE(holds_alternative(mstoreStmt.expression)); + FunctionCall const& mstoreCall = get(mstoreStmt.expression); + CHECK_LOCATION(mstoreCall.debugData->location, sourceText, 420, 680); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_comments_after_valid) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src 1:23:45 + { + /// @src 0:420:680 + /// @invalid + let a:bool := true + } + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + BOOST_REQUIRE_EQUAL(1, result->statements.size()); + CHECK_LOCATION(result->debugData->location, g_strAlternateSourceText, 23, 45); + + BOOST_REQUIRE(holds_alternative(result->statements.at(0))); + VariableDeclaration const& varDecl = get(result->statements.at(0)); + CHECK_LOCATION(varDecl.debugData->location, sourceText, 420, 680); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_suffix) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src 0:420:680foo + {} + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + CHECK_LOCATION(result->debugData->location, "", -1, -1); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_unspecified) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src -1:-1:-1 + {} + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + CHECK_LOCATION(result->debugData->location, "", -1, -1); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_ensure_last_match) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src 0:123:432 + { + /// @src 1:10:20 + /// @src 0:30:40 + let x:bool := true + } + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + BOOST_REQUIRE(holds_alternative(result->statements.at(0))); + VariableDeclaration const& varDecl = get(result->statements.at(0)); + + // Ensure the latest @src per documentation-comment is used (0:30:40). + CHECK_LOCATION(varDecl.debugData->location, sourceText, 30, 40); +} BOOST_AUTO_TEST_SUITE_END() From 5e4868d5d60cb659aea1db64b6946804cf79a234 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Wed, 7 Jul 2021 13:31:01 +0200 Subject: [PATCH 26/98] Adapted tests due to more precise Yul source locations. Also added support for -1 source index, referencing original scanner's source location. --- libyul/AsmParser.cpp | 6 ++++-- test/libyul/Parser.cpp | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 69c43563b..ce25724f9 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -131,11 +131,13 @@ void Parser::fetchSourceLocationFromComment() m_locationOverride = SourceLocation{}; if (!sourceIndex || !start || !end) m_errorReporter.syntaxError(6367_error, commentLocation, "Invalid value in source location mapping. Could not parse location specification."); - else if (!((start < 0 && end < 0) || (start >= 0 && *start <= *end))) + else if (!((*start < 0 && *end < 0) || (*start >= 0 && *start <= *end))) m_errorReporter.syntaxError(5798_error, commentLocation, "Invalid value in source location mapping. Start offset larger than end offset."); + else if (sourceIndex == -1 && (0 <= *start && *start <= *end)) // Use source index -1 to indicate original source. + m_locationOverride = SourceLocation{*start, *end, ParserBase::currentLocation().source}; else if (!(sourceIndex >= 0 && m_charStreamMap->count(static_cast(*sourceIndex)))) m_errorReporter.syntaxError(2674_error, commentLocation, "Invalid source mapping. Source index not defined via @use-src."); - else if (sourceIndex >= 0) + else { shared_ptr charStream = m_charStreamMap->at(static_cast(*sourceIndex)); solAssert(charStream, ""); diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp index 7fe43f842..84f9f8045 100644 --- a/test/libyul/Parser.cpp +++ b/test/libyul/Parser.cpp @@ -556,6 +556,28 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_ensure_last_match) CHECK_LOCATION(varDecl.debugData->location, sourceText, 30, 40); } +BOOST_AUTO_TEST_CASE(customSourceLocations_reference_original_sloc) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src 1:2:3 + { + /// @src -1:10:20 + let x:bool := true + } + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + BOOST_REQUIRE(holds_alternative(result->statements.at(0))); + VariableDeclaration const& varDecl = get(result->statements.at(0)); + + // -1 points to original source code, which in this case is `sourceText` (which is also + // available via `0`, that's why the first @src is set to `1` instead.) + CHECK_LOCATION(varDecl.debugData->location, sourceText, 10, 20); +} + BOOST_AUTO_TEST_SUITE_END() } // end namespaces From f129a3498c689a086d65ef3a399dcd5516e4d58e Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Thu, 8 Jul 2021 16:38:16 +0200 Subject: [PATCH 27/98] Use shared DebugData for when using source locations from comments. --- libyul/AsmParser.cpp | 11 +++++------ libyul/AsmParser.h | 20 +++++++++++++++++++- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index ce25724f9..05b3c4b1c 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -128,21 +128,20 @@ void Parser::fetchSourceLocationFromComment() auto const end = toInt(matchResult[4].str()); auto const commentLocation = m_scanner->currentCommentLocation(); - m_locationOverride = SourceLocation{}; + m_debugDataOverride = DebugData::create(); if (!sourceIndex || !start || !end) m_errorReporter.syntaxError(6367_error, commentLocation, "Invalid value in source location mapping. Could not parse location specification."); else if (!((*start < 0 && *end < 0) || (*start >= 0 && *start <= *end))) m_errorReporter.syntaxError(5798_error, commentLocation, "Invalid value in source location mapping. Start offset larger than end offset."); else if (sourceIndex == -1 && (0 <= *start && *start <= *end)) // Use source index -1 to indicate original source. - m_locationOverride = SourceLocation{*start, *end, ParserBase::currentLocation().source}; + m_debugDataOverride = DebugData::create(SourceLocation{*start, *end, ParserBase::currentLocation().source}); else if (!(sourceIndex >= 0 && m_charStreamMap->count(static_cast(*sourceIndex)))) m_errorReporter.syntaxError(2674_error, commentLocation, "Invalid source mapping. Source index not defined via @use-src."); else { shared_ptr charStream = m_charStreamMap->at(static_cast(*sourceIndex)); solAssert(charStream, ""); - - m_locationOverride = SourceLocation{*start, *end, charStream}; + m_debugDataOverride = DebugData::create(SourceLocation{*start, *end, charStream}); } } } @@ -371,7 +370,7 @@ variant Parser::parseLiteralOrIdentifier() { case Token::Identifier: { - Identifier identifier{DebugData::create(currentLocation()), YulString{currentLiteral()}}; + Identifier identifier{createDebugData(), YulString{currentLiteral()}}; advance(); return identifier; } @@ -402,7 +401,7 @@ variant Parser::parseLiteralOrIdentifier() } Literal literal{ - DebugData::create(currentLocation()), + createDebugData(), kind, YulString{currentLiteral()}, kind == LiteralKind::Boolean ? m_dialect.boolType : m_dialect.defaultType diff --git a/libyul/AsmParser.h b/libyul/AsmParser.h index ed4d249d5..fc0c9bb64 100644 --- a/libyul/AsmParser.h +++ b/libyul/AsmParser.h @@ -59,6 +59,7 @@ public: ParserBase(_errorReporter), m_dialect(_dialect), m_locationOverride{_locationOverride ? *_locationOverride : langutil::SourceLocation{}}, + m_debugDataOverride{}, m_useSourceLocationFrom{ _locationOverride ? UseSourceLocationFrom::LocationOverride : @@ -76,6 +77,7 @@ public: ParserBase(_errorReporter), m_dialect(_dialect), m_charStreamMap{std::move(_charStreamMap)}, + m_debugDataOverride{DebugData::create()}, m_useSourceLocationFrom{UseSourceLocationFrom::Comments} {} @@ -96,11 +98,26 @@ protected: void fetchSourceLocationFromComment(); + /// Creates a DebugData object with the correct source location set. + std::shared_ptr createDebugData() const + { + switch (m_useSourceLocationFrom) + { + case UseSourceLocationFrom::Scanner: + return DebugData::create(ParserBase::currentLocation()); + case UseSourceLocationFrom::LocationOverride: + return DebugData::create(m_locationOverride); + case UseSourceLocationFrom::Comments: + return m_debugDataOverride; + } + solAssert(false, ""); + } + /// Creates an inline assembly node with the current source location. template T createWithLocation() const { T r; - r.debugData = DebugData::create(currentLocation()); + r.debugData = createDebugData(); return r; } @@ -129,6 +146,7 @@ private: std::optional>> m_charStreamMap; langutil::SourceLocation m_locationOverride; + std::shared_ptr m_debugDataOverride; UseSourceLocationFrom m_useSourceLocationFrom = UseSourceLocationFrom::Scanner; ForLoopComponent m_currentForLoopComponent = ForLoopComponent::None; bool m_insideFunction = false; From 7836779975996ffae10973b02fba3e80928f188f Mon Sep 17 00:00:00 2001 From: Marenz Date: Mon, 12 Jul 2021 12:45:56 +0200 Subject: [PATCH 28/98] Fix test error "No text representation found." The result is actually okay as the code in question is never called. --- scripts/common_cmdline.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/common_cmdline.sh b/scripts/common_cmdline.sh index 88dab9cd0..7b452d857 100644 --- a/scripts/common_cmdline.sh +++ b/scripts/common_cmdline.sh @@ -64,7 +64,9 @@ function compileFull() local exit_code=$? local errors; errors=$(grep -v -E \ -e 'Warning: This is a pre-release compiler version|Warning: Experimental features are turned on|pragma experimental ABIEncoderV2|^ +--> |^ +\||^[0-9]+ +\| ' \ - -e 'Warning: Yul is still experimental. Please use the output with care.' < "$stderr_path") + -e 'Warning: Yul is still experimental. Please use the output with care.' \ + -e '^No text representation found.$' < "$stderr_path" + ) set -e rm "$stderr_path" From e5ab68ed71460ad088788ac0bc6d94c16832bcfe Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 12 Jul 2021 17:30:50 +0200 Subject: [PATCH 29/98] Use unknown file for invalid source index. --- libyul/AsmParser.cpp | 6 ++---- test/libyul/Parser.cpp | 4 +--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 05b3c4b1c..5938c3af4 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -131,10 +131,8 @@ void Parser::fetchSourceLocationFromComment() m_debugDataOverride = DebugData::create(); if (!sourceIndex || !start || !end) m_errorReporter.syntaxError(6367_error, commentLocation, "Invalid value in source location mapping. Could not parse location specification."); - else if (!((*start < 0 && *end < 0) || (*start >= 0 && *start <= *end))) - m_errorReporter.syntaxError(5798_error, commentLocation, "Invalid value in source location mapping. Start offset larger than end offset."); - else if (sourceIndex == -1 && (0 <= *start && *start <= *end)) // Use source index -1 to indicate original source. - m_debugDataOverride = DebugData::create(SourceLocation{*start, *end, ParserBase::currentLocation().source}); + else if (sourceIndex == -1) + m_debugDataOverride = DebugData::create(SourceLocation{*start, *end, nullptr}); else if (!(sourceIndex >= 0 && m_charStreamMap->count(static_cast(*sourceIndex)))) m_errorReporter.syntaxError(2674_error, commentLocation, "Invalid source mapping. Source index not defined via @use-src."); else diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp index 84f9f8045..676b6699c 100644 --- a/test/libyul/Parser.cpp +++ b/test/libyul/Parser.cpp @@ -573,9 +573,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_reference_original_sloc) BOOST_REQUIRE(holds_alternative(result->statements.at(0))); VariableDeclaration const& varDecl = get(result->statements.at(0)); - // -1 points to original source code, which in this case is `sourceText` (which is also - // available via `0`, that's why the first @src is set to `1` instead.) - CHECK_LOCATION(varDecl.debugData->location, sourceText, 10, 20); + CHECK_LOCATION(varDecl.debugData->location, "", 10, 20); } BOOST_AUTO_TEST_SUITE_END() From ab5898f11c381e783a435ac74ac72bfca2cce5fc Mon Sep 17 00:00:00 2001 From: CrimsonGlory Date: Mon, 12 Jul 2021 21:18:46 -0300 Subject: [PATCH 30/98] RHS->right hand side --- docs/types/reference-types.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/types/reference-types.rst b/docs/types/reference-types.rst index 7b1b541f3..6d5240ec8 100644 --- a/docs/types/reference-types.rst +++ b/docs/types/reference-types.rst @@ -559,7 +559,7 @@ shown in the following example: function newCampaign(address payable beneficiary, uint goal) public returns (uint campaignID) { campaignID = numCampaigns++; // campaignID is return variable // We cannot use "campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0)" - // because the RHS creates a memory-struct "Campaign" that contains a mapping. + // because the right hand side creates a memory-struct "Campaign" that contains a mapping. Campaign storage c = campaigns[campaignID]; c.beneficiary = beneficiary; c.fundingGoal = goal; From adb09452d87fbf24acf950d8c680114e6b2bfd01 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Wed, 30 Jun 2021 12:08:59 +0200 Subject: [PATCH 31/98] ParserBase has virtual functions but did not have a virtual dtor. --- liblangutil/ParserBase.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/liblangutil/ParserBase.h b/liblangutil/ParserBase.h index c3fb1056d..098826f1c 100644 --- a/liblangutil/ParserBase.h +++ b/liblangutil/ParserBase.h @@ -47,6 +47,8 @@ public: m_parserErrorRecovery = _parserErrorRecovery; } + virtual ~ParserBase() = default; + std::shared_ptr source() const { return m_scanner->charStream(); } protected: From 0aeb1ef47ed6d978da1a80b83ce9a10d87e5b8f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 14 Jul 2021 13:50:03 +0200 Subject: [PATCH 32/98] Require sphinx-rtd-theme>= 0.5.2; older --- docs/requirements.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 038c22b7c..23c8f9043 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,8 @@ -sphinx_rtd_theme>=0.3.1 +# Older versions of sphinx-rtd-theme do not work with never docutils but have a bug in the dependency +# which could result in it being installed anyway and the style (especially bullet points) being broken. +# See https://github.com/readthedocs/sphinx_rtd_theme/issues/1115 +sphinx_rtd_theme>=0.5.2 + pygments-lexer-solidity>=0.7.0 sphinx-a4doc>=1.2.1 From f75b55071ec30958436cc3ee7a0a5ae6fe954518 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 29 Jun 2021 14:38:59 +0200 Subject: [PATCH 33/98] Remove CharStream from SourceLocation. --- libevmasm/Assembly.cpp | 16 +- libevmasm/AssemblyItem.cpp | 4 +- liblangutil/CMakeLists.txt | 1 + liblangutil/CharStream.cpp | 16 +- liblangutil/CharStream.h | 16 +- liblangutil/CharStreamProvider.h | 57 +++++++ liblangutil/Scanner.cpp | 8 +- liblangutil/Scanner.h | 9 +- liblangutil/SourceLocation.cpp | 8 +- liblangutil/SourceLocation.h | 55 +++---- liblangutil/SourceReferenceExtractor.cpp | 40 +++-- liblangutil/SourceReferenceExtractor.h | 8 +- liblangutil/SourceReferenceFormatter.cpp | 10 +- liblangutil/SourceReferenceFormatter.h | 31 +++- libsolidity/analysis/NameAndTypeResolver.cpp | 6 +- libsolidity/analysis/SyntaxChecker.cpp | 2 +- libsolidity/ast/ASTJsonConverter.cpp | 4 +- libsolidity/codegen/CompilerContext.cpp | 4 +- libsolidity/codegen/ir/Common.cpp | 3 +- libsolidity/codegen/ir/IRGenerator.cpp | 5 +- libsolidity/formal/BMC.cpp | 2 +- libsolidity/formal/Predicate.cpp | 5 +- libsolidity/interface/CompilerStack.cpp | 33 +---- libsolidity/interface/CompilerStack.h | 19 +-- libsolidity/interface/StandardCompiler.cpp | 19 ++- libsolidity/parsing/Parser.cpp | 13 +- libyul/AsmJsonImporter.cpp | 5 +- libyul/AsmParser.cpp | 12 +- libyul/AsmParser.h | 6 +- libyul/AssemblyStack.cpp | 20 ++- libyul/AssemblyStack.h | 9 +- libyul/backends/wasm/EVMToEwasmTranslator.cpp | 8 +- libyul/backends/wasm/EVMToEwasmTranslator.h | 10 +- solc/CommandLineInterface.cpp | 7 +- test/libevmasm/Assembler.cpp | 8 +- test/liblangutil/SourceLocation.cpp | 6 +- test/libsolidity/ASTJSONTest.cpp | 10 +- test/libsolidity/AnalysisFramework.cpp | 2 +- test/libsolidity/Assembly.cpp | 29 ++-- test/libsolidity/GasTest.cpp | 5 +- test/libsolidity/InlineAssembly.cpp | 2 +- .../SolidityExecutionFramework.cpp | 6 +- test/libsolidity/SolidityParser.cpp | 6 +- test/libsolidity/SyntaxTest.cpp | 11 +- test/libyul/Common.cpp | 9 -- test/libyul/Common.h | 2 - test/libyul/EVMCodeTransformTest.cpp | 5 +- test/libyul/EwasmTranslationTest.cpp | 20 +-- test/libyul/EwasmTranslationTest.h | 11 +- test/libyul/ObjectCompilerTest.cpp | 11 +- test/libyul/ObjectCompilerTest.h | 2 - test/libyul/Parser.cpp | 88 ++++++----- test/libyul/YulInterpreterTest.cpp | 11 +- test/libyul/YulInterpreterTest.h | 9 -- test/libyul/YulOptimizerTest.cpp | 13 +- test/libyul/YulOptimizerTest.h | 1 - .../tools/ossfuzz/SolidityEvmoneInterface.cpp | 6 +- test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp | 22 +-- test/tools/yulopti.cpp | 17 ++- test/tools/yulrun.cpp | 8 +- tools/solidityUpgrade/SourceTransform.h | 108 ++++++++------ tools/solidityUpgrade/SourceUpgrade.cpp | 13 +- tools/solidityUpgrade/SourceUpgrade.h | 18 +-- tools/solidityUpgrade/Upgrade050.cpp | 4 +- tools/solidityUpgrade/Upgrade060.cpp | 140 +++++++++--------- tools/solidityUpgrade/Upgrade060.h | 9 ++ tools/solidityUpgrade/Upgrade070.cpp | 10 +- tools/solidityUpgrade/UpgradeChange.cpp | 16 +- tools/solidityUpgrade/UpgradeChange.h | 26 ++-- tools/solidityUpgrade/UpgradeSuite.h | 14 +- tools/yulPhaser/Phaser.cpp | 6 +- tools/yulPhaser/Program.cpp | 10 -- tools/yulPhaser/Program.h | 8 +- 73 files changed, 613 insertions(+), 560 deletions(-) create mode 100644 liblangutil/CharStreamProvider.h diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index f63f3ede6..13bb63dfe 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -76,15 +76,15 @@ namespace string locationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location) { if (!_location.hasText() || _sourceCodes.empty()) - return ""; + return {}; - auto it = _sourceCodes.find(_location.source->name()); + auto it = _sourceCodes.find(*_location.sourceName); if (it == _sourceCodes.end()) - return ""; + return {}; string const& source = it->second; if (static_cast(_location.start) >= source.size()) - return ""; + return {}; string cut = source.substr(static_cast(_location.start), static_cast(_location.end - _location.start)); auto newLinePos = cut.find_first_of("\n"); @@ -152,8 +152,8 @@ public: if (!m_location.isValid()) return; m_out << m_prefix << " /*"; - if (m_location.source) - m_out << " \"" + m_location.source->name() + "\""; + if (m_location.sourceName) + m_out << " " + escapeAndQuoteYulString(*m_location.sourceName); if (m_location.hasText()) m_out << ":" << to_string(m_location.start) + ":" + to_string(m_location.end); m_out << " " << locationFromSources(m_sourceCodes, m_location); @@ -235,9 +235,9 @@ Json::Value Assembly::assemblyJSON(map const& _sourceIndices) for (AssemblyItem const& i: m_items) { int sourceIndex = -1; - if (i.location().source) + if (i.location().sourceName) { - auto iter = _sourceIndices.find(i.location().source->name()); + auto iter = _sourceIndices.find(*i.location().sourceName); if (iter != _sourceIndices.end()) sourceIndex = static_cast(iter->second); } diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp index b4acf6f87..75c0e4dca 100644 --- a/libevmasm/AssemblyItem.cpp +++ b/libevmasm/AssemblyItem.cpp @@ -350,8 +350,8 @@ std::string AssemblyItem::computeSourceMapping( SourceLocation const& location = item.location(); int length = location.start != -1 && location.end != -1 ? location.end - location.start : -1; int sourceIndex = - location.source && _sourceIndicesMap.count(location.source->name()) ? - static_cast(_sourceIndicesMap.at(location.source->name())) : + (location.sourceName && _sourceIndicesMap.count(*location.sourceName)) ? + static_cast(_sourceIndicesMap.at(*location.sourceName)) : -1; char jump = '-'; if (item.getJumpType() == evmasm::AssemblyItem::JumpType::IntoFunction) diff --git a/liblangutil/CMakeLists.txt b/liblangutil/CMakeLists.txt index 477d3695c..c98877963 100644 --- a/liblangutil/CMakeLists.txt +++ b/liblangutil/CMakeLists.txt @@ -13,6 +13,7 @@ set(sources ParserBase.h Scanner.cpp Scanner.h + CharStreamProvider.h SemVerHandler.cpp SemVerHandler.h SourceLocation.h diff --git a/liblangutil/CharStream.cpp b/liblangutil/CharStream.cpp index 12e4f642c..8ab99fca8 100644 --- a/liblangutil/CharStream.cpp +++ b/liblangutil/CharStream.cpp @@ -45,9 +45,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** - * @author Christian - * @date 2014 - * Solidity scanner. + * Character stream / input file. */ #include @@ -118,3 +116,15 @@ tuple CharStream::translatePositionToLineColumn(int _position) const } return tuple(lineNumber, searchPosition - lineStart); } + +string_view CharStream::text(SourceLocation const& _location) const +{ + if (!_location.hasText()) + return {}; + solAssert(_location.sourceName && *_location.sourceName == m_name, ""); + solAssert(static_cast(_location.end) <= m_source.size(), ""); + return string_view{m_source}.substr( + static_cast(_location.start), + static_cast(_location.end - _location.start) + ); +} diff --git a/liblangutil/CharStream.h b/liblangutil/CharStream.h index 23a0781fc..4b729771f 100644 --- a/liblangutil/CharStream.h +++ b/liblangutil/CharStream.h @@ -45,9 +45,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** - * @author Christian - * @date 2014 - * Solidity scanner. + * Character stream / input file. */ #pragma once @@ -60,6 +58,8 @@ namespace solidity::langutil { +struct SourceLocation; + /** * Bidirectional stream of characters. * @@ -69,8 +69,8 @@ class CharStream { public: CharStream() = default; - explicit CharStream(std::string _source, std::string name): - m_source(std::move(_source)), m_name(std::move(name)) {} + CharStream(std::string _source, std::string _name): + m_source(std::move(_source)), m_name(std::move(_name)) {} size_t position() const { return m_position; } bool isPastEndOfInput(size_t _charsForward = 0) const { return (m_position + _charsForward) >= m_source.size(); } @@ -90,6 +90,8 @@ public: std::string const& source() const noexcept { return m_source; } std::string const& name() const noexcept { return m_name; } + size_t size() const { return m_source.size(); } + ///@{ ///@name Error printing helper functions /// Functions that help pretty-printing parse errors @@ -112,6 +114,10 @@ public: return true; } + /// @returns the substring of the source that the source location references. + /// Returns an empty string view if the source location does not `hasText()`. + std::string_view text(SourceLocation const& _location) const; + private: std::string m_source; std::string m_name; diff --git a/liblangutil/CharStreamProvider.h b/liblangutil/CharStreamProvider.h new file mode 100644 index 000000000..1f645f7ef --- /dev/null +++ b/liblangutil/CharStreamProvider.h @@ -0,0 +1,57 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +/** + * Interface to retrieve the character stream by a source name. + */ + +#pragma once + +#include +#include + +#include + +namespace solidity::langutil +{ + +/** + * Interface to retrieve a CharStream (source) from a source name. + * Used especially for printing error information. + */ +class CharStreamProvider +{ +public: + virtual ~CharStreamProvider() = default; + virtual CharStream const& charStream(std::string const& _sourceName) const = 0; +}; + +class SingletonCharStreamProvider: public CharStreamProvider +{ +public: + explicit SingletonCharStreamProvider(CharStream const& _charStream): + m_charStream(_charStream) {} + CharStream const& charStream(std::string const& _sourceName) const override + { + solAssert(m_charStream.name() == _sourceName, ""); + return m_charStream; + } +private: + CharStream const& m_charStream; +}; + +} diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index 5dbc2529c..760b4803a 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -138,6 +138,7 @@ private: void Scanner::reset(CharStream _source) { m_source = make_shared(std::move(_source)); + m_sourceName = make_shared(m_source->name()); reset(); } @@ -145,6 +146,7 @@ void Scanner::reset(shared_ptr _source) { solAssert(_source.get() != nullptr, "You MUST provide a CharStream when resetting."); m_source = std::move(_source); + m_sourceName = make_shared(m_source->name()); reset(); } @@ -497,7 +499,7 @@ Token Scanner::scanSlash() return skipSingleLineComment(); // doxygen style /// comment m_skippedComments[NextNext].location.start = firstSlashPosition; - m_skippedComments[NextNext].location.source = m_source; + m_skippedComments[NextNext].location.sourceName = m_sourceName; m_skippedComments[NextNext].token = Token::CommentLiteral; m_skippedComments[NextNext].location.end = static_cast(scanSingleLineDocComment()); return Token::Whitespace; @@ -526,7 +528,7 @@ Token Scanner::scanSlash() return skipMultiLineComment(); // we actually have a multiline documentation comment m_skippedComments[NextNext].location.start = firstSlashPosition; - m_skippedComments[NextNext].location.source = m_source; + m_skippedComments[NextNext].location.sourceName = m_sourceName; Token comment = scanMultiLineDocComment(); m_skippedComments[NextNext].location.end = static_cast(sourcePos()); m_skippedComments[NextNext].token = comment; @@ -766,7 +768,7 @@ void Scanner::scanToken() } while (token == Token::Whitespace); m_tokens[NextNext].location.end = static_cast(sourcePos()); - m_tokens[NextNext].location.source = m_source; + m_tokens[NextNext].location.sourceName = m_sourceName; m_tokens[NextNext].token = token; m_tokens[NextNext].extendedTokenInfo = make_tuple(m, n); } diff --git a/liblangutil/Scanner.h b/liblangutil/Scanner.h index 97f60bbda..ef12703ef 100644 --- a/liblangutil/Scanner.h +++ b/liblangutil/Scanner.h @@ -177,14 +177,6 @@ public: Token peekNextNextToken() const { return m_tokens[NextNext].token; } ///@} - ///@{ - ///@name Error printing helper functions - /// Functions that help pretty-printing parse errors - /// Do only use in error cases, they are quite expensive. - std::string lineAtPosition(int _position) const { return m_source->lineAtPosition(_position); } - std::tuple translatePositionToLineColumn(int _position) const { return m_source->translatePositionToLineColumn(_position); } - ///@} - private: inline Token setError(ScannerError _error) noexcept @@ -270,6 +262,7 @@ private: TokenDesc m_tokens[3] = {}; // desc for the current, next and nextnext token std::shared_ptr m_source; + std::shared_ptr m_sourceName; ScannerKind m_kind = ScannerKind::Solidity; diff --git a/liblangutil/SourceLocation.cpp b/liblangutil/SourceLocation.cpp index 8f3187437..76270829d 100644 --- a/liblangutil/SourceLocation.cpp +++ b/liblangutil/SourceLocation.cpp @@ -34,6 +34,7 @@ SourceLocation const parseSourceLocation(std::string const& _input, std::string boost::algorithm::split(pos, _input, boost::is_any_of(":")); + // TODO What to do with sourceIndex? solAssert(pos.size() == 3, "SourceLocation string must have 3 colon separated numeric fields."); auto const sourceIndex = stoi(pos[Index]); @@ -45,12 +46,7 @@ SourceLocation const parseSourceLocation(std::string const& _input, std::string int start = stoi(pos[Start]); int end = start + stoi(pos[Length]); - // ASSUMPTION: only the name of source is used from here on, the m_source of the CharStream-Object can be empty - std::shared_ptr source; - if (sourceIndex != -1) - source = std::make_shared("", _sourceName); - - return SourceLocation{start, end, source}; + return SourceLocation{start, end, std::make_shared(_sourceName)}; } } diff --git a/liblangutil/SourceLocation.h b/liblangutil/SourceLocation.h index eb858bd06..71e5ad711 100644 --- a/liblangutil/SourceLocation.h +++ b/liblangutil/SourceLocation.h @@ -26,8 +26,6 @@ #include #include -#include - #include #include #include @@ -44,51 +42,44 @@ struct SourceLocation { bool operator==(SourceLocation const& _other) const { - return source.get() == _other.source.get() && start == _other.start && end == _other.end; + return start == _other.start && end == _other.end && equalSources(_other); } bool operator!=(SourceLocation const& _other) const { return !operator==(_other); } - inline bool operator<(SourceLocation const& _other) const + bool operator<(SourceLocation const& _other) const { - if (!source|| !_other.source) - return std::make_tuple(int(!!source), start, end) < std::make_tuple(int(!!_other.source), _other.start, _other.end); + if (!sourceName || !_other.sourceName) + return std::make_tuple(int(!!sourceName), start, end) < std::make_tuple(int(!!_other.sourceName), _other.start, _other.end); else - return std::make_tuple(source->name(), start, end) < std::make_tuple(_other.source->name(), _other.start, _other.end); + return std::make_tuple(*sourceName, start, end) < std::make_tuple(*_other.sourceName, _other.start, _other.end); } - inline bool contains(SourceLocation const& _other) const + bool contains(SourceLocation const& _other) const { - if (!hasText() || !_other.hasText() || source.get() != _other.source.get()) + if (!hasText() || !_other.hasText() || !equalSources(_other)) return false; return start <= _other.start && _other.end <= end; } - inline bool intersects(SourceLocation const& _other) const + bool intersects(SourceLocation const& _other) const { - if (!hasText() || !_other.hasText() || source.get() != _other.source.get()) + if (!hasText() || !_other.hasText() || !equalSources(_other)) return false; return _other.start < end && start < _other.end; } - bool isValid() const { return source || start != -1 || end != -1; } - - bool hasText() const + bool equalSources(SourceLocation const& _other) const { - return - source && - 0 <= start && - start <= end && - end <= int(source->source().length()); + if (!!sourceName != !!_other.sourceName) + return false; + if (sourceName && *sourceName != *_other.sourceName) + return false; + return true; } - std::string text() const - { - assertThrow(source, SourceLocationError, "Requested text from null source."); - assertThrow(0 <= start, SourceLocationError, "Invalid source location."); - assertThrow(start <= end, SourceLocationError, "Invalid source location."); - assertThrow(end <= int(source->source().length()), SourceLocationError, "Invalid source location."); - return source->source().substr(size_t(start), size_t(end - start)); - } + bool isValid() const { return sourceName || start != -1 || end != -1; } + + bool hasText() const { return sourceName && 0 <= start && start <= end; } /// @returns the smallest SourceLocation that contains both @param _a and @param _b. /// Assumes that @param _a and @param _b refer to the same source (exception: if the source of either one @@ -97,8 +88,8 @@ struct SourceLocation /// @param _b, then start resp. end of the result will be -1 as well). static SourceLocation smallestCovering(SourceLocation _a, SourceLocation const& _b) { - if (!_a.source) - _a.source = _b.source; + if (!_a.sourceName) + _a.sourceName = _b.sourceName; if (_a.start < 0) _a.start = _b.start; @@ -112,7 +103,7 @@ struct SourceLocation int start = -1; int end = -1; - std::shared_ptr source; + std::shared_ptr sourceName; }; SourceLocation const parseSourceLocation( @@ -127,8 +118,8 @@ inline std::ostream& operator<<(std::ostream& _out, SourceLocation const& _locat if (!_location.isValid()) return _out << "NO_LOCATION_SPECIFIED"; - if (_location.source) - _out << _location.source->name(); + if (_location.sourceName) + _out << *_location.sourceName; _out << "[" << _location.start << "," << _location.end << "]"; diff --git a/liblangutil/SourceReferenceExtractor.cpp b/liblangutil/SourceReferenceExtractor.cpp index 3eac873aa..717e590df 100644 --- a/liblangutil/SourceReferenceExtractor.cpp +++ b/liblangutil/SourceReferenceExtractor.cpp @@ -16,8 +16,9 @@ */ // SPDX-License-Identifier: GPL-3.0 #include -#include #include +#include +#include #include #include @@ -26,46 +27,57 @@ using namespace std; using namespace solidity; using namespace solidity::langutil; -SourceReferenceExtractor::Message SourceReferenceExtractor::extract(util::Exception const& _exception, string _category) +SourceReferenceExtractor::Message SourceReferenceExtractor::extract( + CharStreamProvider const& _charStreamProvider, + util::Exception const& _exception, + string _category +) { SourceLocation const* location = boost::get_error_info(_exception); string const* message = boost::get_error_info(_exception); - SourceReference primary = extract(location, message ? *message : ""); + SourceReference primary = extract(_charStreamProvider, location, message ? *message : ""); std::vector secondary; auto secondaryLocation = boost::get_error_info(_exception); if (secondaryLocation && !secondaryLocation->infos.empty()) for (auto const& info: secondaryLocation->infos) - secondary.emplace_back(extract(&info.second, info.first)); + secondary.emplace_back(extract(_charStreamProvider, &info.second, info.first)); return Message{std::move(primary), _category, std::move(secondary), nullopt}; } -SourceReferenceExtractor::Message SourceReferenceExtractor::extract(Error const& _error) +SourceReferenceExtractor::Message SourceReferenceExtractor::extract( + CharStreamProvider const& _charStreamProvider, + Error const& _error +) { string category = (_error.type() == Error::Type::Warning) ? "Warning" : "Error"; - Message message = extract(_error, category); + Message message = extract(_charStreamProvider, _error, category); message.errorId = _error.errorId(); return message; } -SourceReference SourceReferenceExtractor::extract(SourceLocation const* _location, std::string message) +SourceReference SourceReferenceExtractor::extract( + CharStreamProvider const& _charStreamProvider, + SourceLocation const* _location, + std::string message +) { - if (!_location || !_location->source.get()) // Nothing we can extract here + if (!_location || !_location->sourceName) // Nothing we can extract here return SourceReference::MessageOnly(std::move(message)); if (!_location->hasText()) // No source text, so we can only extract the source name - return SourceReference::MessageOnly(std::move(message), _location->source->name()); + return SourceReference::MessageOnly(std::move(message), *_location->sourceName); - shared_ptr const& source = _location->source; + CharStream const& charStream = _charStreamProvider.charStream(*_location->sourceName); - LineColumn const interest = source->translatePositionToLineColumn(_location->start); + LineColumn const interest = charStream.translatePositionToLineColumn(_location->start); LineColumn start = interest; - LineColumn end = source->translatePositionToLineColumn(_location->end); + LineColumn end = charStream.translatePositionToLineColumn(_location->end); bool const isMultiline = start.line != end.line; - string line = source->lineAtPosition(_location->start); + string line = charStream.lineAtPosition(_location->start); int locationLength = isMultiline ? @@ -102,7 +114,7 @@ SourceReference SourceReferenceExtractor::extract(SourceLocation const* _locatio return SourceReference{ std::move(message), - source->name(), + *_location->sourceName, interest, isMultiline, line, diff --git a/liblangutil/SourceReferenceExtractor.h b/liblangutil/SourceReferenceExtractor.h index 7eb54845c..d26a941f5 100644 --- a/liblangutil/SourceReferenceExtractor.h +++ b/liblangutil/SourceReferenceExtractor.h @@ -28,6 +28,8 @@ namespace solidity::langutil { +class CharStreamProvider; + struct LineColumn { int line = {-1}; @@ -67,9 +69,9 @@ namespace SourceReferenceExtractor std::optional errorId; }; - Message extract(util::Exception const& _exception, std::string _category); - Message extract(Error const& _error); - SourceReference extract(SourceLocation const* _location, std::string message = ""); + Message extract(CharStreamProvider const& _charStreamProvider, util::Exception const& _exception, std::string _category); + Message extract(CharStreamProvider const& _charStreamProvider, Error const& _error); + SourceReference extract(CharStreamProvider const& _charStreamProvider, SourceLocation const* _location, std::string message = ""); } } diff --git a/liblangutil/SourceReferenceFormatter.cpp b/liblangutil/SourceReferenceFormatter.cpp index cc2afc24b..2a0799036 100644 --- a/liblangutil/SourceReferenceFormatter.cpp +++ b/liblangutil/SourceReferenceFormatter.cpp @@ -173,10 +173,16 @@ void SourceReferenceFormatter::printExceptionInformation(SourceReferenceExtracto void SourceReferenceFormatter::printExceptionInformation(util::Exception const& _exception, std::string const& _category) { - printExceptionInformation(SourceReferenceExtractor::extract(_exception, _category)); + printExceptionInformation(SourceReferenceExtractor::extract(m_charStreamProvider, _exception, _category)); +} + +void SourceReferenceFormatter::printErrorInformation(ErrorList const& _errors) +{ + for (auto const& error: _errors) + printErrorInformation(*error); } void SourceReferenceFormatter::printErrorInformation(Error const& _error) { - printExceptionInformation(SourceReferenceExtractor::extract(_error)); + printExceptionInformation(SourceReferenceExtractor::extract(m_charStreamProvider, _error)); } diff --git a/liblangutil/SourceReferenceFormatter.h b/liblangutil/SourceReferenceFormatter.h index dac76912d..630f00de5 100644 --- a/liblangutil/SourceReferenceFormatter.h +++ b/liblangutil/SourceReferenceFormatter.h @@ -23,6 +23,7 @@ #include #include +#include #include @@ -37,34 +38,53 @@ struct SourceLocation; class SourceReferenceFormatter { public: - SourceReferenceFormatter(std::ostream& _stream, bool _colored, bool _withErrorIds): - m_stream(_stream), m_colored(_colored), m_withErrorIds(_withErrorIds) + SourceReferenceFormatter( + std::ostream& _stream, + CharStreamProvider const& _charStreamProvider, + bool _colored, + bool _withErrorIds + ): + m_stream(_stream), m_charStreamProvider(_charStreamProvider), m_colored(_colored), m_withErrorIds(_withErrorIds) {} /// Prints source location if it is given. void printSourceLocation(SourceReference const& _ref); void printExceptionInformation(SourceReferenceExtractor::Message const& _msg); void printExceptionInformation(util::Exception const& _exception, std::string const& _category); + void printErrorInformation(langutil::ErrorList const& _errors); void printErrorInformation(Error const& _error); static std::string formatExceptionInformation( util::Exception const& _exception, std::string const& _name, + CharStreamProvider const& _charStreamProvider, bool _colored = false, bool _withErrorIds = false ) { std::ostringstream errorOutput; - SourceReferenceFormatter formatter(errorOutput, _colored, _withErrorIds); + SourceReferenceFormatter formatter(errorOutput, _charStreamProvider, _colored, _withErrorIds); formatter.printExceptionInformation(_exception, _name); return errorOutput.str(); } - static std::string formatErrorInformation(Error const& _error) + static std::string formatErrorInformation( + Error const& _error, + CharStreamProvider const& _charStreamProvider + ) { return formatExceptionInformation( _error, - (_error.type() == Error::Type::Warning) ? "Warning" : "Error" + (_error.type() == Error::Type::Warning) ? "Warning" : "Error", + _charStreamProvider + ); + } + + static std::string formatErrorInformation(Error const& _error, CharStream const& _charStream) + { + return formatErrorInformation( + _error, + SingletonCharStreamProvider(_charStream) ); } @@ -79,6 +99,7 @@ private: private: std::ostream& m_stream; + CharStreamProvider const& m_charStreamProvider; bool m_colored; bool m_withErrorIds; }; diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 97308f7e7..20c4f2c46 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -520,9 +520,9 @@ bool DeclarationRegistrationHelper::registerDeclaration( Declaration const* conflictingDeclaration = _container.conflictingDeclaration(_declaration, _name); solAssert(conflictingDeclaration, ""); bool const comparable = - _errorLocation->source && - conflictingDeclaration->location().source && - _errorLocation->source->name() == conflictingDeclaration->location().source->name(); + _errorLocation->sourceName && + conflictingDeclaration->location().sourceName && + *_errorLocation->sourceName == *conflictingDeclaration->location().sourceName; if (comparable && _errorLocation->start < conflictingDeclaration->location().start) { firstDeclarationLocation = *_errorLocation; diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 04745344f..412ad1aa9 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -68,7 +68,7 @@ void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit) string(";\""); // when reporting the warning, print the source name only - m_errorReporter.warning(3420_error, {-1, -1, _sourceUnit.location().source}, errorString); + m_errorReporter.warning(3420_error, {-1, -1, _sourceUnit.location().sourceName}, errorString); } if (!m_sourceUnit->annotation().useABICoderV2.set()) m_sourceUnit->annotation().useABICoderV2 = true; diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index ea8662010..bae1b7d62 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -110,8 +110,8 @@ void ASTJsonConverter::setJsonNode( optional ASTJsonConverter::sourceIndexFromLocation(SourceLocation const& _location) const { - if (_location.source && m_sourceIndices.count(_location.source->name())) - return m_sourceIndices.at(_location.source->name()); + if (_location.sourceName && m_sourceIndices.count(*_location.sourceName)) + return m_sourceIndices.at(*_location.sourceName); else return nullopt; } diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 7b673cca6..8b7f2751b 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -455,7 +455,9 @@ void CompilerContext::appendInlineAssembly( _assembly + "\n" "------------------ Errors: ----------------\n"; for (auto const& error: errorReporter.errors()) - message += SourceReferenceFormatter::formatErrorInformation(*error); + // TODO if we have "locationOverride", it will be the wrong char stream, + // but we do not have access to the solidity scanner. + message += SourceReferenceFormatter::formatErrorInformation(*error, *scanner->charStream()); message += "-------------------------------------------\n"; solAssert(false, message); diff --git a/libsolidity/codegen/ir/Common.cpp b/libsolidity/codegen/ir/Common.cpp index 4f414cb99..8906fa97e 100644 --- a/libsolidity/codegen/ir/Common.cpp +++ b/libsolidity/codegen/ir/Common.cpp @@ -129,8 +129,9 @@ string IRNames::zeroValue(Type const& _type, string const& _variableName) string sourceLocationComment(langutil::SourceLocation const& _location, IRGenerationContext const& _context) { + solAssert(_location.sourceName, ""); return "/// @src " - + to_string(_context.sourceIndices().at(_location.source->name())) + + to_string(_context.sourceIndices().at(*_location.sourceName)) + ":" + to_string(_location.start) + "," diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index 9aab3ff5d..af9335adf 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -101,7 +101,10 @@ pair IRGenerator::run( { string errorMessage; for (auto const& error: asmStack.errors()) - errorMessage += langutil::SourceReferenceFormatter::formatErrorInformation(*error); + errorMessage += langutil::SourceReferenceFormatter::formatErrorInformation( + *error, + asmStack.charStream("") + ); solAssert(false, ir + "\n\nInvalid IR generated:\n" + errorMessage + "\n"); } asmStack.optimize(); diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index c7e25db29..c40424173 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -650,7 +650,7 @@ pair, vector> BMC::modelExpressions() if (uf->annotation().type->isValueType()) { expressionsToEvaluate.emplace_back(expr(*uf)); - expressionNames.push_back(uf->location().text()); + // TODO expressionNames.push_back(uf->location().text()); } return {expressionsToEvaluate, expressionNames}; diff --git a/libsolidity/formal/Predicate.cpp b/libsolidity/formal/Predicate.cpp index c2b8fd607..8e9aace61 100644 --- a/libsolidity/formal/Predicate.cpp +++ b/libsolidity/formal/Predicate.cpp @@ -200,8 +200,9 @@ string Predicate::formatSummaryCall(vector const& _args) co { solAssert(isSummary(), ""); - if (auto funCall = programFunctionCall()) - return funCall->location().text(); + //if (auto funCall = programFunctionCall()) +// return funCall->location().text(); + // TODO /// The signature of a function summary predicate is: summary(error, this, abiFunctions, cryptoFunctions, txData, preBlockChainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars). /// Here we are interested in preInputVars to format the function call, diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 6f86c3cb5..45353720b 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -926,19 +926,6 @@ map CompilerStack::sourceIndices() const return indices; } -map> CompilerStack::indicesToCharStreams() const -{ - map> result; - unsigned index = 0; - for (auto const& s: m_sources) - result[index++] = s.second.scanner->charStream(); - - // NB: CompilerContext::yulUtilityFileName() does not have a source, - result[index++] = shared_ptr{}; - - return result; -} - Json::Value const& CompilerStack::contractABI(string const& _contractName) const { if (m_stackState < AnalysisPerformed) @@ -1048,12 +1035,15 @@ string const& CompilerStack::metadata(Contract const& _contract) const return _contract.metadata.init([&]{ return createMetadata(_contract); }); } -Scanner const& CompilerStack::scanner(string const& _sourceName) const +CharStream const& CompilerStack::charStream(string const& _sourceName) const { if (m_stackState < SourcesSet) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No sources set.")); - return *source(_sourceName).scanner; + solAssert(source(_sourceName).scanner, ""); + solAssert(source(_sourceName).scanner->charStream(), ""); + + return *source(_sourceName).scanner->charStream(); } SourceUnit const& CompilerStack::ast(string const& _sourceName) const @@ -1095,19 +1085,6 @@ size_t CompilerStack::functionEntryPoint( return 0; } -tuple CompilerStack::positionFromSourceLocation(SourceLocation const& _sourceLocation) const -{ - int startLine; - int startColumn; - int endLine; - int endColumn; - tie(startLine, startColumn) = scanner(_sourceLocation.source->name()).translatePositionToLineColumn(_sourceLocation.start); - tie(endLine, endColumn) = scanner(_sourceLocation.source->name()).translatePositionToLineColumn(_sourceLocation.end); - - return make_tuple(++startLine, ++startColumn, ++endLine, ++endColumn); -} - - h256 const& CompilerStack::Source::keccak256() const { if (keccak256HashCached == h256{}) diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 9459d7cfa..77b2fe4de 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -38,6 +38,7 @@ #include #include #include +#include #include @@ -57,6 +58,7 @@ namespace solidity::langutil { class Scanner; +class CharStream; } @@ -87,7 +89,7 @@ class DeclarationContainer; * If error recovery is active, it is possible to progress through the stages even when * there are errors. In any case, producing code is only possible without errors. */ -class CompilerStack +class CompilerStack: public langutil::CharStreamProvider { public: /// Noncopyable. @@ -120,7 +122,7 @@ public: /// and must not emit exceptions. explicit CompilerStack(ReadCallback::Callback _readFile = ReadCallback::Callback()); - ~CompilerStack(); + ~CompilerStack() override; /// @returns the list of errors that occurred during parsing and type checking. langutil::ErrorList const& errors() const { return m_errorReporter.errors(); } @@ -239,12 +241,8 @@ public: /// by sourceNames(). std::map sourceIndices() const; - /// @returns the reverse mapping of source indices to their respective - /// CharStream instances. - std::map> indicesToCharStreams() const; - - /// @returns the previously used scanner, useful for counting lines during error reporting. - langutil::Scanner const& scanner(std::string const& _sourceName) const; + /// @returns the previously used character stream, useful for counting lines during error reporting. + langutil::CharStream const& charStream(std::string const& _sourceName) const override; /// @returns the parsed source unit with the supplied name. SourceUnit const& ast(std::string const& _sourceName) const; @@ -253,11 +251,6 @@ public: /// does not exist. ContractDefinition const& contractDefinition(std::string const& _contractName) const; - /// Helper function for logs printing. Do only use in error cases, it's quite expensive. - /// line and columns are numbered starting from 1 with following order: - /// start line, start column, end line, end column - std::tuple positionFromSourceLocation(langutil::SourceLocation const& _sourceLocation) const; - /// @returns a list of unhandled queries to the SMT solver (has to be supplied in a second run /// by calling @a addSMTLib2Response). std::vector const& unhandledSMTLib2Queries() const { return m_unhandledSMTLib2Queries; } diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index a9c0cfd85..ab70d93b7 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -83,9 +83,9 @@ Json::Value formatFatalError(string const& _type, string const& _message) Json::Value formatSourceLocation(SourceLocation const* location) { Json::Value sourceLocation; - if (location && location->source) + if (location && location->sourceName) { - sourceLocation["file"] = location->source->name(); + sourceLocation["file"] = *location->sourceName; sourceLocation["start"] = location->start; sourceLocation["end"] = location->end; } @@ -109,6 +109,7 @@ Json::Value formatSecondarySourceLocation(SecondarySourceLocation const* _second } Json::Value formatErrorWithException( + CharStreamProvider const& _charStreamProvider, util::Exception const& _exception, bool const& _warning, string const& _type, @@ -119,7 +120,11 @@ Json::Value formatErrorWithException( { string message; // TODO: consider enabling color - string formattedMessage = SourceReferenceFormatter::formatExceptionInformation(_exception, _type); + string formattedMessage = SourceReferenceFormatter::formatExceptionInformation( + _exception, + _type, + _charStreamProvider + ); if (string const* description = boost::get_error_info(_exception)) message = ((_message.length() > 0) ? (_message + ":") : "") + *description; @@ -1017,6 +1022,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting Error const& err = dynamic_cast(*error); errors.append(formatErrorWithException( + compilerStack, *error, err.type() == Error::Type::Warning, err.typeName(), @@ -1030,6 +1036,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting catch (Error const& _error) { errors.append(formatErrorWithException( + compilerStack, _error, false, _error.typeName(), @@ -1050,6 +1057,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting catch (CompilerError const& _exception) { errors.append(formatErrorWithException( + compilerStack, _exception, false, "CompilerError", @@ -1060,6 +1068,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting catch (InternalCompilerError const& _exception) { errors.append(formatErrorWithException( + compilerStack, _exception, false, "InternalCompilerError", @@ -1070,6 +1079,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting catch (UnimplementedFeatureError const& _exception) { errors.append(formatErrorWithException( + compilerStack, _exception, false, "UnimplementedFeatureError", @@ -1080,6 +1090,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting catch (yul::YulException const& _exception) { errors.append(formatErrorWithException( + compilerStack, _exception, false, "YulException", @@ -1090,6 +1101,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting catch (smtutil::SMTLogicError const& _exception) { errors.append(formatErrorWithException( + compilerStack, _exception, false, "SMTLogicException", @@ -1297,6 +1309,7 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings) auto err = dynamic_pointer_cast(error); errors.append(formatErrorWithException( + stack, *error, err->type() == Error::Type::Warning, err->typeName(), diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 3896f34a8..852f056ac 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -50,7 +50,12 @@ class Parser::ASTNodeFactory { public: explicit ASTNodeFactory(Parser& _parser): - m_parser(_parser), m_location{_parser.currentLocation().start, -1, _parser.currentLocation().source} {} + m_parser(_parser), m_location{ + _parser.currentLocation().start, + -1, + _parser.currentLocation().sourceName + } + {} ASTNodeFactory(Parser& _parser, ASTPointer const& _childNode): m_parser(_parser), m_location{_childNode->location()} {} @@ -63,7 +68,7 @@ public: template ASTPointer createNode(Args&& ... _args) { - solAssert(m_location.source, ""); + solAssert(m_location.sourceName, ""); if (m_location.end < 0) markEndPosition(); return make_shared(m_parser.nextID(), m_location, std::forward(_args)...); @@ -2084,7 +2089,7 @@ optional Parser::findLicenseString(std::vector> cons else if (matches.empty()) parserWarning( 1878_error, - {-1, -1, m_scanner->charStream()}, + {-1, -1, m_scanner->currentLocation().sourceName}, "SPDX license identifier not provided in source file. " "Before publishing, consider adding a comment containing " "\"SPDX-License-Identifier: \" to each source file. " @@ -2094,7 +2099,7 @@ optional Parser::findLicenseString(std::vector> cons else parserError( 3716_error, - {-1, -1, m_scanner->charStream()}, + {-1, -1, m_scanner->currentLocation().sourceName}, "Multiple SPDX license identifiers found in source file. " "Use \"AND\" or \"OR\" to combine multiple licenses. " "Please see https://spdx.org for more information." diff --git a/libyul/AsmJsonImporter.cpp b/libyul/AsmJsonImporter.cpp index 0aacbdf84..de4e06870 100644 --- a/libyul/AsmJsonImporter.cpp +++ b/libyul/AsmJsonImporter.cpp @@ -53,10 +53,7 @@ T AsmJsonImporter::createAsmNode(Json::Value const& _node) { T r; SourceLocation location = createSourceLocation(_node); - yulAssert( - location.source && 0 <= location.start && location.start <= location.end, - "Invalid source location in Asm AST" - ); + yulAssert(location.hasText(), "Invalid source location in Asm AST"); r.debugData = DebugData::create(location); return r; } diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 5938c3af4..e8f5b3e4e 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -80,7 +80,7 @@ unique_ptr Parser::parse(std::shared_ptr const& _scanner, bool _ try { m_scanner = _scanner; - if (m_charStreamMap) + if (m_sourceNames) fetchSourceLocationFromComment(); auto block = make_unique(parseBlock()); if (!_reuseScanner) @@ -105,7 +105,7 @@ langutil::Token Parser::advance() void Parser::fetchSourceLocationFromComment() { - solAssert(m_charStreamMap.has_value(), ""); + solAssert(m_sourceNames.has_value(), ""); if (m_scanner->currentCommentLiteral().empty()) return; @@ -133,13 +133,13 @@ void Parser::fetchSourceLocationFromComment() m_errorReporter.syntaxError(6367_error, commentLocation, "Invalid value in source location mapping. Could not parse location specification."); else if (sourceIndex == -1) m_debugDataOverride = DebugData::create(SourceLocation{*start, *end, nullptr}); - else if (!(sourceIndex >= 0 && m_charStreamMap->count(static_cast(*sourceIndex)))) + else if (!(sourceIndex >= 0 && m_sourceNames->count(static_cast(*sourceIndex)))) m_errorReporter.syntaxError(2674_error, commentLocation, "Invalid source mapping. Source index not defined via @use-src."); else { - shared_ptr charStream = m_charStreamMap->at(static_cast(*sourceIndex)); - solAssert(charStream, ""); - m_debugDataOverride = DebugData::create(SourceLocation{*start, *end, charStream}); + shared_ptr sourceName = m_sourceNames->at(static_cast(*sourceIndex)); + solAssert(sourceName, ""); + m_debugDataOverride = DebugData::create(SourceLocation{*start, *end, move(sourceName)}); } } } diff --git a/libyul/AsmParser.h b/libyul/AsmParser.h index fc0c9bb64..4b01ce697 100644 --- a/libyul/AsmParser.h +++ b/libyul/AsmParser.h @@ -72,11 +72,11 @@ public: explicit Parser( langutil::ErrorReporter& _errorReporter, Dialect const& _dialect, - std::map> _charStreamMap + std::map> _sourceNames ): ParserBase(_errorReporter), m_dialect(_dialect), - m_charStreamMap{std::move(_charStreamMap)}, + m_sourceNames{std::move(_sourceNames)}, m_debugDataOverride{DebugData::create()}, m_useSourceLocationFrom{UseSourceLocationFrom::Comments} {} @@ -144,7 +144,7 @@ protected: private: Dialect const& m_dialect; - std::optional>> m_charStreamMap; + std::optional>> m_sourceNames; langutil::SourceLocation m_locationOverride; std::shared_ptr m_debugDataOverride; UseSourceLocationFrom m_useSourceLocationFrom = UseSourceLocationFrom::Scanner; diff --git a/libyul/AssemblyStack.cpp b/libyul/AssemblyStack.cpp index 32318edb8..8a00608cf 100644 --- a/libyul/AssemblyStack.cpp +++ b/libyul/AssemblyStack.cpp @@ -88,18 +88,20 @@ evmasm::Assembly::OptimiserSettings translateOptimiserSettings( } -Scanner const& AssemblyStack::scanner() const +CharStream const& AssemblyStack::charStream(string const& _sourceName) const { - yulAssert(m_scanner, ""); - return *m_scanner; + yulAssert(m_charStream, ""); + yulAssert(m_charStream->name() == _sourceName, ""); + return *m_charStream; } bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string const& _source) { m_errors.clear(); m_analysisSuccessful = false; - m_scanner = make_shared(CharStream(_source, _sourceName)); - m_parserResult = ObjectParser(m_errorReporter, languageToDialect(m_language, m_evmVersion)).parse(m_scanner, false); + m_charStream = make_unique(_source, _sourceName); + shared_ptr scanner = make_shared(*m_charStream); + m_parserResult = ObjectParser(m_errorReporter, languageToDialect(m_language, m_evmVersion)).parse(scanner, false); if (!m_errorReporter.errors().empty()) return false; yulAssert(m_parserResult, ""); @@ -132,7 +134,8 @@ void AssemblyStack::translate(AssemblyStack::Language _targetLanguage) ); *m_parserResult = EVMToEwasmTranslator( - languageToDialect(m_language, m_evmVersion) + languageToDialect(m_language, m_evmVersion), + *this ).run(*parserResult()); m_language = _targetLanguage; @@ -241,6 +244,7 @@ AssemblyStack::assembleWithDeployed(optional _deployName) const { auto [creationAssembly, deployedAssembly] = assembleEVMWithDeployed(_deployName); yulAssert(creationAssembly, ""); + yulAssert(m_charStream, ""); MachineAssemblyObject creationObject; creationObject.bytecode = make_shared(creationAssembly->assemble()); @@ -249,7 +253,7 @@ AssemblyStack::assembleWithDeployed(optional _deployName) const creationObject.sourceMappings = make_unique( evmasm::AssemblyItem::computeSourceMapping( creationAssembly->items(), - {{scanner().charStream() ? scanner().charStream()->name() : "", 0}} + {{m_charStream->name(), 0}} ) ); @@ -261,7 +265,7 @@ AssemblyStack::assembleWithDeployed(optional _deployName) const deployedObject.sourceMappings = make_unique( evmasm::AssemblyItem::computeSourceMapping( deployedAssembly->items(), - {{scanner().charStream() ? scanner().charStream()->name() : "", 0}} + {{m_charStream->name(), 0}} ) ); } diff --git a/libyul/AssemblyStack.h b/libyul/AssemblyStack.h index 925d0f87d..3fcd15998 100644 --- a/libyul/AssemblyStack.h +++ b/libyul/AssemblyStack.h @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -61,7 +62,7 @@ struct MachineAssemblyObject * Full assembly stack that can support EVM-assembly and Yul as input and EVM, EVM1.5 and * Ewasm as output. */ -class AssemblyStack +class AssemblyStack: public langutil::CharStreamProvider { public: enum class Language { Yul, Assembly, StrictAssembly, Ewasm }; @@ -77,8 +78,8 @@ public: m_errorReporter(m_errors) {} - /// @returns the scanner used during parsing - langutil::Scanner const& scanner() const; + /// @returns the char stream used during parsing + langutil::CharStream const& charStream(std::string const& _sourceName) const override; /// Runs parsing and analysis steps, returns false if input cannot be assembled. /// Multiple calls overwrite the previous state. @@ -132,7 +133,7 @@ private: langutil::EVMVersion m_evmVersion; solidity::frontend::OptimiserSettings m_optimiserSettings; - std::shared_ptr m_scanner; + std::unique_ptr m_charStream; bool m_analysisSuccessful = false; std::shared_ptr m_parserResult; diff --git a/libyul/backends/wasm/EVMToEwasmTranslator.cpp b/libyul/backends/wasm/EVMToEwasmTranslator.cpp index 074174532..65687cf59 100644 --- a/libyul/backends/wasm/EVMToEwasmTranslator.cpp +++ b/libyul/backends/wasm/EVMToEwasmTranslator.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include @@ -106,7 +107,7 @@ Object EVMToEwasmTranslator::run(Object const& _object) message += ret.toString(&WasmDialect::instance()); message += "----------------------------------\n"; for (auto const& err: errors) - message += langutil::SourceReferenceFormatter::formatErrorInformation(*err); + message += langutil::SourceReferenceFormatter::formatErrorInformation(*err, m_charStreamProvider); yulAssert(false, message); } @@ -140,7 +141,10 @@ void EVMToEwasmTranslator::parsePolyfill() { string message; for (auto const& err: errors) - message += langutil::SourceReferenceFormatter::formatErrorInformation(*err); + message += langutil::SourceReferenceFormatter::formatErrorInformation( + *err, + SingletonCharStreamProvider(*scanner->charStream()) + ); yulAssert(false, message); } diff --git a/libyul/backends/wasm/EVMToEwasmTranslator.h b/libyul/backends/wasm/EVMToEwasmTranslator.h index 251e3352b..7f95f8d13 100644 --- a/libyul/backends/wasm/EVMToEwasmTranslator.h +++ b/libyul/backends/wasm/EVMToEwasmTranslator.h @@ -25,6 +25,10 @@ #include #include +namespace solidity::langutil +{ +class CharStreamProvider; +} namespace solidity::yul { struct Object; @@ -32,13 +36,17 @@ struct Object; class EVMToEwasmTranslator: public ASTModifier { public: - EVMToEwasmTranslator(Dialect const& _evmDialect): m_dialect(_evmDialect) {} + EVMToEwasmTranslator(Dialect const& _evmDialect, langutil::CharStreamProvider const& _charStreamProvider): + m_dialect(_evmDialect), + m_charStreamProvider(_charStreamProvider) + {} Object run(Object const& _object); private: void parsePolyfill(); Dialect const& m_dialect; + langutil::CharStreamProvider const& m_charStreamProvider; std::shared_ptr m_polyfill; std::set m_polyfillFunctions; diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index a553e0491..aeb40f099 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -561,7 +561,7 @@ bool CommandLineInterface::compile() m_compiler = make_unique(m_fileReader.reader()); - SourceReferenceFormatter formatter(serr(false), coloredOutput(m_options), m_options.formatting.withErrorIds); + SourceReferenceFormatter formatter(serr(false), *m_compiler, coloredOutput(m_options), m_options.formatting.withErrorIds); try { @@ -598,8 +598,7 @@ bool CommandLineInterface::compile() if (!m_compiler->analyze()) { - for (auto const& error: m_compiler->errors()) - formatter.printErrorInformation(*error); + formatter.printErrorInformation(m_compiler->errors()); astAssert(false, "Analysis of the AST failed"); } } @@ -974,7 +973,7 @@ bool CommandLineInterface::assemble( for (auto const& sourceAndStack: assemblyStacks) { auto const& stack = sourceAndStack.second; - SourceReferenceFormatter formatter(serr(false), coloredOutput(m_options), m_options.formatting.withErrorIds); + SourceReferenceFormatter formatter(serr(false), stack, coloredOutput(m_options), m_options.formatting.withErrorIds); for (auto const& error: stack.errors()) { diff --git a/test/libevmasm/Assembler.cpp b/test/libevmasm/Assembler.cpp index 5e260b14d..0c1e4dbad 100644 --- a/test/libevmasm/Assembler.cpp +++ b/test/libevmasm/Assembler.cpp @@ -57,11 +57,11 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) { "sub.asm", 1 } }; Assembly _assembly; - auto root_asm = make_shared("lorem ipsum", "root.asm"); + auto root_asm = make_shared("root.asm"); _assembly.setSourceLocation({1, 3, root_asm}); Assembly _subAsm; - auto sub_asm = make_shared("lorem ipsum", "sub.asm"); + auto sub_asm = make_shared("sub.asm"); _subAsm.setSourceLocation({6, 8, sub_asm}); // PushImmutable _subAsm.appendImmutable("someImmutable"); @@ -172,11 +172,11 @@ BOOST_AUTO_TEST_CASE(immutable) { "sub.asm", 1 } }; Assembly _assembly; - auto root_asm = make_shared("lorem ipsum", "root.asm"); + auto root_asm = make_shared("root.asm"); _assembly.setSourceLocation({1, 3, root_asm}); Assembly _subAsm; - auto sub_asm = make_shared("lorem ipsum", "sub.asm"); + auto sub_asm = make_shared("sub.asm"); _subAsm.setSourceLocation({6, 8, sub_asm}); _subAsm.appendImmutable("someImmutable"); _subAsm.appendImmutable("someOtherImmutable"); diff --git a/test/liblangutil/SourceLocation.cpp b/test/liblangutil/SourceLocation.cpp index ff1664dfe..c8457dd06 100644 --- a/test/liblangutil/SourceLocation.cpp +++ b/test/liblangutil/SourceLocation.cpp @@ -34,9 +34,9 @@ BOOST_AUTO_TEST_SUITE(SourceLocationTest) BOOST_AUTO_TEST_CASE(test_fail) { - auto const source = std::make_shared("lorem ipsum", "source"); - auto const sourceA = std::make_shared("lorem ipsum", "sourceA"); - auto const sourceB = std::make_shared("lorem ipsum", "sourceB"); + auto const source = std::make_shared("source"); + auto const sourceA = std::make_shared("sourceA"); + auto const sourceB = std::make_shared("sourceB"); BOOST_CHECK(SourceLocation{} == SourceLocation{}); BOOST_CHECK((SourceLocation{0, 3, sourceA} != SourceLocation{0, 3, sourceB})); diff --git a/test/libsolidity/ASTJSONTest.cpp b/test/libsolidity/ASTJSONTest.cpp index a571d282b..8579e9ea4 100644 --- a/test/libsolidity/ASTJSONTest.cpp +++ b/test/libsolidity/ASTJSONTest.cpp @@ -141,9 +141,8 @@ TestCase::TestResult ASTJSONTest::run(ostream& _stream, string const& _linePrefi if (!c.compile(CompilerStack::State::Parsed)) { - SourceReferenceFormatter formatter(_stream, _formatted, false); - for (auto const& error: c.errors()) - formatter.printErrorInformation(*error); + SourceReferenceFormatter formatter(_stream, c, _formatted, false); + formatter.printErrorInformation(c.errors()); return TestResult::FatalError; } @@ -167,9 +166,8 @@ TestCase::TestResult ASTJSONTest::run(ostream& _stream, string const& _linePrefi if (m_expectation.empty()) return resultsMatch ? TestResult::Success : TestResult::Failure; - SourceReferenceFormatter formatter(_stream, _formatted, false); - for (auto const& error: c.errors()) - formatter.printErrorInformation(*error); + SourceReferenceFormatter{_stream, c, _formatted, false} + .printErrorInformation(c.errors()); return TestResult::FatalError; } diff --git a/test/libsolidity/AnalysisFramework.cpp b/test/libsolidity/AnalysisFramework.cpp index 36172562d..bc1a5372d 100644 --- a/test/libsolidity/AnalysisFramework.cpp +++ b/test/libsolidity/AnalysisFramework.cpp @@ -151,7 +151,7 @@ string AnalysisFramework::formatErrors() const string AnalysisFramework::formatError(Error const& _error) const { - return SourceReferenceFormatter::formatErrorInformation(_error); + return SourceReferenceFormatter::formatErrorInformation(_error, *m_compiler); } ContractDefinition const* AnalysisFramework::retrieveContractByName(SourceUnit const& _source, string const& _name) diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index 75238c9cf..cfa25e813 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -109,7 +109,7 @@ void printAssemblyLocations(AssemblyItems const& _items) ", " << _loc.end << ", make_shared(\"" << - _loc.source->name() << + *_loc.sourceName << "\")}) +" << endl; }; @@ -157,15 +157,16 @@ BOOST_AUTO_TEST_SUITE(Assembly) BOOST_AUTO_TEST_CASE(location_test) { - auto sourceCode = make_shared(R"( + string sourceCode = R"( pragma abicoder v1; contract test { function f() public returns (uint256 a) { return 16; } } - )", ""); - AssemblyItems items = compileContract(sourceCode); + )"; + AssemblyItems items = compileContract(make_shared(sourceCode, "")); + shared_ptr sourceName = make_shared(); bool hasShifts = solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting(); auto codegenCharStream = make_shared("", "--CODEGEN--"); @@ -173,18 +174,18 @@ BOOST_AUTO_TEST_CASE(location_test) vector locations; if (solidity::test::CommonOptions::get().optimize) locations = - vector(31, SourceLocation{23, 103, sourceCode}) + - vector(1, SourceLocation{41, 100, sourceCode}) + - vector(1, SourceLocation{93, 95, sourceCode}) + - vector(15, SourceLocation{41, 100, sourceCode}); + vector(31, SourceLocation{23, 103, sourceName}) + + vector(1, SourceLocation{41, 100, sourceName}) + + vector(1, SourceLocation{93, 95, sourceName}) + + vector(15, SourceLocation{41, 100, sourceName}); else locations = - vector(hasShifts ? 31 : 32, SourceLocation{23, 103, sourceCode}) + - vector(24, SourceLocation{41, 100, sourceCode}) + - vector(1, SourceLocation{70, 79, sourceCode}) + - vector(1, SourceLocation{93, 95, sourceCode}) + - vector(2, SourceLocation{86, 95, sourceCode}) + - vector(2, SourceLocation{41, 100, sourceCode}); + vector(hasShifts ? 31 : 32, SourceLocation{23, 103, sourceName}) + + vector(24, SourceLocation{41, 100, sourceName}) + + vector(1, SourceLocation{70, 79, sourceName}) + + vector(1, SourceLocation{93, 95, sourceName}) + + vector(2, SourceLocation{86, 95, sourceName}) + + vector(2, SourceLocation{41, 100, sourceName}); checkAssemblyLocations(items, locations); } diff --git a/test/libsolidity/GasTest.cpp b/test/libsolidity/GasTest.cpp index 36910647c..fd215cb94 100644 --- a/test/libsolidity/GasTest.cpp +++ b/test/libsolidity/GasTest.cpp @@ -118,9 +118,8 @@ TestCase::TestResult GasTest::run(ostream& _stream, string const& _linePrefix, b if (!compiler().parseAndAnalyze() || !compiler().compile()) { - SourceReferenceFormatter formatter(_stream, _formatted, false); - for (auto const& error: compiler().errors()) - formatter.printErrorInformation(*error); + SourceReferenceFormatter{_stream, compiler(), _formatted, false} + .printErrorInformation(compiler().errors()); return TestResult::FatalError; } diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 32ed71e17..19b2da3b3 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -81,7 +81,7 @@ std::optional parseAndReturnFirstError( { string errors; for (auto const& err: stack.errors()) - errors += SourceReferenceFormatter::formatErrorInformation(*err); + errors += SourceReferenceFormatter::formatErrorInformation(*err, stack); BOOST_FAIL("Found more than one error:\n" + errors); } error = e; diff --git a/test/libsolidity/SolidityExecutionFramework.cpp b/test/libsolidity/SolidityExecutionFramework.cpp index 6ec949c68..349baf481 100644 --- a/test/libsolidity/SolidityExecutionFramework.cpp +++ b/test/libsolidity/SolidityExecutionFramework.cpp @@ -65,10 +65,8 @@ bytes SolidityExecutionFramework::multiSourceCompileContract( for (auto const& error: m_compiler.errors()) if (error->type() == langutil::Error::Type::CodeGenerationError) BOOST_THROW_EXCEPTION(*error); - langutil::SourceReferenceFormatter formatter(std::cerr, true, false); - - for (auto const& error: m_compiler.errors()) - formatter.printErrorInformation(*error); + langutil::SourceReferenceFormatter{std::cerr, m_compiler, true, false} + .printErrorInformation(m_compiler.errors()); BOOST_ERROR("Compiling contract failed"); } string contractName(_contractName.empty() ? m_compiler.lastContractName(_mainSourceName) : _contractName); diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 5443f7b39..48d87c6bb 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -591,19 +591,21 @@ BOOST_AUTO_TEST_CASE(inline_asm_end_location) class CheckInlineAsmLocation: public ASTConstVisitor { public: + explicit CheckInlineAsmLocation(string _sourceCode): m_sourceCode(_sourceCode) {} bool visited = false; bool visit(InlineAssembly const& _inlineAsm) override { auto loc = _inlineAsm.location(); - auto asmStr = loc.source->source().substr(static_cast(loc.start), static_cast(loc.end - loc.start)); + auto asmStr = m_sourceCode.substr(static_cast(loc.start), static_cast(loc.end - loc.start)); BOOST_CHECK_EQUAL(asmStr, "assembly { a := 0x12345678 }"); visited = true; return false; } + string m_sourceCode; }; - CheckInlineAsmLocation visitor; + CheckInlineAsmLocation visitor{sourceCode}; contract->accept(visitor); BOOST_CHECK_MESSAGE(visitor.visited, "No inline asm block found?!"); diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index ed23bc609..bc0f6a7d8 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -17,6 +17,7 @@ // SPDX-License-Identifier: GPL-3.0 #include + #include #include #include @@ -119,11 +120,13 @@ void SyntaxTest::filterObtainedErrors() string sourceName; if (auto location = boost::get_error_info(*currentError)) { - solAssert(location->source, ""); - sourceName = location->source->name(); - + solAssert(location->sourceName, ""); + sourceName = *location->sourceName; solAssert(m_sources.sources.count(sourceName) == 1, ""); - int preambleSize = static_cast(location->source->source().size()) - static_cast(m_sources.sources[sourceName].size()); + + int preambleSize = + static_cast(compiler().charStream(sourceName).size()) - + static_cast(m_sources.sources[sourceName].size()); solAssert(preambleSize >= 0, ""); // ignore the version & license pragma inserted by the testing tool when calculating locations. diff --git a/test/libyul/Common.cpp b/test/libyul/Common.cpp index 35dd0de26..853b7dafc 100644 --- a/test/libyul/Common.cpp +++ b/test/libyul/Common.cpp @@ -53,15 +53,6 @@ Dialect const& defaultDialect(bool _yul) } } -void yul::test::printErrors(ErrorList const& _errors) -{ - SourceReferenceFormatter formatter(cout, true, false); - - for (auto const& error: _errors) - formatter.printErrorInformation(*error); -} - - pair, shared_ptr> yul::test::parse(string const& _source, bool _yul) { AssemblyStack stack( diff --git a/test/libyul/Common.h b/test/libyul/Common.h index 57f659400..f6c63feb9 100644 --- a/test/libyul/Common.h +++ b/test/libyul/Common.h @@ -44,8 +44,6 @@ struct Dialect; namespace solidity::yul::test { -void printErrors(langutil::ErrorList const& _errors); - std::pair, std::shared_ptr> parse(std::string const& _source, bool _yul = true); diff --git a/test/libyul/EVMCodeTransformTest.cpp b/test/libyul/EVMCodeTransformTest.cpp index eea7e0914..043eae331 100644 --- a/test/libyul/EVMCodeTransformTest.cpp +++ b/test/libyul/EVMCodeTransformTest.cpp @@ -51,9 +51,8 @@ TestCase::TestResult EVMCodeTransformTest::run(ostream& _stream, string const& _ if (!stack.parseAndAnalyze("", m_source)) { AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; - SourceReferenceFormatter formatter(_stream, true, false); - for (auto const& error: stack.errors()) - formatter.printErrorInformation(*error); + SourceReferenceFormatter{_stream, stack, true, false} + .printErrorInformation(stack.errors()); return TestResult::FatalError; } diff --git a/test/libyul/EwasmTranslationTest.cpp b/test/libyul/EwasmTranslationTest.cpp index 3c5e6cacb..6051c2e20 100644 --- a/test/libyul/EwasmTranslationTest.cpp +++ b/test/libyul/EwasmTranslationTest.cpp @@ -63,7 +63,8 @@ TestCase::TestResult EwasmTranslationTest::run(ostream& _stream, string const& _ return TestResult::FatalError; *m_object = EVMToEwasmTranslator( - EVMDialect::strictAssemblyForEVMObjects(solidity::test::CommonOptions::get().evmVersion()) + EVMDialect::strictAssemblyForEVMObjects(solidity::test::CommonOptions::get().evmVersion()), + m_stack ).run(*m_object); // Add call to "main()". @@ -78,20 +79,21 @@ TestCase::TestResult EwasmTranslationTest::run(ostream& _stream, string const& _ bool EwasmTranslationTest::parse(ostream& _stream, string const& _linePrefix, bool const _formatted) { - AssemblyStack stack( + m_stack = AssemblyStack( solidity::test::CommonOptions::get().evmVersion(), AssemblyStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::none() ); - if (stack.parseAndAnalyze("", m_source)) + if (m_stack.parseAndAnalyze("", m_source)) { - m_object = stack.parserResult(); + m_object = m_stack.parserResult(); return true; } else { AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; - printErrors(_stream, stack.errors()); + SourceReferenceFormatter{_stream, m_stack, true, false} + .printErrorInformation(m_stack.errors()); return false; } } @@ -114,11 +116,3 @@ string EwasmTranslationTest::interpret() state.dumpTraceAndState(result); return result.str(); } - -void EwasmTranslationTest::printErrors(ostream& _stream, ErrorList const& _errors) -{ - SourceReferenceFormatter formatter(_stream, true, false); - - for (auto const& error: _errors) - formatter.printErrorInformation(*error); -} diff --git a/test/libyul/EwasmTranslationTest.h b/test/libyul/EwasmTranslationTest.h index 5175430e3..eba82beee 100644 --- a/test/libyul/EwasmTranslationTest.h +++ b/test/libyul/EwasmTranslationTest.h @@ -20,16 +20,12 @@ #include -namespace solidity::langutil -{ -class Scanner; -class Error; -using ErrorList = std::vector>; -} +#include namespace solidity::yul { struct Object; +class AssemblyStack; } namespace solidity::yul::test @@ -51,9 +47,8 @@ private: bool parse(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted); std::string interpret(); - static void printErrors(std::ostream& _stream, langutil::ErrorList const& _errors); - std::shared_ptr m_object; + AssemblyStack m_stack; }; } diff --git a/test/libyul/ObjectCompilerTest.cpp b/test/libyul/ObjectCompilerTest.cpp index f4a1c296d..2c3560726 100644 --- a/test/libyul/ObjectCompilerTest.cpp +++ b/test/libyul/ObjectCompilerTest.cpp @@ -69,7 +69,8 @@ TestCase::TestResult ObjectCompilerTest::run(ostream& _stream, string const& _li if (!stack.parseAndAnalyze("source", m_source)) { AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; - printErrors(_stream, stack.errors()); + SourceReferenceFormatter{_stream, stack, true, false} + .printErrorInformation(stack.errors()); return TestResult::FatalError; } stack.optimize(); @@ -104,11 +105,3 @@ TestCase::TestResult ObjectCompilerTest::run(ostream& _stream, string const& _li return checkResult(_stream, _linePrefix, _formatted); } - -void ObjectCompilerTest::printErrors(ostream& _stream, ErrorList const& _errors) -{ - SourceReferenceFormatter formatter(_stream, true, false); - - for (auto const& error: _errors) - formatter.printErrorInformation(*error); -} diff --git a/test/libyul/ObjectCompilerTest.h b/test/libyul/ObjectCompilerTest.h index e75f72fa9..c7880fe55 100644 --- a/test/libyul/ObjectCompilerTest.h +++ b/test/libyul/ObjectCompilerTest.h @@ -54,8 +54,6 @@ private: bool parse(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted); void disambiguate(); - static void printErrors(std::ostream& _stream, langutil::ErrorList const& _errors); - frontend::OptimisationPreset m_optimisationPreset; bool m_wasm = false; }; diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp index 676b6699c..553e84d4b 100644 --- a/test/libyul/Parser.cpp +++ b/test/libyul/Parser.cpp @@ -51,24 +51,19 @@ namespace solidity::yul::test namespace { -string_view constexpr g_strAlternateSourceText = "{}"; - shared_ptr parse(string const& _source, Dialect const& _dialect, ErrorReporter& errorReporter) { try { auto scanner = make_shared(CharStream(_source, "")); - map> indicesToCharStreams; - indicesToCharStreams[0] = scanner->charStream(); - indicesToCharStreams[1] = make_shared( - string(g_strAlternateSourceText.data(), g_strAlternateSourceText.size()), - "alternate.sol" - ); + map> indicesToSourceNames; + indicesToSourceNames[0] = make_shared("source0"); + indicesToSourceNames[1] = make_shared("source1"); auto parserResult = yul::Parser( errorReporter, _dialect, - move(indicesToCharStreams) + move(indicesToSourceNames) ).parse(scanner, false); if (parserResult) { @@ -200,9 +195,9 @@ BOOST_AUTO_TEST_CASE(default_types_set) ); } -#define CHECK_LOCATION(_actual, _sourceText, _start, _end) \ +#define CHECK_LOCATION(_actual, _sourceName, _start, _end) \ do { \ - BOOST_CHECK_EQUAL((_sourceText), ((_actual).source ? (_actual).source->source() : "")); \ + BOOST_CHECK_EQUAL((_sourceName), ((_actual).sourceName ? *(_actual).sourceName : "")); \ BOOST_CHECK_EQUAL((_start), (_actual).start); \ BOOST_CHECK_EQUAL((_end), (_actual).end); \ } while (0) @@ -217,7 +212,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_empty_block) EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); - CHECK_LOCATION(result->debugData->location, sourceText, 234, 543); + CHECK_LOCATION(result->debugData->location, "source0", 234, 543); } BOOST_AUTO_TEST_CASE(customSourceLocations_block_with_children) @@ -235,12 +230,12 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_block_with_children) EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); - CHECK_LOCATION(result->debugData->location, sourceText, 234, 543); + CHECK_LOCATION(result->debugData->location, "source0", 234, 543); BOOST_REQUIRE_EQUAL(3, result->statements.size()); - CHECK_LOCATION(locationOf(result->statements.at(0)), sourceText, 234, 543); - CHECK_LOCATION(locationOf(result->statements.at(1)), sourceText, 123, 432); + CHECK_LOCATION(locationOf(result->statements.at(0)), "source0", 234, 543); + CHECK_LOCATION(locationOf(result->statements.at(1)), "source0", 123, 432); // [2] is inherited source location - CHECK_LOCATION(locationOf(result->statements.at(2)), sourceText, 123, 432); + CHECK_LOCATION(locationOf(result->statements.at(2)), "source0", 123, 432); } BOOST_AUTO_TEST_CASE(customSourceLocations_block_different_sources) @@ -258,12 +253,12 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_block_different_sources) EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); - CHECK_LOCATION(result->debugData->location, sourceText, 234, 543); + CHECK_LOCATION(result->debugData->location, "source0", 234, 543); BOOST_REQUIRE_EQUAL(3, result->statements.size()); - CHECK_LOCATION(locationOf(result->statements.at(0)), sourceText, 234, 543); - CHECK_LOCATION(locationOf(result->statements.at(1)), g_strAlternateSourceText, 123, 432); + CHECK_LOCATION(locationOf(result->statements.at(0)), "source0", 234, 543); + CHECK_LOCATION(locationOf(result->statements.at(1)), "source1", 123, 432); // [2] is inherited source location - CHECK_LOCATION(locationOf(result->statements.at(2)), g_strAlternateSourceText, 123, 432); + CHECK_LOCATION(locationOf(result->statements.at(2)), "source1", 123, 432); } BOOST_AUTO_TEST_CASE(customSourceLocations_block_nested) @@ -280,9 +275,9 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_block_nested) EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); - CHECK_LOCATION(result->debugData->location, sourceText, 234, 543); + CHECK_LOCATION(result->debugData->location, "source0", 234, 543); BOOST_REQUIRE_EQUAL(2, result->statements.size()); - CHECK_LOCATION(locationOf(result->statements.at(1)), sourceText, 343, 434); + CHECK_LOCATION(locationOf(result->statements.at(1)), "source0", 343, 434); } BOOST_AUTO_TEST_CASE(customSourceLocations_block_switch_case) @@ -304,19 +299,19 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_block_switch_case) EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); - CHECK_LOCATION(result->debugData->location, sourceText, 234, 543); + CHECK_LOCATION(result->debugData->location, "source0", 234, 543); BOOST_REQUIRE_EQUAL(2, result->statements.size()); BOOST_REQUIRE(holds_alternative(result->statements.at(1))); auto const& switchStmt = get(result->statements.at(1)); - CHECK_LOCATION(switchStmt.debugData->location, sourceText, 343, 434); + CHECK_LOCATION(switchStmt.debugData->location, "source0", 343, 434); BOOST_REQUIRE_EQUAL(1, switchStmt.cases.size()); - CHECK_LOCATION(switchStmt.cases.at(0).debugData->location, sourceText, 3141, 59265); + CHECK_LOCATION(switchStmt.cases.at(0).debugData->location, "source0", 3141, 59265); auto const& caseBody = switchStmt.cases.at(0).body; BOOST_REQUIRE_EQUAL(1, caseBody.statements.size()); - CHECK_LOCATION(locationOf(caseBody.statements.at(0)), sourceText, 271, 828); + CHECK_LOCATION(locationOf(caseBody.statements.at(0)), "source0", 271, 828); } BOOST_AUTO_TEST_CASE(customSourceLocations_inherit_into_outer_scope) @@ -337,19 +332,19 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_inherit_into_outer_scope) shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); - CHECK_LOCATION(result->debugData->location, sourceText, 1, 100); + CHECK_LOCATION(result->debugData->location, "source0", 1, 100); BOOST_REQUIRE_EQUAL(3, result->statements.size()); - CHECK_LOCATION(locationOf(result->statements.at(0)), sourceText, 1, 100); + CHECK_LOCATION(locationOf(result->statements.at(0)), "source0", 1, 100); // First child element must be a block itself with one statement. BOOST_REQUIRE(holds_alternative(result->statements.at(0))); BOOST_REQUIRE_EQUAL(get(result->statements.at(0)).statements.size(), 1); - CHECK_LOCATION(locationOf(get(result->statements.at(0)).statements.at(0)), sourceText, 123, 432); + CHECK_LOCATION(locationOf(get(result->statements.at(0)).statements.at(0)), "source0", 123, 432); // The next two elements have an inherited source location from the prior inner scope. - CHECK_LOCATION(locationOf(result->statements.at(1)), sourceText, 123, 432); - CHECK_LOCATION(locationOf(result->statements.at(2)), sourceText, 123, 432); + CHECK_LOCATION(locationOf(result->statements.at(1)), "source0", 123, 432); + CHECK_LOCATION(locationOf(result->statements.at(2)), "source0", 123, 432); } BOOST_AUTO_TEST_CASE(customSourceLocations_assign_empty) @@ -368,8 +363,8 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_assign_empty) shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); // should still parse BOOST_REQUIRE_EQUAL(2, result->statements.size()); - CHECK_LOCATION(locationOf(result->statements.at(0)), sourceText, 123, 432); - CHECK_LOCATION(locationOf(result->statements.at(1)), g_strAlternateSourceText, 1, 10); + CHECK_LOCATION(locationOf(result->statements.at(0)), "source0", 123, 432); + CHECK_LOCATION(locationOf(result->statements.at(1)), "source1", 1, 10); } BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_source_index) @@ -407,10 +402,10 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_1) BOOST_REQUIRE(!!result); BOOST_REQUIRE_EQUAL(1, result->statements.size()); - CHECK_LOCATION(locationOf(result->statements.at(0)), sourceText, 123, 432); + CHECK_LOCATION(locationOf(result->statements.at(0)), "source0", 123, 432); BOOST_REQUIRE(holds_alternative(result->statements.at(0))); VariableDeclaration const& varDecl = get(result->statements.at(0)); - CHECK_LOCATION(locationOf(*varDecl.value), sourceText, 234, 2026); + CHECK_LOCATION(locationOf(*varDecl.value), "source0", 234, 2026); } BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_2) @@ -429,20 +424,20 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_2) shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); BOOST_REQUIRE_EQUAL(1, result->statements.size()); - CHECK_LOCATION(result->debugData->location, sourceText, 0, 5); + CHECK_LOCATION(result->debugData->location, "source0", 0, 5); // `let x := add(1, ` BOOST_REQUIRE(holds_alternative(result->statements.at(0))); VariableDeclaration const& varDecl = get(result->statements.at(0)); - CHECK_LOCATION(varDecl.debugData->location, sourceText, 0, 5); + CHECK_LOCATION(varDecl.debugData->location, "source0", 0, 5); BOOST_REQUIRE(!!varDecl.value); BOOST_REQUIRE(holds_alternative(*varDecl.value)); FunctionCall const& call = get(*varDecl.value); - CHECK_LOCATION(call.debugData->location, g_strAlternateSourceText, 2, 3); + CHECK_LOCATION(call.debugData->location, "source1", 2, 3); // `2` BOOST_REQUIRE_EQUAL(2, call.arguments.size()); - CHECK_LOCATION(locationOf(call.arguments.at(1)), sourceText, 4, 8); + CHECK_LOCATION(locationOf(call.arguments.at(1)), "source0", 4, 8); } BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_3) @@ -463,24 +458,24 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_3) shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); BOOST_REQUIRE_EQUAL(2, result->statements.size()); - CHECK_LOCATION(result->debugData->location, g_strAlternateSourceText, 23, 45); + CHECK_LOCATION(result->debugData->location, "source1", 23, 45); BOOST_REQUIRE(holds_alternative(result->statements.at(0))); Block const& innerBlock = get(result->statements.at(0)); - CHECK_LOCATION(innerBlock.debugData->location, g_strAlternateSourceText, 23, 45); + CHECK_LOCATION(innerBlock.debugData->location, "source1", 23, 45); BOOST_REQUIRE_EQUAL(1, innerBlock.statements.size()); BOOST_REQUIRE(holds_alternative(result->statements.at(1))); ExpressionStatement const& sstoreStmt = get(innerBlock.statements.at(0)); BOOST_REQUIRE(holds_alternative(sstoreStmt.expression)); FunctionCall const& sstoreCall = get(sstoreStmt.expression); - CHECK_LOCATION(sstoreCall.debugData->location, g_strAlternateSourceText, 23, 45); + CHECK_LOCATION(sstoreCall.debugData->location, "source1", 23, 45); BOOST_REQUIRE(holds_alternative(result->statements.at(1))); ExpressionStatement mstoreStmt = get(result->statements.at(1)); BOOST_REQUIRE(holds_alternative(mstoreStmt.expression)); FunctionCall const& mstoreCall = get(mstoreStmt.expression); - CHECK_LOCATION(mstoreCall.debugData->location, sourceText, 420, 680); + CHECK_LOCATION(mstoreCall.debugData->location, "source0", 420, 680); } BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_comments_after_valid) @@ -499,11 +494,11 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_comments_after_valid) shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); BOOST_REQUIRE_EQUAL(1, result->statements.size()); - CHECK_LOCATION(result->debugData->location, g_strAlternateSourceText, 23, 45); + CHECK_LOCATION(result->debugData->location, "source1", 23, 45); BOOST_REQUIRE(holds_alternative(result->statements.at(0))); VariableDeclaration const& varDecl = get(result->statements.at(0)); - CHECK_LOCATION(varDecl.debugData->location, sourceText, 420, 680); + CHECK_LOCATION(varDecl.debugData->location, "source0", 420, 680); } BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_suffix) @@ -553,7 +548,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_ensure_last_match) VariableDeclaration const& varDecl = get(result->statements.at(0)); // Ensure the latest @src per documentation-comment is used (0:30:40). - CHECK_LOCATION(varDecl.debugData->location, sourceText, 30, 40); + CHECK_LOCATION(varDecl.debugData->location, "source0", 30, 40); } BOOST_AUTO_TEST_CASE(customSourceLocations_reference_original_sloc) @@ -573,6 +568,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_reference_original_sloc) BOOST_REQUIRE(holds_alternative(result->statements.at(0))); VariableDeclaration const& varDecl = get(result->statements.at(0)); + // -1 points to original source code, which in this case is `"source0"` (which is also CHECK_LOCATION(varDecl.debugData->location, "", 10, 20); } diff --git a/test/libyul/YulInterpreterTest.cpp b/test/libyul/YulInterpreterTest.cpp index f275e38a4..6f7da9f98 100644 --- a/test/libyul/YulInterpreterTest.cpp +++ b/test/libyul/YulInterpreterTest.cpp @@ -78,7 +78,8 @@ bool YulInterpreterTest::parse(ostream& _stream, string const& _linePrefix, bool else { AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; - printErrors(_stream, stack.errors()); + SourceReferenceFormatter{_stream, stack, true, false} + .printErrorInformation(stack.errors()); return false; } } @@ -101,11 +102,3 @@ string YulInterpreterTest::interpret() state.dumpTraceAndState(result); return result.str(); } - -void YulInterpreterTest::printErrors(ostream& _stream, ErrorList const& _errors) -{ - SourceReferenceFormatter formatter(_stream, true, false); - - for (auto const& error: _errors) - formatter.printErrorInformation(*error); -} diff --git a/test/libyul/YulInterpreterTest.h b/test/libyul/YulInterpreterTest.h index 88e535fb0..fc362ecf6 100644 --- a/test/libyul/YulInterpreterTest.h +++ b/test/libyul/YulInterpreterTest.h @@ -20,13 +20,6 @@ #include -namespace solidity::langutil -{ -class Scanner; -class Error; -using ErrorList = std::vector>; -} - namespace solidity::yul { struct AsmAnalysisInfo; @@ -52,8 +45,6 @@ private: bool parse(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted); std::string interpret(); - static void printErrors(std::ostream& _stream, langutil::ErrorList const& _errors); - std::shared_ptr m_ast; std::shared_ptr m_analysisInfo; }; diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 36d9cefc2..3454f09ac 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -28,6 +28,7 @@ #include #include +#include #include @@ -115,16 +116,10 @@ std::pair, std::shared_ptr> YulOptimize if (!object || !analysisInfo || !Error::containsOnlyWarnings(errors)) { AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; - printErrors(_stream, errors); + CharStream charStream(_source, ""); + SourceReferenceFormatter{_stream, SingletonCharStreamProvider(charStream), true, false} + .printErrorInformation(errors); return {}; } return {std::move(object), std::move(analysisInfo)}; } - -void YulOptimizerTest::printErrors(ostream& _stream, ErrorList const& _errors) -{ - SourceReferenceFormatter formatter(_stream, true, false); - - for (auto const& error: _errors) - formatter.printErrorInformation(*error); -} diff --git a/test/libyul/YulOptimizerTest.h b/test/libyul/YulOptimizerTest.h index b4ba4ca7a..832b5dfb8 100644 --- a/test/libyul/YulOptimizerTest.h +++ b/test/libyul/YulOptimizerTest.h @@ -51,7 +51,6 @@ private: std::pair, std::shared_ptr> parse( std::ostream& _stream, std::string const& _linePrefix, bool const _formatted, std::string const& _source ); - static void printErrors(std::ostream& _stream, langutil::ErrorList const& _errors); std::string m_optimizerStep; diff --git a/test/tools/ossfuzz/SolidityEvmoneInterface.cpp b/test/tools/ossfuzz/SolidityEvmoneInterface.cpp index 954ae2bf0..4d536183c 100644 --- a/test/tools/ossfuzz/SolidityEvmoneInterface.cpp +++ b/test/tools/ossfuzz/SolidityEvmoneInterface.cpp @@ -41,13 +41,11 @@ optional SolidityCompilationFramework::compileContract() { if (m_compilerInput.debugFailure) { - SourceReferenceFormatter formatter(cerr, false, false); - cerr << "Compiling contract failed" << endl; for (auto const& error: m_compiler.errors()) - formatter.printExceptionInformation( + cerr << SourceReferenceFormatter::formatErrorInformation( *error, - formatter.formatErrorInformation(*error) + m_compiler ); } return {}; diff --git a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp index d405c2bc8..1fe4e6eaa 100644 --- a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp +++ b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp @@ -43,20 +43,6 @@ using namespace solidity::yul; using namespace solidity::yul::test; using namespace solidity::yul::test::yul_fuzzer; -namespace -{ -void printErrors(ostream& _stream, ErrorList const& _errors) -{ - SourceReferenceFormatter formatter(_stream, false, false); - - for (auto const& error: _errors) - formatter.printExceptionInformation( - *error, - (error->type() == Error::Type::Warning) ? "Warning" : "Error" - ); -} -} - DEFINE_PROTO_FUZZER(Program const& _input) { ProtoConverter converter; @@ -88,7 +74,13 @@ DEFINE_PROTO_FUZZER(Program const& _input) !Error::containsOnlyWarnings(stack.errors()) ) { - printErrors(std::cout, stack.errors()); + SourceReferenceFormatter formatter(std::cout, stack, false, false); + + for (auto const& error: stack.errors()) + formatter.printExceptionInformation( + *error, + (error->type() == Error::Type::Warning) ? "Warning" : "Error" + ); yulAssert(false, "Proto fuzzer generated malformed program"); } diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index 1d3ca3868..aaf7cc516 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -44,6 +44,8 @@ #include #include +#include +#include #include #include @@ -78,17 +80,19 @@ class YulOpti public: void printErrors() { - SourceReferenceFormatter formatter(cerr, true, false); - - for (auto const& error: m_errors) - formatter.printErrorInformation(*error); + SourceReferenceFormatter{ + cerr, + SingletonCharStreamProvider(*m_scanner->charStream()), + true, + false + }.printErrorInformation(m_errors); } bool parse(string const& _input) { ErrorReporter errorReporter(m_errors); - shared_ptr scanner = make_shared(CharStream(_input, "")); - m_ast = yul::Parser(errorReporter, m_dialect).parse(scanner, false); + m_scanner = make_shared(CharStream(_input, "")); + m_ast = yul::Parser(errorReporter, m_dialect).parse(m_scanner, false); if (!m_ast || !errorReporter.errors().empty()) { cerr << "Error parsing source." << endl; @@ -230,6 +234,7 @@ public: private: ErrorList m_errors; + shared_ptr m_scanner; shared_ptr m_ast; Dialect const& m_dialect{EVMDialect::strictAssemblyForEVMObjects(EVMVersion{})}; shared_ptr m_analysisInfo; diff --git a/test/tools/yulrun.cpp b/test/tools/yulrun.cpp index b5f8d60dd..7906be744 100644 --- a/test/tools/yulrun.cpp +++ b/test/tools/yulrun.cpp @@ -53,12 +53,6 @@ namespace po = boost::program_options; namespace { -void printErrors(ErrorList const& _errors) -{ - for (auto const& error: _errors) - SourceReferenceFormatter(cout, true, false).printErrorInformation(*error); -} - pair, shared_ptr> parse(string const& _source) { AssemblyStack stack( @@ -73,7 +67,7 @@ pair, shared_ptr> parse(string const& _source } else { - printErrors(stack.errors()); + SourceReferenceFormatter(cout, stack, true, false).printErrorInformation(stack.errors()); return {}; } } diff --git a/tools/solidityUpgrade/SourceTransform.h b/tools/solidityUpgrade/SourceTransform.h index 65226a442..04d128298 100644 --- a/tools/solidityUpgrade/SourceTransform.h +++ b/tools/solidityUpgrade/SourceTransform.h @@ -22,6 +22,9 @@ #include +#include +#include + #include #include @@ -47,42 +50,57 @@ public: operator std::string() { return m_stream.str(); } }; +class SourceTextRetriever +{ +public: + explicit SourceTextRetriever(langutil::CharStreamProvider const& _charStreamProvider): + m_charStreamProvider(_charStreamProvider) {} + std::string text(langutil::SourceLocation const& _location) const + { + solAssert(_location.hasText(), ""); + return std::string{m_charStreamProvider.charStream(*_location.sourceName).text(_location)}; + } +protected: + langutil::CharStreamProvider const& m_charStreamProvider; +}; /** * Helper that provides functions which analyze certain source locations * on a textual base. They utilize regular expression to search for * keywords or to determine formatting. */ -class SourceAnalysis +class SourceAnalysis: public SourceTextRetriever { public: - static bool isMultilineKeyword( + using SourceTextRetriever::SourceTextRetriever; + + bool isMultilineKeyword( langutil::SourceLocation const& _location, std::string const& _keyword - ) + ) const { return regex_search( - _location.text(), + text(_location), std::regex{"(\\b" + _keyword + "\\b\\n|\\r|\\r\\n)"} ); } - static bool hasMutabilityKeyword(langutil::SourceLocation const& _location) + bool hasMutabilityKeyword(langutil::SourceLocation const& _location) const { return regex_search( - _location.text(), + text(_location), std::regex{"(\\b(pure|view|nonpayable|payable)\\b)"} ); } - static bool hasVirtualKeyword(langutil::SourceLocation const& _location) + bool hasVirtualKeyword(langutil::SourceLocation const& _location) const { - return regex_search(_location.text(), std::regex{"(\\b(virtual)\\b)"}); + return regex_search(text(_location), std::regex{"(\\b(virtual)\\b)"}); } - static bool hasVisibilityKeyword(langutil::SourceLocation const& _location) + bool hasVisibilityKeyword(langutil::SourceLocation const& _location) const { - return regex_search(_location.text(), std::regex{"\\bpublic\\b"}); + return regex_search(text(_location), std::regex{"\\bpublic\\b"}); } }; @@ -117,21 +135,23 @@ public: * on a textual base. In general, these utilize regular expressions applied * to the given source location. */ -class SourceTransform +class SourceTransform: public SourceTextRetriever { public: + using SourceTextRetriever::SourceTextRetriever; + /// Searches for the keyword given and prepends the expression. /// E.g. `function f() view;` -> `function f() public view;` - static std::string insertBeforeKeyword( + std::string insertBeforeKeyword( langutil::SourceLocation const& _location, std::string const& _keyword, std::string const& _expression - ) + ) const { auto _regex = std::regex{"(\\b" + _keyword + "\\b)"}; - if (regex_search(_location.text(), _regex)) + if (regex_search(text(_location), _regex)) return regex_replace( - _location.text(), + text(_location), _regex, _expression + " " + _keyword, std::regex_constants::format_first_only @@ -139,7 +159,7 @@ public: else solAssert( false, - LocationHelper() << "Could not fix: " << _location.text() << " at " << _location << + LocationHelper() << "Could not fix: " << text(_location) << " at " << _location << "\nNeeds to be fixed manually." ); @@ -148,22 +168,22 @@ public: /// Searches for the keyword given and appends the expression. /// E.g. `function f() public {}` -> `function f() public override {}` - static std::string insertAfterKeyword( + std::string insertAfterKeyword( langutil::SourceLocation const& _location, std::string const& _keyword, std::string const& _expression - ) + ) const { - bool isMultiline = SourceAnalysis::isMultilineKeyword(_location, _keyword); + bool isMultiline = SourceAnalysis{m_charStreamProvider}.isMultilineKeyword(_location, _keyword); std::string toAppend = isMultiline ? ("\n " + _expression) : (" " + _expression); std::regex keyword{"(\\b" + _keyword + "\\b)"}; - if (regex_search(_location.text(), keyword)) - return regex_replace(_location.text(), keyword, _keyword + toAppend); + if (regex_search(text(_location), keyword)) + return regex_replace(text(_location), keyword, _keyword + toAppend); else solAssert( false, - LocationHelper() << "Could not fix: " << _location.text() << " at " << _location << + LocationHelper() << "Could not fix: " << text(_location) << " at " << _location << "\nNeeds to be fixed manually." ); @@ -174,22 +194,22 @@ public: /// Searches for the first right parenthesis and appends the expression /// given. /// E.g. `function f() {}` -> `function f() public {}` - static std::string insertAfterRightParenthesis( + std::string insertAfterRightParenthesis( langutil::SourceLocation const& _location, std::string const& _expression - ) + ) const { auto _regex = std::regex{"(\\))"}; - if (regex_search(_location.text(), _regex)) + if (regex_search(text(_location), _regex)) return regex_replace( - _location.text(), + text(_location), std::regex{"(\\))"}, ") " + _expression ); else solAssert( false, - LocationHelper() << "Could not fix: " << _location.text() << " at " << _location << + LocationHelper() << "Could not fix: " << text(_location) << " at " << _location << "\nNeeds to be fixed manually." ); @@ -199,38 +219,38 @@ public: /// Searches for the `function` keyword and its identifier and replaces /// both by the expression given. /// E.g. `function Storage() {}` -> `constructor() {}` - static std::string replaceFunctionName( + std::string replaceFunctionName( langutil::SourceLocation const& _location, std::string const& _name, std::string const& _expression - ) + ) const { auto _regex = std::regex{ "(\\bfunction\\s*" + _name + "\\b)"}; - if (regex_search(_location.text(), _regex)) + if (regex_search(text(_location), _regex)) return regex_replace( - _location.text(), + text(_location), _regex, _expression ); else solAssert( false, - LocationHelper() << "Could not fix: " << _location.text() << " at " << _location << + LocationHelper() << "Could not fix: " << text(_location) << " at " << _location << "\nNeeds to be fixed manually." ); return ""; } - static std::string gasUpdate(langutil::SourceLocation const& _location) + std::string gasUpdate(langutil::SourceLocation const& _location) const { // dot, "gas", any number of whitespaces, left bracket std::regex gasReg{"\\.gas\\s*\\("}; - if (regex_search(_location.text(), gasReg)) + if (regex_search(text(_location), gasReg)) { std::string out = regex_replace( - _location.text(), + text(_location), gasReg, "{gas: ", std::regex_constants::format_first_only @@ -240,22 +260,22 @@ public: else solAssert( false, - LocationHelper() << "Could not fix: " << _location.text() << " at " << _location << + LocationHelper() << "Could not fix: " << text(_location) << " at " << _location << "\nNeeds to be fixed manually." ); return ""; } - static std::string valueUpdate(langutil::SourceLocation const& _location) + std::string valueUpdate(langutil::SourceLocation const& _location) const { // dot, "value", any number of whitespaces, left bracket std::regex valueReg{"\\.value\\s*\\("}; - if (regex_search(_location.text(), valueReg)) + if (regex_search(text(_location), valueReg)) { std::string out = regex_replace( - _location.text(), + text(_location), valueReg, "{value: ", std::regex_constants::format_first_only @@ -265,21 +285,21 @@ public: else solAssert( false, - LocationHelper() << "Could not fix: " << _location.text() << " at " << _location << + LocationHelper() << "Could not fix: " << text(_location) << " at " << _location << "\nNeeds to be fixed manually." ); return ""; } - static std::string nowUpdate(langutil::SourceLocation const& _location) + std::string nowUpdate(langutil::SourceLocation const& _location) const { - return regex_replace(_location.text(), std::regex{"now"}, "block.timestamp"); + return regex_replace(text(_location), std::regex{"now"}, "block.timestamp"); } - static std::string removeVisibility(langutil::SourceLocation const& _location) + std::string removeVisibility(langutil::SourceLocation const& _location) const { - std::string replacement = _location.text(); + std::string replacement = text(_location); for (auto const& replace: {"public ", "public", "internal ", "internal", "external ", "external"}) replacement = regex_replace(replacement, std::regex{replace}, ""); return replacement; diff --git a/tools/solidityUpgrade/SourceUpgrade.cpp b/tools/solidityUpgrade/SourceUpgrade.cpp index 540a042a7..6b9bcd1d0 100644 --- a/tools/solidityUpgrade/SourceUpgrade.cpp +++ b/tools/solidityUpgrade/SourceUpgrade.cpp @@ -347,14 +347,14 @@ bool SourceUpgrade::analyzeAndUpgrade(pair const& _sourceCode) log() << "Analyzing and upgrading " << _sourceCode.first << "." << endl; if (m_compiler->state() >= CompilerStack::State::AnalysisPerformed) - m_suite.analyze(m_compiler->ast(_sourceCode.first)); + m_suite.analyze(*m_compiler, m_compiler->ast(_sourceCode.first)); if (!m_suite.changes().empty()) { auto& change = m_suite.changes().front(); if (verbose) - change.log(true); + change.log(*m_compiler, true); if (change.level() == UpgradeChange::Level::Safe) { @@ -388,20 +388,19 @@ void SourceUpgrade::applyChange( log() << _change.patch(); } - _change.apply(); - m_sourceCodes[_sourceCode.first] = _change.source(); + m_sourceCodes[_sourceCode.first] = _change.apply(_sourceCode.second); if (!dryRun) - writeInputFile(_sourceCode.first, _change.source()); + writeInputFile(_sourceCode.first, m_sourceCodes[_sourceCode.first]); } void SourceUpgrade::printErrors() const { - auto formatter = make_unique(cout, true, false); + langutil::SourceReferenceFormatter formatter{cout, *m_compiler, true, false}; for (auto const& error: m_compiler->errors()) if (error->type() != langutil::Error::Type::Warning) - formatter->printErrorInformation(*error); + formatter.printErrorInformation(*error); } void SourceUpgrade::printStatistics() const diff --git a/tools/solidityUpgrade/SourceUpgrade.h b/tools/solidityUpgrade/SourceUpgrade.h index f70d24080..11e7e12a9 100644 --- a/tools/solidityUpgrade/SourceUpgrade.h +++ b/tools/solidityUpgrade/SourceUpgrade.h @@ -68,29 +68,29 @@ private: class Suite: public UpgradeSuite { public: - void analyze(frontend::SourceUnit const& _sourceUnit) + void analyze(langutil::CharStreamProvider const& _charStreamProvider, frontend::SourceUnit const& _sourceUnit) { /// Solidity 0.5.0 if (isActivated(Module::ConstructorKeyword)) - ConstructorKeyword{m_changes}.analyze(_sourceUnit); + ConstructorKeyword{_charStreamProvider, m_changes}.analyze(_sourceUnit); if (isActivated(Module::VisibilitySpecifier)) - VisibilitySpecifier{m_changes}.analyze(_sourceUnit); + VisibilitySpecifier{_charStreamProvider, m_changes}.analyze(_sourceUnit); /// Solidity 0.6.0 if (isActivated(Module::AbstractContract)) - AbstractContract{m_changes}.analyze(_sourceUnit); + AbstractContract{_charStreamProvider, m_changes}.analyze(_sourceUnit); if (isActivated(Module::OverridingFunction)) - OverridingFunction{m_changes}.analyze(_sourceUnit); + OverridingFunction{_charStreamProvider, m_changes}.analyze(_sourceUnit); if (isActivated(Module::VirtualFunction)) - VirtualFunction{m_changes}.analyze(_sourceUnit); + VirtualFunction{_charStreamProvider, m_changes}.analyze(_sourceUnit); /// Solidity 0.7.0 if (isActivated(Module::DotSyntax)) - DotSyntax{m_changes}.analyze(_sourceUnit); + DotSyntax{_charStreamProvider, m_changes}.analyze(_sourceUnit); if (isActivated(Module::NowKeyword)) - NowKeyword{m_changes}.analyze(_sourceUnit); + NowKeyword{_charStreamProvider, m_changes}.analyze(_sourceUnit); if (isActivated(Module::ConstrutorVisibility)) - ConstructorVisibility{m_changes}.analyze(_sourceUnit); + ConstructorVisibility{_charStreamProvider, m_changes}.analyze(_sourceUnit); } void activateModule(Module _module) { m_modules.insert(_module); } diff --git a/tools/solidityUpgrade/Upgrade050.cpp b/tools/solidityUpgrade/Upgrade050.cpp index 9c0ecd656..c8b4de9e8 100644 --- a/tools/solidityUpgrade/Upgrade050.cpp +++ b/tools/solidityUpgrade/Upgrade050.cpp @@ -36,7 +36,7 @@ void ConstructorKeyword::endVisit(ContractDefinition const& _contract) m_changes.emplace_back( UpgradeChange::Level::Safe, function->location(), - SourceTransform::replaceFunctionName( + SourceTransform{m_charStreamProvider}.replaceFunctionName( function->location(), function->name(), "constructor" @@ -50,6 +50,6 @@ void VisibilitySpecifier::endVisit(FunctionDefinition const& _function) m_changes.emplace_back( UpgradeChange::Level::Safe, _function.location(), - SourceTransform::insertAfterRightParenthesis(_function.location(), "public") + SourceTransform{m_charStreamProvider}.insertAfterRightParenthesis(_function.location(), "public") ); } diff --git a/tools/solidityUpgrade/Upgrade060.cpp b/tools/solidityUpgrade/Upgrade060.cpp index c614f89d0..52010db39 100644 --- a/tools/solidityUpgrade/Upgrade060.cpp +++ b/tools/solidityUpgrade/Upgrade060.cpp @@ -29,75 +29,6 @@ using namespace solidity; using namespace solidity::frontend; using namespace solidity::tools; -using Contracts = set; - -namespace -{ - -inline string appendOverride( - FunctionDefinition const& _function, - Contracts const& _expectedContracts -) -{ - auto location = _function.location(); - string upgradedCode; - string overrideExpression = SourceGeneration::functionOverride(_expectedContracts); - - if (SourceAnalysis::hasVirtualKeyword(location)) - upgradedCode = SourceTransform::insertAfterKeyword( - location, - "virtual", - overrideExpression - ); - else if (SourceAnalysis::hasMutabilityKeyword(location)) - upgradedCode = SourceTransform::insertAfterKeyword( - location, - stateMutabilityToString(_function.stateMutability()), - overrideExpression - ); - else if (SourceAnalysis::hasVisibilityKeyword(location)) - upgradedCode = SourceTransform::insertAfterKeyword( - location, - Declaration::visibilityToString(_function.visibility()), - overrideExpression - ); - else - upgradedCode = SourceTransform::insertAfterRightParenthesis( - location, - overrideExpression - ); - - return upgradedCode; -} - -inline string appendVirtual(FunctionDefinition const& _function) -{ - auto location = _function.location(); - string upgradedCode; - - if (SourceAnalysis::hasMutabilityKeyword(location)) - upgradedCode = SourceTransform::insertAfterKeyword( - location, - stateMutabilityToString(_function.stateMutability()), - "virtual" - ); - else if (SourceAnalysis::hasVisibilityKeyword(location)) - upgradedCode = SourceTransform::insertAfterKeyword( - location, - Declaration::visibilityToString(_function.visibility()), - "virtual" - ); - else - upgradedCode = SourceTransform::insertAfterRightParenthesis( - _function.location(), - "virtual" - ); - - return upgradedCode; -} - -} - void AbstractContract::endVisit(ContractDefinition const& _contract) { bool isFullyImplemented = _contract.annotation().unimplementedDeclarations->empty(); @@ -110,7 +41,7 @@ void AbstractContract::endVisit(ContractDefinition const& _contract) m_changes.emplace_back( UpgradeChange::Level::Safe, _contract.location(), - SourceTransform::insertBeforeKeyword(_contract.location(), "contract", "abstract") + SourceTransform{m_charStreamProvider}.insertBeforeKeyword(_contract.location(), "contract", "abstract") ); } @@ -159,6 +90,42 @@ void OverridingFunction::endVisit(ContractDefinition const& _contract) } } +string OverridingFunction::appendOverride( + FunctionDefinition const& _function, + Contracts const& _expectedContracts +) +{ + auto location = _function.location(); + string upgradedCode; + string overrideExpression = SourceGeneration::functionOverride(_expectedContracts); + + if (SourceAnalysis{m_charStreamProvider}.hasVirtualKeyword(location)) + upgradedCode = SourceTransform{m_charStreamProvider}.insertAfterKeyword( + location, + "virtual", + overrideExpression + ); + else if (SourceAnalysis{m_charStreamProvider}.hasMutabilityKeyword(location)) + upgradedCode = SourceTransform{m_charStreamProvider}.insertAfterKeyword( + location, + stateMutabilityToString(_function.stateMutability()), + overrideExpression + ); + else if (SourceAnalysis{m_charStreamProvider}.hasVisibilityKeyword(location)) + upgradedCode = SourceTransform{m_charStreamProvider}.insertAfterKeyword( + location, + Declaration::visibilityToString(_function.visibility()), + overrideExpression + ); + else + upgradedCode = SourceTransform{m_charStreamProvider}.insertAfterRightParenthesis( + location, + overrideExpression + ); + + return upgradedCode; +} + void VirtualFunction::endVisit(ContractDefinition const& _contract) { auto const& inheritedFunctions = m_overrideChecker.inheritedFunctions(_contract); @@ -192,12 +159,39 @@ void VirtualFunction::endVisit(ContractDefinition const& _contract) ) { m_changes.emplace_back( - UpgradeChange::Level::Safe, - function->location(), - appendVirtual(*function) + UpgradeChange::Level::Safe, + function->location(), + appendVirtual(*function) ); } } } } } + +string VirtualFunction::appendVirtual(FunctionDefinition const& _function) const +{ + auto location = _function.location(); + string upgradedCode; + + if (SourceAnalysis{m_charStreamProvider}.hasMutabilityKeyword(location)) + upgradedCode = SourceTransform{m_charStreamProvider}.insertAfterKeyword( + location, + stateMutabilityToString(_function.stateMutability()), + "virtual" + ); + else if (SourceAnalysis{m_charStreamProvider}.hasVisibilityKeyword(location)) + upgradedCode = SourceTransform{m_charStreamProvider}.insertAfterKeyword( + location, + Declaration::visibilityToString(_function.visibility()), + "virtual" + ); + else + upgradedCode = SourceTransform{m_charStreamProvider}.insertAfterRightParenthesis( + _function.location(), + "virtual" + ); + + return upgradedCode; +} + diff --git a/tools/solidityUpgrade/Upgrade060.h b/tools/solidityUpgrade/Upgrade060.h index 9a8880b96..46b10a3a0 100644 --- a/tools/solidityUpgrade/Upgrade060.h +++ b/tools/solidityUpgrade/Upgrade060.h @@ -50,7 +50,14 @@ public: void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); } private: + using Contracts = std::set; + void endVisit(frontend::ContractDefinition const& _contract) override; + + std::string appendOverride( + frontend::FunctionDefinition const& _function, + Contracts const& _expectedContracts + ); }; /** @@ -65,6 +72,8 @@ public: void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); } private: void endVisit(frontend::ContractDefinition const& _function) override; + + std::string appendVirtual(frontend::FunctionDefinition const& _function) const; }; } diff --git a/tools/solidityUpgrade/Upgrade070.cpp b/tools/solidityUpgrade/Upgrade070.cpp index 04b88f7ca..6a26b1400 100644 --- a/tools/solidityUpgrade/Upgrade070.cpp +++ b/tools/solidityUpgrade/Upgrade070.cpp @@ -29,14 +29,14 @@ void DotSyntax::endVisit(FunctionCall const& _functionCall) m_changes.emplace_back( UpgradeChange::Level::Safe, _functionCall.location(), - SourceTransform::valueUpdate(_functionCall.location()) + SourceTransform{m_charStreamProvider}.valueUpdate(_functionCall.location()) ); if (funcType->gasSet()) m_changes.emplace_back( UpgradeChange::Level::Safe, _functionCall.location(), - SourceTransform::gasUpdate(_functionCall.location()) + SourceTransform{m_charStreamProvider}.gasUpdate(_functionCall.location()) ); } } @@ -55,7 +55,7 @@ void NowKeyword::endVisit(Identifier const& _identifier) m_changes.emplace_back( UpgradeChange::Level::Safe, _identifier.location(), - SourceTransform::nowUpdate(_identifier.location()) + SourceTransform{m_charStreamProvider}.nowUpdate(_identifier.location()) ); } } @@ -72,7 +72,7 @@ void ConstructorVisibility::endVisit(ContractDefinition const& _contract) m_changes.emplace_back( UpgradeChange::Level::Safe, _contract.location(), - SourceTransform::insertBeforeKeyword(_contract.location(), "contract", "abstract") + SourceTransform{m_charStreamProvider}.insertBeforeKeyword(_contract.location(), "contract", "abstract") ); for (FunctionDefinition const* function: _contract.definedFunctions()) @@ -80,6 +80,6 @@ void ConstructorVisibility::endVisit(ContractDefinition const& _contract) m_changes.emplace_back( UpgradeChange::Level::Safe, function->location(), - SourceTransform::removeVisibility(function->location()) + SourceTransform{m_charStreamProvider}.removeVisibility(function->location()) ); } diff --git a/tools/solidityUpgrade/UpgradeChange.cpp b/tools/solidityUpgrade/UpgradeChange.cpp index 51475ffd3..2976efe5d 100644 --- a/tools/solidityUpgrade/UpgradeChange.cpp +++ b/tools/solidityUpgrade/UpgradeChange.cpp @@ -26,18 +26,20 @@ using namespace solidity::langutil; using namespace solidity::util; using namespace solidity::tools; -void UpgradeChange::apply() +string UpgradeChange::apply(string _source) const { - m_source.replace( + _source.replace( static_cast(m_location.start), - static_cast(m_location.end - m_location.start), m_patch + static_cast(m_location.end - m_location.start), + m_patch ); + return _source; } -void UpgradeChange::log(bool const _shorten) const +void UpgradeChange::log(CharStreamProvider const& _charStreamProvider, bool const _shorten) const { stringstream os; - SourceReferenceFormatter formatter{os, true, false}; + SourceReferenceFormatter formatter{os, _charStreamProvider, true, false}; string start = to_string(m_location.start); string end = to_string(m_location.end); @@ -48,10 +50,10 @@ void UpgradeChange::log(bool const _shorten) const os << endl; AnsiColorized(os, true, {formatting::BOLD, color}) << "Upgrade change (" << level << ")" << endl; os << "=======================" << endl; - formatter.printSourceLocation(SourceReferenceExtractor::extract(&m_location)); + formatter.printSourceLocation(SourceReferenceExtractor::extract(_charStreamProvider, &m_location)); os << endl; - LineColumn lineEnd = m_location.source->translatePositionToLineColumn(m_location.end); + LineColumn lineEnd = _charStreamProvider.charStream(*m_location.sourceName).translatePositionToLineColumn(m_location.end); int const leftpad = static_cast(log10(max(lineEnd.line, 1))) + 2; stringstream output; diff --git a/tools/solidityUpgrade/UpgradeChange.h b/tools/solidityUpgrade/UpgradeChange.h index 19f08c6a0..6cccc154b 100644 --- a/tools/solidityUpgrade/UpgradeChange.h +++ b/tools/solidityUpgrade/UpgradeChange.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -47,32 +48,27 @@ public: Level _level, langutil::SourceLocation _location, std::string _patch - ) - : + ): m_location(_location), - m_source(_location.source->source()), m_patch(std::move(_patch)), - m_level(_level) {} + m_level(_level) + {} ~UpgradeChange() {} - langutil::SourceLocation const& location() { return m_location; } - std::string source() const { return m_source; } - std::string patch() { return m_patch; } + langutil::SourceLocation const& location() const { return m_location; } + std::string patch() const { return m_patch; } Level level() const { return m_level; } - /// Does the actual replacement of code under at current source location. - /// The change is applied on the upgrade-specific copy of source code. - /// The altered code is then requested by the upgrade routine later on. - void apply(); - /// Does a pretty-print of this upgrade change. It uses a source formatter - /// provided by the compiler in order to print affected code. Since the patch + /// Performs the actual replacement on the provided original source code + /// and returns the modified source code. + std::string apply(std::string _source) const; + /// Does a pretty-print of this upgrade change. Since the patch /// can contain a lot of code lines, it can be shortened, which is signaled /// by setting the flag. - void log(bool const _shorten = true) const; + void log(langutil::CharStreamProvider const& _charStreamProvider, bool const _shorten = true) const; private: langutil::SourceLocation m_location; - std::string m_source; std::string m_patch; Level m_level; diff --git a/tools/solidityUpgrade/UpgradeSuite.h b/tools/solidityUpgrade/UpgradeSuite.h index 33aeb40eb..2bde6ec0e 100644 --- a/tools/solidityUpgrade/UpgradeSuite.h +++ b/tools/solidityUpgrade/UpgradeSuite.h @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -37,13 +38,17 @@ namespace solidity::tools class Upgrade { public: - Upgrade(std::vector& _changes): m_changes(_changes) {} + Upgrade( + langutil::CharStreamProvider const& _charStreamProvider, + std::vector& _changes + ): m_changes(_changes), m_charStreamProvider(_charStreamProvider) {} protected: /// A reference to a suite-specific set of changes. /// It is passed to all upgrade modules and meant to collect /// reported changes. std::vector& m_changes; + langutil::CharStreamProvider const& m_charStreamProvider; }; /** @@ -53,8 +58,11 @@ protected: class AnalysisUpgrade: public Upgrade, public frontend::ASTConstVisitor { public: - AnalysisUpgrade(std::vector& _changes): - Upgrade(_changes), + AnalysisUpgrade( + langutil::CharStreamProvider const& _charStreamProvider, + std::vector& _changes + ): + Upgrade(_charStreamProvider, _changes), m_errorReporter(m_errors), m_overrideChecker(m_errorReporter) {} diff --git a/tools/yulPhaser/Phaser.cpp b/tools/yulPhaser/Phaser.cpp index ca15f3ccd..db7d79c31 100644 --- a/tools/yulPhaser/Phaser.cpp +++ b/tools/yulPhaser/Phaser.cpp @@ -27,6 +27,8 @@ #include #include +#include +#include #include #include @@ -389,7 +391,9 @@ vector ProgramFactory::build(Options const& _options) variant programOrErrors = Program::load(sourceCode); if (holds_alternative(programOrErrors)) { - cerr << get(programOrErrors) << endl; + SourceReferenceFormatter{cerr, SingletonCharStreamProvider(sourceCode), true, false} + .printErrorInformation(get(programOrErrors)); + cerr << endl; assertThrow(false, InvalidProgram, "Failed to load program " + path); } diff --git a/tools/yulPhaser/Program.cpp b/tools/yulPhaser/Program.cpp index e01581d6a..2635b27fa 100644 --- a/tools/yulPhaser/Program.cpp +++ b/tools/yulPhaser/Program.cpp @@ -60,16 +60,6 @@ ostream& operator<<(ostream& _stream, Program const& _program); } -ostream& std::operator<<(ostream& _outputStream, ErrorList const& _errors) -{ - SourceReferenceFormatter formatter(_outputStream, true, false); - - for (auto const& error: _errors) - formatter.printErrorInformation(*error); - - return _outputStream; -} - Program::Program(Program const& program): m_ast(make_unique(get(ASTCopier{}(*program.m_ast)))), m_dialect{program.m_dialect}, diff --git a/tools/yulPhaser/Program.h b/tools/yulPhaser/Program.h index d0eb8e197..e42471d77 100644 --- a/tools/yulPhaser/Program.h +++ b/tools/yulPhaser/Program.h @@ -35,6 +35,7 @@ namespace solidity::langutil { class CharStream; +class Scanner; } @@ -47,13 +48,6 @@ struct CodeWeights; } -namespace std -{ - -std::ostream& operator<<(std::ostream& _outputStream, solidity::langutil::ErrorList const& _errors); - -} - namespace solidity::phaser { From 01dc77e5a2e301f798ff48ba41fb536ae4d32a72 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 1 Jul 2021 16:58:01 +0200 Subject: [PATCH 34/98] Properly assign source names for AST import. --- liblangutil/SourceLocation.cpp | 20 ++++++++++---------- liblangutil/SourceLocation.h | 5 ++--- libsolidity/ast/ASTJsonImporter.cpp | 12 +++++------- libsolidity/ast/ASTJsonImporter.h | 7 ++----- libyul/AsmJsonImporter.cpp | 2 +- libyul/AsmJsonImporter.h | 7 ++++--- 6 files changed, 24 insertions(+), 29 deletions(-) diff --git a/liblangutil/SourceLocation.cpp b/liblangutil/SourceLocation.cpp index 76270829d..058cc6b57 100644 --- a/liblangutil/SourceLocation.cpp +++ b/liblangutil/SourceLocation.cpp @@ -22,31 +22,31 @@ #include using namespace solidity; -namespace solidity::langutil -{ +using namespace solidity::langutil; +using namespace std; -SourceLocation const parseSourceLocation(std::string const& _input, std::string const& _sourceName, size_t _maxIndex) +SourceLocation solidity::langutil::parseSourceLocation(string const& _input, vector> const& _sourceNames) { // Expected input: "start:length:sourceindex" - enum SrcElem : size_t { Start, Length, Index }; + enum SrcElem: size_t { Start, Length, Index }; - std::vector pos; + vector pos; boost::algorithm::split(pos, _input, boost::is_any_of(":")); - // TODO What to do with sourceIndex? solAssert(pos.size() == 3, "SourceLocation string must have 3 colon separated numeric fields."); auto const sourceIndex = stoi(pos[Index]); astAssert( - sourceIndex == -1 || _maxIndex >= static_cast(sourceIndex), + sourceIndex == -1 || (0 <= sourceIndex && static_cast(sourceIndex) < _sourceNames.size()), "'src'-field ill-formatted or src-index too high" ); int start = stoi(pos[Start]); int end = start + stoi(pos[Length]); - return SourceLocation{start, end, std::make_shared(_sourceName)}; -} - + SourceLocation result{start, end, {}}; + if (sourceIndex != -1) + result.sourceName = _sourceNames.at(static_cast(sourceIndex)); + return result; } diff --git a/liblangutil/SourceLocation.h b/liblangutil/SourceLocation.h index 71e5ad711..845b269ca 100644 --- a/liblangutil/SourceLocation.h +++ b/liblangutil/SourceLocation.h @@ -106,10 +106,9 @@ struct SourceLocation std::shared_ptr sourceName; }; -SourceLocation const parseSourceLocation( +SourceLocation parseSourceLocation( std::string const& _input, - std::string const& _sourceName, - size_t _maxIndex = std::numeric_limits::max() + std::vector> const& _sourceNames ); /// Stream output for Location (used e.g. in boost exceptions). diff --git a/libsolidity/ast/ASTJsonImporter.cpp b/libsolidity/ast/ASTJsonImporter.cpp index 916dbac14..041bf49a6 100644 --- a/libsolidity/ast/ASTJsonImporter.cpp +++ b/libsolidity/ast/ASTJsonImporter.cpp @@ -57,14 +57,12 @@ ASTPointer ASTJsonImporter::nullOrCast(Json::Value const& _json) map> ASTJsonImporter::jsonToSourceUnit(map const& _sourceList) { - m_sourceList = _sourceList; for (auto const& src: _sourceList) - m_sourceLocations.emplace_back(make_shared(src.first)); - for (auto const& srcPair: m_sourceList) + m_sourceNames.emplace_back(make_shared(src.first)); + for (auto const& srcPair: _sourceList) { astAssert(!srcPair.second.isNull(), ""); astAssert(member(srcPair.second,"nodeType") == "SourceUnit", "The 'nodeType' of the highest node must be 'SourceUnit'."); - m_currentSourceName = srcPair.first; m_sourceUnits[srcPair.first] = createSourceUnit(srcPair.second, srcPair.first); } return m_sourceUnits; @@ -94,14 +92,14 @@ SourceLocation const ASTJsonImporter::createSourceLocation(Json::Value const& _n { astAssert(member(_node, "src").isString(), "'src' must be a string"); - return solidity::langutil::parseSourceLocation(_node["src"].asString(), m_currentSourceName, m_sourceLocations.size()); + return solidity::langutil::parseSourceLocation(_node["src"].asString(), m_sourceNames); } SourceLocation ASTJsonImporter::createNameSourceLocation(Json::Value const& _node) { astAssert(member(_node, "nameLocation").isString(), "'nameLocation' must be a string"); - return solidity::langutil::parseSourceLocation(_node["nameLocation"].asString(), m_currentSourceName, m_sourceLocations.size()); + return solidity::langutil::parseSourceLocation(_node["nameLocation"].asString(), m_sourceNames); } template @@ -616,7 +614,7 @@ ASTPointer ASTJsonImporter::createInlineAssembly(Json::Value con astAssert(m_evmVersion == evmVersion, "Imported tree evm version differs from configured evm version!"); yul::Dialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(evmVersion.value()); - shared_ptr operations = make_shared(yul::AsmJsonImporter(m_currentSourceName).createBlock(member(_node, "AST"))); + shared_ptr operations = make_shared(yul::AsmJsonImporter(m_sourceNames).createBlock(member(_node, "AST"))); return createASTNode( _node, nullOrASTString(_node, "documentation"), diff --git a/libsolidity/ast/ASTJsonImporter.h b/libsolidity/ast/ASTJsonImporter.h index a4d43293f..3f1f5265c 100644 --- a/libsolidity/ast/ASTJsonImporter.h +++ b/libsolidity/ast/ASTJsonImporter.h @@ -152,13 +152,10 @@ private: ///@} // =========== member variables =============== - /// Stores filepath as sourcenames to AST in JSON format - std::map m_sourceList; - /// list of filepaths (used as sourcenames) - std::vector> m_sourceLocations; + /// list of source names, order by source index + std::vector> m_sourceNames; /// filepath to AST std::map> m_sourceUnits; - std::string m_currentSourceName; /// IDs already used by the nodes std::set m_usedIDs; /// Configured EVM version diff --git a/libyul/AsmJsonImporter.cpp b/libyul/AsmJsonImporter.cpp index de4e06870..87ce31497 100644 --- a/libyul/AsmJsonImporter.cpp +++ b/libyul/AsmJsonImporter.cpp @@ -45,7 +45,7 @@ SourceLocation const AsmJsonImporter::createSourceLocation(Json::Value const& _n { yulAssert(member(_node, "src").isString(), "'src' must be a string"); - return solidity::langutil::parseSourceLocation(_node["src"].asString(), m_sourceName); + return solidity::langutil::parseSourceLocation(_node["src"].asString(), m_sourceNames); } template diff --git a/libyul/AsmJsonImporter.h b/libyul/AsmJsonImporter.h index e491430b6..506352fa4 100644 --- a/libyul/AsmJsonImporter.h +++ b/libyul/AsmJsonImporter.h @@ -38,7 +38,9 @@ namespace solidity::yul class AsmJsonImporter { public: - explicit AsmJsonImporter(std::string _sourceName) : m_sourceName(std::move(_sourceName)) {} + explicit AsmJsonImporter(std::vector> const& _sourceNames): + m_sourceNames(_sourceNames) + {} yul::Block createBlock(Json::Value const& _node); private: @@ -70,8 +72,7 @@ private: yul::Break createBreak(Json::Value const& _node); yul::Continue createContinue(Json::Value const& _node); - std::string m_sourceName; - + std::vector> const& m_sourceNames; }; } From e3525b81d096977ab14f9d619664016c51b75f0a Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 1 Jul 2021 17:28:06 +0200 Subject: [PATCH 35/98] Supply scanner to model checker. --- libsolidity/formal/BMC.cpp | 15 ++++++++++++--- libsolidity/formal/BMC.h | 3 ++- libsolidity/formal/CHC.cpp | 9 +++++---- libsolidity/formal/CHC.h | 3 ++- libsolidity/formal/ModelChecker.cpp | 5 +++-- libsolidity/formal/ModelChecker.h | 1 + libsolidity/formal/Predicate.cpp | 17 +++++++++++++---- libsolidity/formal/Predicate.h | 10 +++++++++- libsolidity/formal/SMTEncoder.cpp | 8 ++++++-- libsolidity/formal/SMTEncoder.h | 8 +++++++- libsolidity/interface/CompilerStack.cpp | 2 +- 11 files changed, 61 insertions(+), 20 deletions(-) diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index c40424173..e86906ff9 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -22,6 +22,9 @@ #include +#include +#include + #ifdef HAVE_Z3_DLOPEN #include #endif @@ -38,9 +41,10 @@ BMC::BMC( map const& _smtlib2Responses, ReadCallback::Callback const& _smtCallback, smtutil::SMTSolverChoice _enabledSolvers, - ModelCheckerSettings const& _settings + ModelCheckerSettings const& _settings, + CharStreamProvider const& _charStreamProvider ): - SMTEncoder(_context, _settings), + SMTEncoder(_context, _settings, _charStreamProvider), m_interface(make_unique(_smtlib2Responses, _smtCallback, _enabledSolvers, _settings.timeout)), m_outerErrorReporter(_errorReporter) { @@ -650,7 +654,12 @@ pair, vector> BMC::modelExpressions() if (uf->annotation().type->isValueType()) { expressionsToEvaluate.emplace_back(expr(*uf)); - // TODO expressionNames.push_back(uf->location().text()); + string expressionName; + if (uf->location().hasText()) + expressionName = m_charStreamProvider.charStream(*uf->location().sourceName).text( + uf->location() + ); + expressionNames.push_back(move(expressionName)); } return {expressionsToEvaluate, expressionNames}; diff --git a/libsolidity/formal/BMC.h b/libsolidity/formal/BMC.h index 7f718b47c..fae2ecf45 100644 --- a/libsolidity/formal/BMC.h +++ b/libsolidity/formal/BMC.h @@ -63,7 +63,8 @@ public: std::map const& _smtlib2Responses, ReadCallback::Callback const& _smtCallback, smtutil::SMTSolverChoice _enabledSolvers, - ModelCheckerSettings const& _settings + ModelCheckerSettings const& _settings, + langutil::CharStreamProvider const& _charStreamProvider ); void analyze(SourceUnit const& _sources, std::map> _solvedTargets); diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index 23967ecd2..10e066f60 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -57,9 +57,10 @@ CHC::CHC( [[maybe_unused]] map const& _smtlib2Responses, [[maybe_unused]] ReadCallback::Callback const& _smtCallback, SMTSolverChoice _enabledSolvers, - ModelCheckerSettings const& _settings + ModelCheckerSettings const& _settings, + CharStreamProvider const& _charStreamProvider ): - SMTEncoder(_context, _settings), + SMTEncoder(_context, _settings, _charStreamProvider), m_outerErrorReporter(_errorReporter), m_enabledSolvers(_enabledSolvers) { @@ -1741,7 +1742,7 @@ optional CHC::generateCounterexample(CHCSolverInterface::CexGraph const& path.emplace_back("State: " + modelMsg); } - string txCex = summaryPredicate->formatSummaryCall(summaryArgs); + string txCex = summaryPredicate->formatSummaryCall(summaryArgs, m_charStreamProvider); list calls; auto dfs = [&](unsigned parent, unsigned node, unsigned depth, auto&& _dfs) -> void { @@ -1753,7 +1754,7 @@ optional CHC::generateCounterexample(CHCSolverInterface::CexGraph const& if (!pred->isConstructorSummary()) for (unsigned v: callGraph[node]) _dfs(node, v, depth + 1, _dfs); - calls.push_front(string(depth * 4, ' ') + pred->formatSummaryCall(nodeArgs(node))); + calls.push_front(string(depth * 4, ' ') + pred->formatSummaryCall(nodeArgs(node), m_charStreamProvider)); if (pred->isInternalCall()) calls.front() += " -- internal call"; else if (pred->isExternalCallTrusted()) diff --git a/libsolidity/formal/CHC.h b/libsolidity/formal/CHC.h index 7b6a50f14..6886c2c7f 100644 --- a/libsolidity/formal/CHC.h +++ b/libsolidity/formal/CHC.h @@ -57,7 +57,8 @@ public: std::map const& _smtlib2Responses, ReadCallback::Callback const& _smtCallback, smtutil::SMTSolverChoice _enabledSolvers, - ModelCheckerSettings const& _settings + ModelCheckerSettings const& _settings, + langutil::CharStreamProvider const& _charStreamProvider ); void analyze(SourceUnit const& _sources); diff --git a/libsolidity/formal/ModelChecker.cpp b/libsolidity/formal/ModelChecker.cpp index c820729f9..e17c7f4df 100644 --- a/libsolidity/formal/ModelChecker.cpp +++ b/libsolidity/formal/ModelChecker.cpp @@ -32,6 +32,7 @@ using namespace solidity::frontend; ModelChecker::ModelChecker( ErrorReporter& _errorReporter, + langutil::CharStreamProvider const& _charStreamProvider, map const& _smtlib2Responses, ModelCheckerSettings _settings, ReadCallback::Callback const& _smtCallback, @@ -40,8 +41,8 @@ ModelChecker::ModelChecker( m_errorReporter(_errorReporter), m_settings(_settings), m_context(), - m_bmc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, _enabledSolvers, m_settings), - m_chc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, _enabledSolvers, m_settings) + m_bmc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, _enabledSolvers, m_settings, _charStreamProvider), + m_chc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, _enabledSolvers, m_settings, _charStreamProvider) { } diff --git a/libsolidity/formal/ModelChecker.h b/libsolidity/formal/ModelChecker.h index 3bcc9ecde..0bf2b4ffc 100644 --- a/libsolidity/formal/ModelChecker.h +++ b/libsolidity/formal/ModelChecker.h @@ -49,6 +49,7 @@ public: /// should be used, even if all are available. The default choice is to use all. ModelChecker( langutil::ErrorReporter& _errorReporter, + langutil::CharStreamProvider const& _charStreamProvider, std::map const& _smtlib2Responses, ModelCheckerSettings _settings = ModelCheckerSettings{}, ReadCallback::Callback const& _smtCallback = ReadCallback::Callback(), diff --git a/libsolidity/formal/Predicate.cpp b/libsolidity/formal/Predicate.cpp index 8e9aace61..f1dc1e080 100644 --- a/libsolidity/formal/Predicate.cpp +++ b/libsolidity/formal/Predicate.cpp @@ -20,6 +20,8 @@ #include +#include +#include #include #include @@ -196,13 +198,20 @@ bool Predicate::isInterface() const return m_type == PredicateType::Interface; } -string Predicate::formatSummaryCall(vector const& _args) const +string Predicate::formatSummaryCall( + vector const& _args, + langutil::CharStreamProvider const& _charStreamProvider +) const { solAssert(isSummary(), ""); - //if (auto funCall = programFunctionCall()) -// return funCall->location().text(); - // TODO + if (auto funCall = programFunctionCall()) + { + if (funCall->location().hasText()) + return string(_charStreamProvider.charStream(*funCall->location().sourceName).text(funCall->location())); + else + return {}; + } /// The signature of a function summary predicate is: summary(error, this, abiFunctions, cryptoFunctions, txData, preBlockChainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars). /// Here we are interested in preInputVars to format the function call, diff --git a/libsolidity/formal/Predicate.h b/libsolidity/formal/Predicate.h index ee9a13716..6f5a05a50 100644 --- a/libsolidity/formal/Predicate.h +++ b/libsolidity/formal/Predicate.h @@ -27,6 +27,11 @@ #include #include +namespace solidity::langutil +{ +class CharStreamProvider; +} + namespace solidity::frontend { @@ -142,7 +147,10 @@ public: /// @returns a formatted string representing a call to this predicate /// with _args. - std::string formatSummaryCall(std::vector const& _args) const; + std::string formatSummaryCall( + std::vector const& _args, + langutil::CharStreamProvider const& _charStreamProvider + ) const; /// @returns the values of the state variables from _args at the point /// where this summary was reached. diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index eb7e2a17e..4069134c9 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -30,6 +30,8 @@ #include #include +#include + #include #include @@ -45,11 +47,13 @@ using namespace solidity::frontend; SMTEncoder::SMTEncoder( smt::EncodingContext& _context, - ModelCheckerSettings const& _settings + ModelCheckerSettings const& _settings, + langutil::CharStreamProvider const& _charStreamProvider ): m_errorReporter(m_smtErrors), m_context(_context), - m_settings(_settings) + m_settings(_settings), + m_charStreamProvider(_charStreamProvider) { } diff --git a/libsolidity/formal/SMTEncoder.h b/libsolidity/formal/SMTEncoder.h index 9b8402cc1..9adf38ffe 100644 --- a/libsolidity/formal/SMTEncoder.h +++ b/libsolidity/formal/SMTEncoder.h @@ -43,6 +43,7 @@ namespace solidity::langutil { class ErrorReporter; struct SourceLocation; +class CharStreamProvider; } namespace solidity::frontend @@ -53,7 +54,8 @@ class SMTEncoder: public ASTConstVisitor public: SMTEncoder( smt::EncodingContext& _context, - ModelCheckerSettings const& _settings + ModelCheckerSettings const& _settings, + langutil::CharStreamProvider const& _charStreamProvider ); /// @returns true if engine should proceed with analysis. @@ -469,6 +471,10 @@ protected: ModelCheckerSettings const& m_settings; + /// Character stream for each source, + /// used for retrieving source text of expressions for e.g. counter-examples. + langutil::CharStreamProvider const& m_charStreamProvider; + smt::SymbolicState& state(); }; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 45353720b..93ddd4122 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -555,7 +555,7 @@ bool CompilerStack::analyze() if (noErrors) { - ModelChecker modelChecker(m_errorReporter, m_smtlib2Responses, m_modelCheckerSettings, m_readFile, m_enabledSMTSolvers); + ModelChecker modelChecker(m_errorReporter, *this, m_smtlib2Responses, m_modelCheckerSettings, m_readFile, m_enabledSMTSolvers); auto allSources = applyMap(m_sourceOrder, [](Source const* _source) { return _source->ast; }); modelChecker.enableAllEnginesIfPragmaPresent(allSources); modelChecker.checkRequestedSourcesAndContracts(allSources); From 312ac4a2555858c09b4bdcb5c7295a792af91fea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 14 Jul 2021 21:28:20 +0200 Subject: [PATCH 36/98] docs: Remove deprecated escape characters from the list of available escapes --- docs/types/value-types.rst | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index 1e2c3abc2..caa00a83e 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -514,17 +514,21 @@ Additionally, string literals also support the following escape characters: - ``\\`` (backslash) - ``\'`` (single quote) - ``\"`` (double quote) - - ``\b`` (backspace) - - ``\f`` (form feed) - ``\n`` (newline) - ``\r`` (carriage return) - ``\t`` (tab) - - ``\v`` (vertical tab) - ``\xNN`` (hex escape, see below) - ``\uNNNN`` (unicode escape, see below) ``\xNN`` takes a hex value and inserts the appropriate byte, while ``\uNNNN`` takes a Unicode codepoint and inserts an UTF-8 sequence. +.. note:: + + Until version 0.8.0 there were three additional escape sequences: ``\b``, ``\f`` and ``\v``. + They are commonly available in other languages but rarely needed in practice. + If you do need them, they can still be inserted via hexadecimal escapes, i.e. ``\x08``, ``\x0c`` + and ``\x0b``, respectively, just as any other ASCII character. + The string in the following example has a length of ten bytes. It starts with a newline byte, followed by a double quote, a single quote a backslash character and then (without separator) the From 6753c8f624c01b09ff07309a84113538a3ee1431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 14 Jul 2021 21:29:01 +0200 Subject: [PATCH 37/98] Rename escapeAndQuoteYulString() back to escapeAndQuoteString() --- libevmasm/Assembly.cpp | 2 +- libsolidity/codegen/ir/IRGeneratorForStatements.cpp | 2 +- libsolutil/CommonData.cpp | 6 ++---- libsolutil/CommonData.h | 4 ++-- libyul/AsmPrinter.cpp | 2 +- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 13bb63dfe..dad9635d7 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -153,7 +153,7 @@ public: return; m_out << m_prefix << " /*"; if (m_location.sourceName) - m_out << " " + escapeAndQuoteYulString(*m_location.sourceName); + m_out << " " + escapeAndQuoteString(*m_location.sourceName); if (m_location.hasText()) m_out << ":" << to_string(m_location.start) + ":" + to_string(m_location.end); m_out << " " << locationFromSources(m_sourceCodes, m_location); diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index aea70536a..86fcddc92 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -3166,5 +3166,5 @@ bool IRGeneratorForStatements::visit(TryCatchClause const& _clause) string IRGeneratorForStatements::linkerSymbol(ContractDefinition const& _library) const { solAssert(_library.isLibrary(), ""); - return "linkersymbol(" + util::escapeAndQuoteYulString(_library.fullyQualifiedName()) + ")"; + return "linkersymbol(" + util::escapeAndQuoteString(_library.fullyQualifiedName()) + ")"; } diff --git a/libsolutil/CommonData.cpp b/libsolutil/CommonData.cpp index c6688fee8..a49f90491 100644 --- a/libsolutil/CommonData.cpp +++ b/libsolutil/CommonData.cpp @@ -192,13 +192,11 @@ string solidity::util::formatAsStringOrNumber(string const& _value) if (c <= 0x1f || c >= 0x7f || c == '"') return "0x" + h256(_value, h256::AlignLeft).hex(); - // The difference in escaping is only in characters below 0x1f and the string does not have them - // so this will work for Solidity strings too. - return escapeAndQuoteYulString(_value); + return escapeAndQuoteString(_value); } -string solidity::util::escapeAndQuoteYulString(string const& _input) +string solidity::util::escapeAndQuoteString(string const& _input) { string out; diff --git a/libsolutil/CommonData.h b/libsolutil/CommonData.h index 3c8b2d80c..19d66fc91 100644 --- a/libsolutil/CommonData.h +++ b/libsolutil/CommonData.h @@ -552,9 +552,9 @@ bool isValidDecimal(std::string const& _string); /// _value cannot be longer than 32 bytes. std::string formatAsStringOrNumber(std::string const& _value); -/// @returns a string with the usual backslash-escapes for non-ASCII +/// @returns a string with the usual backslash-escapes for non-printable and non-ASCII /// characters and surrounded by '"'-characters. -std::string escapeAndQuoteYulString(std::string const& _input); +std::string escapeAndQuoteString(std::string const& _input); template bool containerEqual(Container const& _lhs, Container const& _rhs, Compare&& _compare) diff --git a/libyul/AsmPrinter.cpp b/libyul/AsmPrinter.cpp index e91208feb..8c5f26178 100644 --- a/libyul/AsmPrinter.cpp +++ b/libyul/AsmPrinter.cpp @@ -57,7 +57,7 @@ string AsmPrinter::operator()(Literal const& _literal) const break; } - return escapeAndQuoteYulString(_literal.value.str()) + appendTypeName(_literal.type); + return escapeAndQuoteString(_literal.value.str()) + appendTypeName(_literal.type); } string AsmPrinter::operator()(Identifier const& _identifier) const From ce79e2515b783163abc3e6d960036d9aa1de9363 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 14 Jul 2021 17:55:05 +0200 Subject: [PATCH 38/98] docs: Fix badly indented lists --- docs/060-breaking-changes.rst | 16 +- docs/abi-spec.rst | 100 +++++----- docs/bugs.rst | 20 +- docs/contracts/libraries.rst | 16 +- docs/internals/optimizer.rst | 148 +++++++-------- docs/internals/source_mappings.rst | 4 +- docs/ir/ir-breaking-changes.rst | 280 ++++++++++++++-------------- docs/natspec-format.rst | 6 +- docs/types/value-types.rst | 36 ++-- docs/units-and-global-variables.rst | 10 +- docs/using-the-compiler.rst | 4 +- docs/yul.rst | 45 ++--- libsolidity/codegen/ir/README.md | 4 +- 13 files changed, 350 insertions(+), 339 deletions(-) diff --git a/docs/060-breaking-changes.rst b/docs/060-breaking-changes.rst index 32397677c..915a9f335 100644 --- a/docs/060-breaking-changes.rst +++ b/docs/060-breaking-changes.rst @@ -89,14 +89,14 @@ New Features This section lists things that were not possible prior to Solidity 0.6.0 or were more difficult to achieve. - * The :ref:`try/catch statement ` allows you to react on failed external calls. - * ``struct`` and ``enum`` types can be declared at file level. - * Array slices can be used for calldata arrays, for example ``abi.decode(msg.data[4:], (uint, uint))`` - is a low-level way to decode the function call payload. - * Natspec supports multiple return parameters in developer documentation, enforcing the same naming check as ``@param``. - * Yul and Inline Assembly have a new statement called ``leave`` that exits the current function. - * Conversions from ``address`` to ``address payable`` are now possible via ``payable(x)``, where - ``x`` must be of type ``address``. +* The :ref:`try/catch statement ` allows you to react on failed external calls. +* ``struct`` and ``enum`` types can be declared at file level. +* Array slices can be used for calldata arrays, for example ``abi.decode(msg.data[4:], (uint, uint))`` + is a low-level way to decode the function call payload. +* Natspec supports multiple return parameters in developer documentation, enforcing the same naming check as ``@param``. +* Yul and Inline Assembly have a new statement called ``leave`` that exits the current function. +* Conversions from ``address`` to ``address payable`` are now possible via ``payable(x)``, where + ``x`` must be of type ``address``. Interface Changes diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index d412a2a2e..0c38e0f27 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -77,8 +77,9 @@ The following (fixed-size) array type exists: - ``[M]``: a fixed-length array of ``M`` elements, ``M >= 0``, of the given type. - .. note:: - While this ABI specification can express fixed-length arrays with zero elements, they're not supported by the compiler. + .. note:: + + While this ABI specification can express fixed-length arrays with zero elements, they're not supported by the compiler. The following non-fixed-size types exist: @@ -124,13 +125,13 @@ Design Criteria for the Encoding The encoding is designed to have the following properties, which are especially useful if some arguments are nested arrays: - 1. The number of reads necessary to access a value is at most the depth of the value - inside the argument array structure, i.e. four reads are needed to retrieve ``a_i[k][l][r]``. In a - previous version of the ABI, the number of reads scaled linearly with the total number of dynamic - parameters in the worst case. +1. The number of reads necessary to access a value is at most the depth of the value + inside the argument array structure, i.e. four reads are needed to retrieve ``a_i[k][l][r]``. In a + previous version of the ABI, the number of reads scaled linearly with the total number of dynamic + parameters in the worst case. - 2. The data of a variable or array element is not interleaved with other data and it is - relocatable, i.e. it only uses relative "addresses". +2. The data of a variable or array element is not interleaved with other data and it is + relocatable, i.e. it only uses relative "addresses". Formal Specification of the Encoding @@ -312,21 +313,21 @@ these are directly the values we want to pass, whereas for the dynamic types ``u we use the offset in bytes to the start of their data area, measured from the start of the value encoding (i.e. not counting the first four bytes containing the hash of the function signature). These are: - - ``0x0000000000000000000000000000000000000000000000000000000000000123`` (``0x123`` padded to 32 bytes) - - ``0x0000000000000000000000000000000000000000000000000000000000000080`` (offset to start of data part of second parameter, 4*32 bytes, exactly the size of the head part) - - ``0x3132333435363738393000000000000000000000000000000000000000000000`` (``"1234567890"`` padded to 32 bytes on the right) - - ``0x00000000000000000000000000000000000000000000000000000000000000e0`` (offset to start of data part of fourth parameter = offset to start of data part of first dynamic parameter + size of data part of first dynamic parameter = 4\*32 + 3\*32 (see below)) +- ``0x0000000000000000000000000000000000000000000000000000000000000123`` (``0x123`` padded to 32 bytes) +- ``0x0000000000000000000000000000000000000000000000000000000000000080`` (offset to start of data part of second parameter, 4*32 bytes, exactly the size of the head part) +- ``0x3132333435363738393000000000000000000000000000000000000000000000`` (``"1234567890"`` padded to 32 bytes on the right) +- ``0x00000000000000000000000000000000000000000000000000000000000000e0`` (offset to start of data part of fourth parameter = offset to start of data part of first dynamic parameter + size of data part of first dynamic parameter = 4\*32 + 3\*32 (see below)) After this, the data part of the first dynamic argument, ``[0x456, 0x789]`` follows: - - ``0x0000000000000000000000000000000000000000000000000000000000000002`` (number of elements of the array, 2) - - ``0x0000000000000000000000000000000000000000000000000000000000000456`` (first element) - - ``0x0000000000000000000000000000000000000000000000000000000000000789`` (second element) +- ``0x0000000000000000000000000000000000000000000000000000000000000002`` (number of elements of the array, 2) +- ``0x0000000000000000000000000000000000000000000000000000000000000456`` (first element) +- ``0x0000000000000000000000000000000000000000000000000000000000000789`` (second element) Finally, we encode the data part of the second dynamic argument, ``"Hello, world!"``: - - ``0x000000000000000000000000000000000000000000000000000000000000000d`` (number of elements (bytes in this case): 13) - - ``0x48656c6c6f2c20776f726c642100000000000000000000000000000000000000`` (``"Hello, world!"`` padded to 32 bytes on the right) +- ``0x000000000000000000000000000000000000000000000000000000000000000d`` (number of elements (bytes in this case): 13) +- ``0x48656c6c6f2c20776f726c642100000000000000000000000000000000000000`` (``"Hello, world!"`` padded to 32 bytes on the right) All together, the encoding is (newline after function selector and each 32-bytes for clarity): @@ -348,14 +349,14 @@ with values ``([[1, 2], [3]], ["one", "two", "three"])`` but start from the most First we encode the length and data of the first embedded dynamic array ``[1, 2]`` of the first root array ``[[1, 2], [3]]``: - - ``0x0000000000000000000000000000000000000000000000000000000000000002`` (number of elements in the first array, 2; the elements themselves are ``1`` and ``2``) - - ``0x0000000000000000000000000000000000000000000000000000000000000001`` (first element) - - ``0x0000000000000000000000000000000000000000000000000000000000000002`` (second element) +- ``0x0000000000000000000000000000000000000000000000000000000000000002`` (number of elements in the first array, 2; the elements themselves are ``1`` and ``2``) +- ``0x0000000000000000000000000000000000000000000000000000000000000001`` (first element) +- ``0x0000000000000000000000000000000000000000000000000000000000000002`` (second element) Then we encode the length and data of the second embedded dynamic array ``[3]`` of the first root array ``[[1, 2], [3]]``: - - ``0x0000000000000000000000000000000000000000000000000000000000000001`` (number of elements in the second array, 1; the element is ``3``) - - ``0x0000000000000000000000000000000000000000000000000000000000000003`` (first element) +- ``0x0000000000000000000000000000000000000000000000000000000000000001`` (number of elements in the second array, 1; the element is ``3``) +- ``0x0000000000000000000000000000000000000000000000000000000000000003`` (first element) Then we need to find the offsets ``a`` and ``b`` for their respective dynamic arrays ``[1, 2]`` and ``[3]``. To calculate the offsets we can take a look at the encoded data of the first root array ``[[1, 2], [3]]`` @@ -380,12 +381,12 @@ thus ``b = 0x00000000000000000000000000000000000000000000000000000000000000a0``. Then we encode the embedded strings of the second root array: - - ``0x0000000000000000000000000000000000000000000000000000000000000003`` (number of characters in word ``"one"``) - - ``0x6f6e650000000000000000000000000000000000000000000000000000000000`` (utf8 representation of word ``"one"``) - - ``0x0000000000000000000000000000000000000000000000000000000000000003`` (number of characters in word ``"two"``) - - ``0x74776f0000000000000000000000000000000000000000000000000000000000`` (utf8 representation of word ``"two"``) - - ``0x0000000000000000000000000000000000000000000000000000000000000005`` (number of characters in word ``"three"``) - - ``0x7468726565000000000000000000000000000000000000000000000000000000`` (utf8 representation of word ``"three"``) +- ``0x0000000000000000000000000000000000000000000000000000000000000003`` (number of characters in word ``"one"``) +- ``0x6f6e650000000000000000000000000000000000000000000000000000000000`` (utf8 representation of word ``"one"``) +- ``0x0000000000000000000000000000000000000000000000000000000000000003`` (number of characters in word ``"two"``) +- ``0x74776f0000000000000000000000000000000000000000000000000000000000`` (utf8 representation of word ``"two"``) +- ``0x0000000000000000000000000000000000000000000000000000000000000005`` (number of characters in word ``"three"``) +- ``0x7468726565000000000000000000000000000000000000000000000000000000`` (utf8 representation of word ``"three"``) In parallel to the first root array, since strings are dynamic elements we need to find their offsets ``c``, ``d`` and ``e``: @@ -416,11 +417,11 @@ and have the same encodings for a function with a signature ``g(string[],uint[][ Then we encode the length of the first root array: - - ``0x0000000000000000000000000000000000000000000000000000000000000002`` (number of elements in the first root array, 2; the elements themselves are ``[1, 2]`` and ``[3]``) +- ``0x0000000000000000000000000000000000000000000000000000000000000002`` (number of elements in the first root array, 2; the elements themselves are ``[1, 2]`` and ``[3]``) Then we encode the length of the second root array: - - ``0x0000000000000000000000000000000000000000000000000000000000000003`` (number of strings in the second root array, 3; the strings themselves are ``"one"``, ``"two"`` and ``"three"``) +- ``0x0000000000000000000000000000000000000000000000000000000000000003`` (number of strings in the second root array, 3; the strings themselves are ``"one"``, ``"two"`` and ``"three"``) Finally we find the offsets ``f`` and ``g`` for their respective root dynamic arrays ``[[1, 2], [3]]`` and ``["one", "two", "three"]``, and assemble parts in the correct order: @@ -761,18 +762,19 @@ As an example, the encoding of ``int16(-1), bytes1(0x42), uint16(0x03), string(" ^^^^^^^^^^^^^^^^^^^^^^^^^^ string("Hello, world!") without a length field More specifically: - - During the encoding, everything is encoded in-place. This means that there is - no distinction between head and tail, as in the ABI encoding, and the length - of an array is not encoded. - - The direct arguments of ``abi.encodePacked`` are encoded without padding, - as long as they are not arrays (or ``string`` or ``bytes``). - - The encoding of an array is the concatenation of the - encoding of its elements **with** padding. - - Dynamically-sized types like ``string``, ``bytes`` or ``uint[]`` are encoded - without their length field. - - The encoding of ``string`` or ``bytes`` does not apply padding at the end - unless it is part of an array or struct (then it is padded to a multiple of - 32 bytes). + +- During the encoding, everything is encoded in-place. This means that there is + no distinction between head and tail, as in the ABI encoding, and the length + of an array is not encoded. +- The direct arguments of ``abi.encodePacked`` are encoded without padding, + as long as they are not arrays (or ``string`` or ``bytes``). +- The encoding of an array is the concatenation of the + encoding of its elements **with** padding. +- Dynamically-sized types like ``string``, ``bytes`` or ``uint[]`` are encoded + without their length field. +- The encoding of ``string`` or ``bytes`` does not apply padding at the end + unless it is part of an array or struct (then it is padded to a multiple of + 32 bytes). In general, the encoding is ambiguous as soon as there are two dynamically-sized elements, because of the missing length field. @@ -801,13 +803,13 @@ Indexed event parameters that are not value types, i.e. arrays and structs are n stored directly but instead a keccak256-hash of an encoding is stored. This encoding is defined as follows: - - the encoding of a ``bytes`` and ``string`` value is just the string contents - without any padding or length prefix. - - the encoding of a struct is the concatenation of the encoding of its members, - always padded to a multiple of 32 bytes (even ``bytes`` and ``string``). - - the encoding of an array (both dynamically- and statically-sized) is - the concatenation of the encoding of its elements, always padded to a multiple - of 32 bytes (even ``bytes`` and ``string``) and without any length prefix +- the encoding of a ``bytes`` and ``string`` value is just the string contents + without any padding or length prefix. +- the encoding of a struct is the concatenation of the encoding of its members, + always padded to a multiple of 32 bytes (even ``bytes`` and ``string``). +- the encoding of an array (both dynamically- and statically-sized) is + the concatenation of the encoding of its elements, always padded to a multiple + of 32 bytes (even ``bytes`` and ``string``) and without any length prefix In the above, as usual, a negative number is padded by sign extension and not zero padded. ``bytesNN`` types are padded on the right while ``uintNN`` / ``intNN`` are padded on the left. diff --git a/docs/bugs.rst b/docs/bugs.rst index 73700adf3..75a23e499 100644 --- a/docs/bugs.rst +++ b/docs/bugs.rst @@ -19,16 +19,16 @@ which can be used to check which bugs affect a specific version of the compiler. Contract source verification tools and also other tools interacting with contracts should consult this list according to the following criteria: - - It is mildly suspicious if a contract was compiled with a nightly - compiler version instead of a released version. This list does not keep - track of unreleased or nightly versions. - - It is also mildly suspicious if a contract was compiled with a version that was - not the most recent at the time the contract was created. For contracts - created from other contracts, you have to follow the creation chain - back to a transaction and use the date of that transaction as creation date. - - It is highly suspicious if a contract was compiled with a compiler that - contains a known bug and the contract was created at a time where a newer - compiler version containing a fix was already released. +- It is mildly suspicious if a contract was compiled with a nightly + compiler version instead of a released version. This list does not keep + track of unreleased or nightly versions. +- It is also mildly suspicious if a contract was compiled with a version that was + not the most recent at the time the contract was created. For contracts + created from other contracts, you have to follow the creation chain + back to a transaction and use the date of that transaction as creation date. +- It is highly suspicious if a contract was compiled with a compiler that + contains a known bug and the contract was created at a time where a newer + compiler version containing a fix was already released. The JSON file of known bugs below is an array of objects, one for each bug, with the following keys: diff --git a/docs/contracts/libraries.rst b/docs/contracts/libraries.rst index eee9de765..a6a062501 100644 --- a/docs/contracts/libraries.rst +++ b/docs/contracts/libraries.rst @@ -223,14 +223,14 @@ following an internal naming schema and arguments of types not supported in the The following identifiers are used for the types in the signatures: - - Value types, non-storage ``string`` and non-storage ``bytes`` use the same identifiers as in the contract ABI. - - Non-storage array types follow the same convention as in the contract ABI, i.e. ``[]`` for dynamic arrays and - ``[M]`` for fixed-size arrays of ``M`` elements. - - Non-storage structs are referred to by their fully qualified name, i.e. ``C.S`` for ``contract C { struct S { ... } }``. - - Storage pointer mappings use ``mapping( => ) storage`` where ```` and ```` are - the identifiers for the key and value types of the mapping, respectively. - - Other storage pointer types use the type identifier of their corresponding non-storage type, but append a single space - followed by ``storage`` to it. +- Value types, non-storage ``string`` and non-storage ``bytes`` use the same identifiers as in the contract ABI. +- Non-storage array types follow the same convention as in the contract ABI, i.e. ``[]`` for dynamic arrays and + ``[M]`` for fixed-size arrays of ``M`` elements. +- Non-storage structs are referred to by their fully qualified name, i.e. ``C.S`` for ``contract C { struct S { ... } }``. +- Storage pointer mappings use ``mapping( => ) storage`` where ```` and ```` are + the identifiers for the key and value types of the mapping, respectively. +- Other storage pointer types use the type identifier of their corresponding non-storage type, but append a single space + followed by ``storage`` to it. The argument encoding is the same as for the regular contract ABI, except for storage pointers, which are encoded as a ``uint256`` value referring to the storage slot to which they point. diff --git a/docs/internals/optimizer.rst b/docs/internals/optimizer.rst index 142a89602..64c8b1460 100644 --- a/docs/internals/optimizer.rst +++ b/docs/internals/optimizer.rst @@ -269,11 +269,11 @@ backtracking. All components of the Yul-based optimizer module are explained below. The following transformation steps are the main components: - - SSA Transform - - Common Subexpression Eliminator - - Expression Simplifier - - Redundant Assign Eliminator - - Full Function Inliner +- SSA Transform +- Common Subexpression Eliminator +- Expression Simplifier +- Redundant Assign Eliminator +- Full Function Inliner Optimizer Steps --------------- @@ -281,36 +281,36 @@ Optimizer Steps This is a list of all steps the Yul-based optimizer sorted alphabetically. You can find more information on the individual steps and their sequence below. - - :ref:`block-flattener`. - - :ref:`circular-reference-pruner`. - - :ref:`common-subexpression-eliminator`. - - :ref:`conditional-simplifier`. - - :ref:`conditional-unsimplifier`. - - :ref:`control-flow-simplifier`. - - :ref:`dead-code-eliminator`. - - :ref:`equivalent-function-combiner`. - - :ref:`expression-joiner`. - - :ref:`expression-simplifier`. - - :ref:`expression-splitter`. - - :ref:`for-loop-condition-into-body`. - - :ref:`for-loop-condition-out-of-body`. - - :ref:`for-loop-init-rewriter`. - - :ref:`functional-inliner`. - - :ref:`function-grouper`. - - :ref:`function-hoister`. - - :ref:`function-specializer`. - - :ref:`literal-rematerialiser`. - - :ref:`load-resolver`. - - :ref:`loop-invariant-code-motion`. - - :ref:`redundant-assign-eliminator`. - - :ref:`reasoning-based-simplifier`. - - :ref:`rematerialiser`. - - :ref:`SSA-reverser`. - - :ref:`SSA-transform`. - - :ref:`structural-simplifier`. - - :ref:`unused-function-parameter-pruner`. - - :ref:`unused-pruner`. - - :ref:`var-decl-initializer`. +- :ref:`block-flattener`. +- :ref:`circular-reference-pruner`. +- :ref:`common-subexpression-eliminator`. +- :ref:`conditional-simplifier`. +- :ref:`conditional-unsimplifier`. +- :ref:`control-flow-simplifier`. +- :ref:`dead-code-eliminator`. +- :ref:`equivalent-function-combiner`. +- :ref:`expression-joiner`. +- :ref:`expression-simplifier`. +- :ref:`expression-splitter`. +- :ref:`for-loop-condition-into-body`. +- :ref:`for-loop-condition-out-of-body`. +- :ref:`for-loop-init-rewriter`. +- :ref:`functional-inliner`. +- :ref:`function-grouper`. +- :ref:`function-hoister`. +- :ref:`function-specializer`. +- :ref:`literal-rematerialiser`. +- :ref:`load-resolver`. +- :ref:`loop-invariant-code-motion`. +- :ref:`redundant-assign-eliminator`. +- :ref:`reasoning-based-simplifier`. +- :ref:`rematerialiser`. +- :ref:`SSA-reverser`. +- :ref:`SSA-transform`. +- :ref:`structural-simplifier`. +- :ref:`unused-function-parameter-pruner`. +- :ref:`unused-pruner`. +- :ref:`var-decl-initializer`. Selecting Optimizations ----------------------- @@ -589,8 +589,8 @@ For any variable ``a`` that is assigned to somewhere in the code (variables that are declared with value and never re-assigned are not modified) perform the following transforms: - - replace ``let a := v`` by ``let a_i := v let a := a_i`` - - replace ``a := v`` by ``let a_i := v a := a_i`` where ``i`` is a number such that ``a_i`` is yet unused. +- replace ``let a := v`` by ``let a_i := v let a := a_i`` +- replace ``a := v`` by ``let a_i := v a := a_i`` where ``i`` is a number such that ``a_i`` is yet unused. Furthermore, always record the current value of ``i`` used for ``a`` and replace each reference to ``a`` by ``a_i``. @@ -677,9 +677,9 @@ joins, the two mappings coming from the two branches are combined in the followi Statements that are only in one mapping or have the same state are used unchanged. Conflicting values are resolved in the following way: - - "unused", "undecided" -> "undecided" - - "unused", "used" -> "used" - - "undecided, "used" -> "used" +- "unused", "undecided" -> "undecided" +- "unused", "used" -> "used" +- "undecided, "used" -> "used" For for-loops, the condition, body and post-part are visited twice, taking the joining control-flow at the condition into account. @@ -735,10 +735,10 @@ is side-effect free and its evaluation only depends on the values of variables and the call-constant state of the environment. Most expressions are movable. The following parts make an expression non-movable: - - function calls (might be relaxed in the future if all statements in the function are movable) - - opcodes that (can) have side-effects (like ``call`` or ``selfdestruct``) - - opcodes that read or write memory, storage or external state information - - opcodes that depend on the current PC, memory size or returndata size +- function calls (might be relaxed in the future if all statements in the function are movable) +- opcodes that (can) have side-effects (like ``call`` or ``selfdestruct``) +- opcodes that read or write memory, storage or external state information +- opcodes that depend on the current PC, memory size or returndata size DataflowAnalyzer ^^^^^^^^^^^^^^^^ @@ -836,8 +836,8 @@ ReasoningBasedSimplifier This optimizer uses SMT solvers to check whether ``if`` conditions are constant. - - If ``constraints AND condition`` is UNSAT, the condition is never true and the whole body can be removed. - - If ``constraints AND NOT condition`` is UNSAT, the condition is always true and can be replaced by ``1``. +- If ``constraints AND condition`` is UNSAT, the condition is never true and the whole body can be removed. +- If ``constraints AND NOT condition`` is UNSAT, the condition is always true and can be replaced by ``1``. The simplifications above can only be applied if the condition is movable. @@ -872,13 +872,13 @@ we cannot assign a specific value. Current features: - - switch cases: insert " := " - - after if statement with terminating control-flow, insert " := 0" +- switch cases: insert " := " +- after if statement with terminating control-flow, insert " := 0" Future features: - - allow replacements by "1" - - take termination of user-defined functions into account +- allow replacements by "1" +- take termination of user-defined functions into account Works best with SSA form and if dead code removal has run before. @@ -898,15 +898,15 @@ ControlFlowSimplifier Simplifies several control-flow structures: - - replace if with empty body with pop(condition) - - remove empty default switch case - - remove empty switch case if no default case exists - - replace switch with no cases with pop(expression) - - turn switch with single case into if - - replace switch with only default case with pop(expression) and body - - replace switch with const expr with matching case body - - replace ``for`` with terminating control flow and without other break/continue by ``if`` - - remove ``leave`` at the end of a function. +- replace if with empty body with pop(condition) +- remove empty default switch case +- remove empty switch case if no default case exists +- replace switch with no cases with pop(expression) +- turn switch with single case into if +- replace switch with only default case with pop(expression) and body +- replace switch with const expr with matching case body +- replace ``for`` with terminating control flow and without other break/continue by ``if`` +- remove ``leave`` at the end of a function. None of these operations depend on the data flow. The StructuralSimplifier performs similar tasks that do depend on data flow. @@ -956,13 +956,13 @@ StructuralSimplifier This is a general step that performs various kinds of simplifications on a structural level: - - replace if statement with empty body by ``pop(condition)`` - - replace if statement with true condition by its body - - remove if statement with false condition - - turn switch with single case into if - - replace switch with only default case by ``pop(expression)`` and body - - replace switch with literal expression by matching case body - - replace for loop with false condition by its initialization part +- replace if statement with empty body by ``pop(condition)`` +- replace if statement with true condition by its body +- remove if statement with false condition +- turn switch with single case into if +- replace switch with only default case by ``pop(expression)`` and body +- replace switch with literal expression by matching case body +- replace for loop with false condition by its initialization part This component uses the Dataflow Analyzer. @@ -1008,8 +1008,8 @@ declarations inside conditional branches will not be moved out of the loop. Requirements: - - The Disambiguator, ForLoopInitRewriter and FunctionHoister must be run upfront. - - Expression splitter and SSA transform should be run upfront to obtain better result. +- The Disambiguator, ForLoopInitRewriter and FunctionHoister must be run upfront. +- Expression splitter and SSA transform should be run upfront to obtain better result. Function-Level Optimizations @@ -1089,15 +1089,15 @@ FunctionalInliner This component of the optimizer performs restricted function inlining by inlining functions that can be inlined inside functional expressions, i.e. functions that: - - return a single value. - - have a body like ``r := ``. - - neither reference themselves nor ``r`` in the right hand side. +- return a single value. +- have a body like ``r := ``. +- neither reference themselves nor ``r`` in the right hand side. Furthermore, for all parameters, all of the following need to be true: - - The argument is movable. - - The parameter is either referenced less than twice in the function body, or the argument is rather cheap - ("cost" of at most 1, like a constant up to 0xff). +- The argument is movable. +- The parameter is either referenced less than twice in the function body, or the argument is rather cheap + ("cost" of at most 1, like a constant up to 0xff). Example: The function to be inlined has the form of ``function f(...) -> r { r := E }`` where ``E`` is an expression that does not reference ``r`` and all arguments in the function call are movable expressions. diff --git a/docs/internals/source_mappings.rst b/docs/internals/source_mappings.rst index 0ee215533..fbb84e2d1 100644 --- a/docs/internals/source_mappings.rst +++ b/docs/internals/source_mappings.rst @@ -56,8 +56,8 @@ used in a single modifier. In order to compress these source mappings especially for bytecode, the following rules are used: - - If a field is empty, the value of the preceding element is used. - - If a ``:`` is missing, all following fields are considered empty. +- If a field is empty, the value of the preceding element is used. +- If a ``:`` is missing, all following fields are considered empty. This means the following source mappings represent the same information: diff --git a/docs/ir/ir-breaking-changes.rst b/docs/ir/ir-breaking-changes.rst index 7d6b6d54f..29b548654 100644 --- a/docs/ir/ir-breaking-changes.rst +++ b/docs/ir/ir-breaking-changes.rst @@ -11,180 +11,187 @@ Semantic Only Changes This section lists the changes that are semantic-only, thus potentially hiding new and different behavior in existing code. - * When storage structs are deleted, every storage slot that contains a member of the struct is set to zero entirely. Formally, padding space was left untouched. -Consequently, if the padding space within a struct is used to store data (e.g. in the context of a contract upgrade), you have to be aware that ``delete`` will now also clear the added member (while it wouldn't have been cleared in the past). +- When storage structs are deleted, every storage slot that contains a member of the struct is set to zero entirely. Formally, padding space was left untouched. + Consequently, if the padding space within a struct is used to store data (e.g. in the context of a contract upgrade), you have to be aware that ``delete`` will now also clear the added member (while it wouldn't have been cleared in the past). -.. code-block:: solidity + .. code-block:: solidity - // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.7.0; + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >0.7.0; - contract C { - struct S { - uint64 y; - uint64 z; - } - S s; - function f() public { - // ... - delete s; - // s occupies only first 16 bytes of the 32 bytes slot - // delete will write zero to the full slot - } - } + contract C { + struct S { + uint64 y; + uint64 z; + } + S s; + function f() public { + // ... + delete s; + // s occupies only first 16 bytes of the 32 bytes slot + // delete will write zero to the full slot + } + } -We have the same behavior for implicit delete, for example when array of structs is shortened. + We have the same behavior for implicit delete, for example when array of structs is shortened. - * Function modifiers are implemented in a slightly different way regarding function parameters. - This especially has an effect if the placeholder ``_;`` is evaluated multiple times in a modifier. - In the old code generator, each function parameter has a fixed slot on the stack. If the function - is run multiple times because ``_;`` is used multiple times or used in a loop, then a change to the - function parameter's value is visible in the next execution of the function. - The new code generator implements modifiers using actual functions and passes function parameters on. - This means that multiple executions of a function will get the same values for the parameters. +- Function modifiers are implemented in a slightly different way regarding function parameters. + This especially has an effect if the placeholder ``_;`` is evaluated multiple times in a modifier. + In the old code generator, each function parameter has a fixed slot on the stack. If the function + is run multiple times because ``_;`` is used multiple times or used in a loop, then a change to the + function parameter's value is visible in the next execution of the function. + The new code generator implements modifiers using actual functions and passes function parameters on. + This means that multiple executions of a function will get the same values for the parameters. -.. code-block:: solidity + .. code-block:: solidity - // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.7.0; - contract C { - function f(uint _a) public pure mod() returns (uint _r) { - _r = _a++; - } - modifier mod() { _; _; } - } + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.7.0; + contract C { + function f(uint _a) public pure mod() returns (uint _r) { + _r = _a++; + } + modifier mod() { _; _; } + } -If you execute ``f(0)`` in the old code generator, it will return ``2``, while -it will return ``1`` when using the new code generator. + If you execute ``f(0)`` in the old code generator, it will return ``2``, while + it will return ``1`` when using the new code generator. - * The order of contract initialization has changed in case of inheritance. +- The order of contract initialization has changed in case of inheritance. -The order used to be: - - All state variables are zero-initialized at the beginning. - - Evaluate base constructor arguments from most derived to most base contract. - - Initialize all state variables in the whole inheritance hierarchy from most base to most derived. - - Run the constructor, if present, for all contracts in the linearized hierarchy from most base to most derived. + The order used to be: -New order: - - All state variables are zero-initialized at the beginning. - - Evaluate base constructor arguments from most derived to most base contract. - - For every contract in order from most base to most derived in the linearized hierarchy execute: - 1. If present at declaration, initial values are assigned to state variables. - 2. Constructor, if present. + - All state variables are zero-initialized at the beginning. + - Evaluate base constructor arguments from most derived to most base contract. + - Initialize all state variables in the whole inheritance hierarchy from most base to most derived. + - Run the constructor, if present, for all contracts in the linearized hierarchy from most base to most derived. + + New order: + + - All state variables are zero-initialized at the beginning. + - Evaluate base constructor arguments from most derived to most base contract. + - For every contract in order from most base to most derived in the linearized hierarchy execute: + + 1. If present at declaration, initial values are assigned to state variables. + 2. Constructor, if present. This causes differences in some contracts, for example: -.. code-block:: solidity + .. code-block:: solidity - // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.7.0; + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >0.7.0; - contract A { - uint x; - constructor() { - x = 42; - } - function f() public view returns(uint256) { - return x; - } - } - contract B is A { - uint public y = f(); - } + contract A { + uint x; + constructor() { + x = 42; + } + function f() public view returns(uint256) { + return x; + } + } + contract B is A { + uint public y = f(); + } -Previously, ``y`` would be set to 0. This is due to the fact that we would first initialize state variables: First, ``x`` is set to 0, and when initializing ``y``, ``f()`` would return 0 causing ``y`` to be 0 as well. -With the new rules, ``y`` will be set to 42. We first initialize ``x`` to 0, then call A's constructor which sets ``x`` to 42. Finally, when initializing ``y``, ``f()`` returns 42 causing ``y`` to be 42. + Previously, ``y`` would be set to 0. This is due to the fact that we would first initialize state variables: First, ``x`` is set to 0, and when initializing ``y``, ``f()`` would return 0 causing ``y`` to be 0 as well. + With the new rules, ``y`` will be set to 42. We first initialize ``x`` to 0, then call A's constructor which sets ``x`` to 42. Finally, when initializing ``y``, ``f()`` returns 42 causing ``y`` to be 42. - * Copying ``bytes`` arrays from memory to storage is implemented in a different way. The old code generator always copies full words, while the new one cuts the byte array after its end. The old behaviour can lead to dirty data being copied after the end of the array (but still in the same storage slot). -This causes differences in some contracts, for example: +- Copying ``bytes`` arrays from memory to storage is implemented in a different way. The old code generator always copies full words, while the new one cuts the byte array after its end. The old behaviour can lead to dirty data being copied after the end of the array (but still in the same storage slot). + This causes differences in some contracts, for example: -.. code-block:: solidity + .. code-block:: solidity - // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.8.0; + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >0.8.0; - contract C { - bytes x; - function f() public returns (uint _r) { - bytes memory m = "tmp"; - assembly { - mstore(m, 8) - mstore(add(m, 32), "deadbeef15dead") - } - x = m; - assembly { - _r := sload(x.slot) - } - } - } + contract C { + bytes x; + function f() public returns (uint _r) { + bytes memory m = "tmp"; + assembly { + mstore(m, 8) + mstore(add(m, 32), "deadbeef15dead") + } + x = m; + assembly { + _r := sload(x.slot) + } + } + } -Previously ``f()`` would return ``0x6465616462656566313564656164000000000000000000000000000000000010`` (it has correct length, and correct first 8 elements, but then it contains dirty data which was set via assembly). -Now it is returning ``0x6465616462656566000000000000000000000000000000000000000000000010`` (it has correct length, and correct elements, but does not contain superfluous data). + Previously ``f()`` would return ``0x6465616462656566313564656164000000000000000000000000000000000010`` (it has correct length, and correct first 8 elements, but then it contains dirty data which was set via assembly). + Now it is returning ``0x6465616462656566000000000000000000000000000000000000000000000010`` (it has correct length, and correct elements, but does not contain superfluous data). -.. index:: ! evaluation order; expression + .. index:: ! evaluation order; expression -* For the old code generator, the evaluation order of expressions is unspecified. +- For the old code generator, the evaluation order of expressions is unspecified. For the new code generator, we try to evaluate in source order (left to right), but do not guarantee it. This can lead to semantic differences. -For example: + For example: -.. code-block:: solidity + .. code-block:: solidity - // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.8.0; - contract C { - function preincr_u8(uint8 _a) public pure returns (uint8) { - return ++_a + _a; - } - } + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >0.8.0; + contract C { + function preincr_u8(uint8 _a) public pure returns (uint8) { + return ++_a + _a; + } + } -The function ``preincr_u8(1)`` returns the following values: -- Old code generator: 3 (``1 + 2``) but the return value is unspecified in general -- New code generator: 4 (``2 + 2``) but the return value is not guaranteed + The function ``preincr_u8(1)`` returns the following values: -.. index:: ! evaluation order; function arguments + - Old code generator: 3 (``1 + 2``) but the return value is unspecified in general + - New code generator: 4 (``2 + 2``) but the return value is not guaranteed -On the other hand, function argument expressions are evaluated in the same order by both code generators with the exception of the global functions ``addmod`` and ``mulmod``. -For example: + .. index:: ! evaluation order; function arguments -.. code-block:: solidity + On the other hand, function argument expressions are evaluated in the same order by both code generators with the exception of the global functions ``addmod`` and ``mulmod``. + For example: - // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.8.0; - contract C { - function add(uint8 _a, uint8 _b) public pure returns (uint8) { - return _a + _b; - } - function g(uint8 _a, uint8 _b) public pure returns (uint8) { - return add(++_a + ++_b, _a + _b); - } - } + .. code-block:: solidity -The function ``g(1, 2)`` returns the following values: -- Old code generator: ``10`` (``add(2 + 3, 2 + 3)``) but the return value is unspecified in general -- New code generator: ``10`` but the return value is not guaranteed + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >0.8.0; + contract C { + function add(uint8 _a, uint8 _b) public pure returns (uint8) { + return _a + _b; + } + function g(uint8 _a, uint8 _b) public pure returns (uint8) { + return add(++_a + ++_b, _a + _b); + } + } -The arguments to the global functions ``addmod`` and ``mulmod`` are evaluated right-to-left by the old code generator -and left-to-right by the new code generator. -For example: + The function ``g(1, 2)`` returns the following values: -:: - // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.8.0; - contract C { - function f() public pure returns (uint256 aMod, uint256 mMod) { - uint256 x = 3; - // Old code gen: add/mulmod(5, 4, 3) - // New code gen: add/mulmod(4, 5, 5) - aMod = addmod(++x, ++x, x); - mMod = mulmod(++x, ++x, x); - } - } + - Old code generator: ``10`` (``add(2 + 3, 2 + 3)``) but the return value is unspecified in general + - New code generator: ``10`` but the return value is not guaranteed -The function ``f()`` returns the following values: -- Old code generator: ``aMod = 0`` and ``mMod = 2`` -- New code generator: ``aMod = 4`` and ``mMod = 0`` + The arguments to the global functions ``addmod`` and ``mulmod`` are evaluated right-to-left by the old code generator + and left-to-right by the new code generator. + For example: + + :: + + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >0.8.0; + contract C { + function f() public pure returns (uint256 aMod, uint256 mMod) { + uint256 x = 3; + // Old code gen: add/mulmod(5, 4, 3) + // New code gen: add/mulmod(4, 5, 5) + aMod = addmod(++x, ++x, x); + mMod = mulmod(++x, ++x, x); + } + } + + The function ``f()`` returns the following values: + + - Old code generator: ``aMod = 0`` and ``mMod = 2`` + - New code generator: ``aMod = 4`` and ``mMod = 0`` Internals @@ -234,6 +241,7 @@ For example: } The function ``f(1)`` returns the following values: + - Old code generator: (``fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe``, ``00000000000000000000000000000000000000000000000000000000000000fe``) - New code generator: (``00000000000000000000000000000000000000000000000000000000000000fe``, ``00000000000000000000000000000000000000000000000000000000000000fe``) diff --git a/docs/natspec-format.rst b/docs/natspec-format.rst index a49ddd895..297bbc8c6 100644 --- a/docs/natspec-format.rst +++ b/docs/natspec-format.rst @@ -166,9 +166,9 @@ Inheritance Notes Functions without NatSpec will automatically inherit the documentation of their base function. Exceptions to this are: - * When the parameter names are different. - * When there is more than one base function. - * When there is an explicit ``@inheritdoc`` tag which specifies which contract should be used to inherit. +* When the parameter names are different. +* When there is more than one base function. +* When there is an explicit ``@inheritdoc`` tag which specifies which contract should be used to inherit. .. _header-output: diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index caa00a83e..8ec1edf27 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -128,10 +128,10 @@ The modulo operation ``a % n`` yields the remainder ``r`` after the division of by the operand ``n``, where ``q = int(a / n)`` and ``r = a - (n * q)``. This means that modulo results in the same sign as its left operand (or zero) and ``a % n == -(-a % n)`` holds for negative ``a``: - * ``int256(5) % int256(2) == int256(1)`` - * ``int256(5) % int256(-2) == int256(1)`` - * ``int256(-5) % int256(2) == int256(-1)`` - * ``int256(-5) % int256(-2) == int256(-1)`` +* ``int256(5) % int256(2) == int256(1)`` +* ``int256(5) % int256(-2) == int256(1)`` +* ``int256(-5) % int256(2) == int256(-1)`` +* ``int256(-5) % int256(-2) == int256(-1)`` .. note:: Modulo with zero causes a :ref:`Panic error`. This check can **not** be disabled through ``unchecked { ... }``. @@ -184,8 +184,8 @@ Address The address type comes in two flavours, which are largely identical: - - ``address``: Holds a 20 byte value (size of an Ethereum address). - - ``address payable``: Same as ``address``, but with the additional members ``transfer`` and ``send``. +- ``address``: Holds a 20 byte value (size of an Ethereum address). +- ``address payable``: Same as ``address``, but with the additional members ``transfer`` and ``send``. The idea behind this distinction is that ``address payable`` is an address you can send Ether to, while a plain ``address`` cannot be sent Ether. @@ -510,15 +510,15 @@ String literals can only contain printable ASCII characters, which means the cha Additionally, string literals also support the following escape characters: - - ``\`` (escapes an actual newline) - - ``\\`` (backslash) - - ``\'`` (single quote) - - ``\"`` (double quote) - - ``\n`` (newline) - - ``\r`` (carriage return) - - ``\t`` (tab) - - ``\xNN`` (hex escape, see below) - - ``\uNNNN`` (unicode escape, see below) +- ``\`` (escapes an actual newline) +- ``\\`` (backslash) +- ``\'`` (single quote) +- ``\"`` (double quote) +- ``\n`` (newline) +- ``\r`` (carriage return) +- ``\t`` (tab) +- ``\xNN`` (hex escape, see below) +- ``\uNNNN`` (unicode escape, see below) ``\xNN`` takes a hex value and inserts the appropriate byte, while ``\uNNNN`` takes a Unicode codepoint and inserts an UTF-8 sequence. @@ -660,9 +660,9 @@ their parameter types are identical, their return types are identical, their internal/external property is identical and the state mutability of ``A`` is more restrictive than the state mutability of ``B``. In particular: - - ``pure`` functions can be converted to ``view`` and ``non-payable`` functions - - ``view`` functions can be converted to ``non-payable`` functions - - ``payable`` functions can be converted to ``non-payable`` functions +- ``pure`` functions can be converted to ``view`` and ``non-payable`` functions +- ``view`` functions can be converted to ``non-payable`` functions +- ``payable`` functions can be converted to ``non-payable`` functions No other conversions between function types are possible. diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 2e912ae84..df79d1b13 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -29,11 +29,11 @@ Suffixes like ``seconds``, ``minutes``, ``hours``, ``days`` and ``weeks`` after literal numbers can be used to specify units of time where seconds are the base unit and units are considered naively in the following way: - * ``1 == 1 seconds`` - * ``1 minutes == 60 seconds`` - * ``1 hours == 60 minutes`` - * ``1 days == 24 hours`` - * ``1 weeks == 7 days`` +* ``1 == 1 seconds`` +* ``1 minutes == 60 seconds`` +* ``1 hours == 60 minutes`` +* ``1 days == 24 hours`` +* ``1 weeks == 7 days`` Take care if you perform calendar calculations using these units, because not every year equals 365 days and not even every day has 24 hours diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index a023a8877..5f261c165 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -30,8 +30,8 @@ set it to ``--optimize-runs=1``. If you expect many transactions and do not care output size, set ``--optimize-runs`` to a high number. 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 +- the size of the binary search in the function dispatch routine +- the way constants like large numbers or strings are stored .. index:: allowed paths, --allow-paths, base path, --base-path diff --git a/docs/yul.rst b/docs/yul.rst index 093bf6597..b8557475f 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -157,16 +157,16 @@ where an object is expected. Inside a code block, the following elements can be used (see the later sections for more details): - - literals, i.e. ``0x123``, ``42`` or ``"abc"`` (strings up to 32 characters) - - calls to builtin functions, e.g. ``add(1, mload(0))`` - - variable declarations, e.g. ``let x := 7``, ``let x := add(y, 3)`` or ``let x`` (initial value of 0 is assigned) - - identifiers (variables), e.g. ``add(3, x)`` - - assignments, e.g. ``x := add(y, 3)`` - - blocks where local variables are scoped inside, e.g. ``{ let x := 3 { let y := add(x, 1) } }`` - - if statements, e.g. ``if lt(a, b) { sstore(0, 1) }`` - - switch statements, e.g. ``switch mload(0) case 0 { revert() } default { mstore(0, 1) }`` - - for loops, e.g. ``for { let i := 0} lt(i, 10) { i := add(i, 1) } { mstore(i, 7) }`` - - function definitions, e.g. ``function f(a, b) -> c { c := add(a, b) }``` +- literals, i.e. ``0x123``, ``42`` or ``"abc"`` (strings up to 32 characters) +- calls to builtin functions, e.g. ``add(1, mload(0))`` +- variable declarations, e.g. ``let x := 7``, ``let x := add(y, 3)`` or ``let x`` (initial value of 0 is assigned) +- identifiers (variables), e.g. ``add(3, x)`` +- assignments, e.g. ``x := add(y, 3)`` +- blocks where local variables are scoped inside, e.g. ``{ let x := 3 { let y := add(x, 1) } }`` +- if statements, e.g. ``if lt(a, b) { sstore(0, 1) }`` +- switch statements, e.g. ``switch mload(0) case 0 { revert() } default { mstore(0, 1) }`` +- for loops, e.g. ``for { let i := 0} lt(i, 10) { i := add(i, 1) } { mstore(i, 7) }`` +- function definitions, e.g. ``function f(a, b) -> c { c := add(a, b) }``` Multiple syntactical elements can follow each other simply separated by whitespace, i.e. there is no terminating ``;`` or newline required. @@ -985,9 +985,10 @@ that are not known to the Yul compiler. It also allows you to create bytecode sequences that will not be modified by the optimizer. The functions are ``verbatim_i_o("", ...)``, where - - ``n`` is a decimal between 0 and 99 that specifies the number of input stack slots / variables - - ``m`` is a decimal between 0 and 99 that specifies the number of output stack slots / variables - - ``data`` is a string literal that contains the sequence of bytes + +- ``n`` is a decimal between 0 and 99 that specifies the number of input stack slots / variables +- ``m`` is a decimal between 0 and 99 that specifies the number of output stack slots / variables +- ``data`` is a string literal that contains the sequence of bytes If you for example want to define a function that multiplies the input by two, without the optimizer touching the constant two, you can use @@ -1022,15 +1023,15 @@ verbatim bytecode that are not checked by the compiler. Violations of these restrictions can result in undefined behaviour. - - Control-flow should not jump into or out of verbatim blocks, - but it can jump within the same verbatim block. - - Stack contents apart from the input and output parameters - should not be accessed. - - The stack height difference should be exactly ``m - n`` - (output slots minus input slots). - - Verbatim bytecode cannot make any assumptions about the - surrounding bytecode. All required parameters have to be - passed in as stack variables. +- Control-flow should not jump into or out of verbatim blocks, + but it can jump within the same verbatim block. +- Stack contents apart from the input and output parameters + should not be accessed. +- The stack height difference should be exactly ``m - n`` + (output slots minus input slots). +- Verbatim bytecode cannot make any assumptions about the + surrounding bytecode. All required parameters have to be + passed in as stack variables. The optimizer does not analyze verbatim bytecode and always assumes that it modifies all aspects of state and thus can only diff --git a/libsolidity/codegen/ir/README.md b/libsolidity/codegen/ir/README.md index 468ecd269..cfabd83b9 100644 --- a/libsolidity/codegen/ir/README.md +++ b/libsolidity/codegen/ir/README.md @@ -6,5 +6,5 @@ with EVM dialect. The main semantic differences to the legacy code generator are the following: - - Arithmetic operations cause a failing assertion if the result is not in range. - - Resizing a storage array to a length larger than 2**64 causes a failing assertion. \ No newline at end of file +- Arithmetic operations cause a failing assertion if the result is not in range. +- Resizing a storage array to a length larger than 2**64 causes a failing assertion. From 42f982c0636f73dabed1675e897200eea3ae1d9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 14 Jul 2021 18:56:55 +0200 Subject: [PATCH 39/98] docs: Fix indentation in some of the code snippets --- docs/abi-spec.rst | 170 +++++++++++++++---------------- docs/contracts/events.rst | 22 ++-- docs/internals/optimizer.rst | 46 ++++----- docs/layout-of-source-files.rst | 16 +-- docs/smtchecker.rst | 175 ++++++++++++++++---------------- docs/using-the-compiler.rst | 14 +-- 6 files changed, 222 insertions(+), 221 deletions(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 0c38e0f27..2b0ffbe8e 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -530,12 +530,12 @@ i.e. ``0xcf479181``, ``uint256(0)``, ``uint256(amount)``. The error selectors ``0x00000000`` and ``0xffffffff`` are reserved for future use. .. warning:: - Never trust error data. - The error data by default bubbles up through the chain of external calls, which - means that a contract may receive an error not defined in any of the contracts - it calls directly. - Furthermore, any contract can fake any error by returning data that matches - an error signature, even if the error is not defined anywhere. + Never trust error data. + The error data by default bubbles up through the chain of external calls, which + means that a contract may receive an error not defined in any of the contracts + it calls directly. + Furthermore, any contract can fake any error by returning data that matches + an error signature, even if the error is not defined anywhere. .. _abi_json: @@ -619,24 +619,24 @@ would result in the JSON: .. code-block:: json - [{ - "type":"error", - "inputs": [{"name":"available","type":"uint256"},{"name":"required","type":"uint256"}], - "name":"InsufficientBalance" - }, { - "type":"event", - "inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}], - "name":"Event" - }, { - "type":"event", - "inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}], - "name":"Event2" - }, { - "type":"function", - "inputs": [{"name":"a","type":"uint256"}], - "name":"foo", - "outputs": [] - }] + [{ + "type":"error", + "inputs": [{"name":"available","type":"uint256"},{"name":"required","type":"uint256"}], + "name":"InsufficientBalance" + }, { + "type":"event", + "inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}], + "name":"Event" + }, { + "type":"event", + "inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}], + "name":"Event2" + }, { + "type":"function", + "inputs": [{"name":"a","type":"uint256"}], + "name":"foo", + "outputs": [] + }] Handling tuple types -------------------- @@ -671,61 +671,61 @@ would result in the JSON: .. code-block:: json - [ - { - "name": "f", - "type": "function", - "inputs": [ - { - "name": "s", - "type": "tuple", - "components": [ - { - "name": "a", - "type": "uint256" - }, - { - "name": "b", - "type": "uint256[]" - }, - { - "name": "c", - "type": "tuple[]", - "components": [ - { - "name": "x", - "type": "uint256" - }, - { - "name": "y", - "type": "uint256" - } - ] - } - ] - }, - { - "name": "t", - "type": "tuple", - "components": [ - { - "name": "x", - "type": "uint256" - }, - { - "name": "y", - "type": "uint256" - } - ] - }, - { - "name": "a", - "type": "uint256" - } - ], - "outputs": [] - } - ] + [ + { + "name": "f", + "type": "function", + "inputs": [ + { + "name": "s", + "type": "tuple", + "components": [ + { + "name": "a", + "type": "uint256" + }, + { + "name": "b", + "type": "uint256[]" + }, + { + "name": "c", + "type": "tuple[]", + "components": [ + { + "name": "x", + "type": "uint256" + }, + { + "name": "y", + "type": "uint256" + } + ] + } + ] + }, + { + "name": "t", + "type": "tuple", + "components": [ + { + "name": "x", + "type": "uint256" + }, + { + "name": "y", + "type": "uint256" + } + ] + }, + { + "name": "a", + "type": "uint256" + } + ], + "outputs": [] + } + ] .. _abi_packed_mode: @@ -786,12 +786,12 @@ for prepending a function selector. Since the encoding is ambiguous, there is no .. warning:: - If you use ``keccak256(abi.encodePacked(a, b))`` and both ``a`` and ``b`` are dynamic types, - it is easy to craft collisions in the hash value by moving parts of ``a`` into ``b`` and - vice-versa. More specifically, ``abi.encodePacked("a", "bc") == abi.encodePacked("ab", "c")``. - If you use ``abi.encodePacked`` for signatures, authentication or data integrity, make - sure to always use the same types and check that at most one of them is dynamic. - Unless there is a compelling reason, ``abi.encode`` should be preferred. + If you use ``keccak256(abi.encodePacked(a, b))`` and both ``a`` and ``b`` are dynamic types, + it is easy to craft collisions in the hash value by moving parts of ``a`` into ``b`` and + vice-versa. More specifically, ``abi.encodePacked("a", "bc") == abi.encodePacked("ab", "c")``. + If you use ``abi.encodePacked`` for signatures, authentication or data integrity, make + sure to always use the same types and check that at most one of them is dynamic. + Unless there is a compelling reason, ``abi.encode`` should be preferred. .. _indexed_event_encoding: diff --git a/docs/contracts/events.rst b/docs/contracts/events.rst index d282499dc..3c34d0d1e 100644 --- a/docs/contracts/events.rst +++ b/docs/contracts/events.rst @@ -124,17 +124,17 @@ The output of the above looks like the following (trimmed): .. code-block:: json - { - "returnValues": { - "_from": "0x1111…FFFFCCCC", - "_id": "0x50…sd5adb20", - "_value": "0x420042" - }, - "raw": { - "data": "0x7f…91385", - "topics": ["0xfd4…b4ead7", "0x7f…1a91385"] - } - } + { + "returnValues": { + "_from": "0x1111…FFFFCCCC", + "_id": "0x50…sd5adb20", + "_value": "0x420042" + }, + "raw": { + "data": "0x7f…91385", + "topics": ["0xfd4…b4ead7", "0x7f…1a91385"] + } + } Additional Resources for Understanding Events ============================================== diff --git a/docs/internals/optimizer.rst b/docs/internals/optimizer.rst index 64c8b1460..aee7c8493 100644 --- a/docs/internals/optimizer.rst +++ b/docs/internals/optimizer.rst @@ -89,21 +89,21 @@ the sequence: .. code-block:: none - PUSH 32 - PUSH 0 - CALLDATALOAD - PUSH 100 - DUP2 - MSTORE - KECCAK256 + PUSH 32 + PUSH 0 + CALLDATALOAD + PUSH 100 + DUP2 + MSTORE + KECCAK256 or the equivalent Yul .. code-block:: yul - let x := calldataload(0) - mstore(x, 100) - let value := keccak256(x, 32) + let x := calldataload(0) + mstore(x, 100) + let value := keccak256(x, 32) In this case, the optimizer tracks the value at a memory location ``calldataload(0)`` and then realizes that the Keccak-256 hash can be evaluated at compile time. This only works if there is no @@ -116,14 +116,14 @@ For example, .. code-block:: yul - let x := calldataload(0) - mstore(x, 100) - // Current knowledge memory location x -> 100 - let y := add(x, 32) - // Does not clear the knowledge that x -> 100, since y does not write to [x, x + 32) - mstore(y, 200) - // This Keccak-256 can now be evaluated - let value := keccak256(x, 32) + let x := calldataload(0) + mstore(x, 100) + // Current knowledge memory location x -> 100 + let y := add(x, 32) + // Does not clear the knowledge that x -> 100, since y does not write to [x, x + 32) + mstore(y, 200) + // This Keccak-256 can now be evaluated + let value := keccak256(x, 32) Therefore, modifications to storage and memory locations, of say location ``l``, must erase knowledge about storage or memory locations which may be equal to ``l``. More specifically, for @@ -239,8 +239,8 @@ for all references to ``tag_f`` leaving it unused, s.t. it can be removed, yield .. code-block:: text - ...body of function f... - ...opcodes after call to f... + ...body of function f... + ...opcodes after call to f... So the call to function ``f`` is inlined and the original definition of ``f`` can be removed. @@ -375,7 +375,7 @@ After this step, a program has the following normal form: .. code-block:: text - { I F... } + { I F... } Where ``I`` is a (potentially empty) block that does not contain any function definitions (not even recursively) and ``F`` is a list of function definitions such that no function contains a function definition. @@ -1053,8 +1053,8 @@ remove the parameter and create a new "linking" function as follows: .. code-block:: yul - function f(a,b) -> x { x := div(a,b) } - function f2(a,b,c) -> x, y { x := f(a,b) } + function f(a,b) -> x { x := div(a,b) } + function f2(a,b,c) -> x, y { x := f(a,b) } and replace all references to ``f`` by ``f2``. The inliner should be run afterwards to make sure that all references to ``f2`` are replaced by diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst index 22624c410..9a54a14ef 100644 --- a/docs/layout-of-source-files.rst +++ b/docs/layout-of-source-files.rst @@ -182,7 +182,7 @@ At a global level, you can use import statements of the following form: :: - import "filename"; + import "filename"; The ``filename`` part is called an *import path*. This statement imports all global symbols from "filename" (and symbols imported there) into the @@ -197,7 +197,7 @@ the global symbols from ``"filename"``: :: - import * as symbolName from "filename"; + import * as symbolName from "filename"; which results in all global symbols being available in the format ``symbolName.symbol``. @@ -215,7 +215,7 @@ the code below creates new global symbols ``alias`` and ``symbol2`` which refere :: - import {symbol1 as alias, symbol2} from "filename"; + import {symbol1 as alias, symbol2} from "filename"; .. index:: virtual filesystem, source unit name, import; path, filesystem path, import callback, Remix IDE @@ -255,12 +255,12 @@ Single-line comments (``//``) and multi-line comments (``/*...*/``) are possible :: - // This is a single-line comment. + // This is a single-line comment. - /* - This is a - multi-line comment. - */ + /* + This is a + multi-line comment. + */ .. note:: A single-line comment is terminated by any unicode line terminator diff --git a/docs/smtchecker.rst b/docs/smtchecker.rst index dff8c8463..a4580ce4d 100644 --- a/docs/smtchecker.rst +++ b/docs/smtchecker.rst @@ -44,23 +44,24 @@ where the default is no engine. Selecting the engine enables the SMTChecker on a .. note:: - Prior to Solidity 0.8.4, the default way to enable the SMTChecker was via - ``pragma experimental SMTChecker;`` and only the contracts containing the - pragma would be analyzed. That pragma has been deprecated, and although it - still enables the SMTChecker for backwards compatibility, it will be removed - in Solidity 0.9.0. Note also that now using the pragma even in a single file - enables the SMTChecker for all files. + Prior to Solidity 0.8.4, the default way to enable the SMTChecker was via + ``pragma experimental SMTChecker;`` and only the contracts containing the + pragma would be analyzed. That pragma has been deprecated, and although it + still enables the SMTChecker for backwards compatibility, it will be removed + in Solidity 0.9.0. Note also that now using the pragma even in a single file + enables the SMTChecker for all files. .. note:: - The lack of warnings for a verification target represents an undisputed - mathematical proof of correctness, assuming no bugs in the SMTChecker and - the underlying solver. Keep in mind that these problems are - *very hard* and sometimes *impossible* to solve automatically in the - general case. Therefore, several properties might not be solved or might - lead to false positives for large contracts. Every proven property should - be seen as an important achievement. For advanced users, see :ref:`SMTChecker Tuning ` - to learn a few options that might help proving more complex - properties. + + The lack of warnings for a verification target represents an undisputed + mathematical proof of correctness, assuming no bugs in the SMTChecker and + the underlying solver. Keep in mind that these problems are + *very hard* and sometimes *impossible* to solve automatically in the + general case. Therefore, several properties might not be solved or might + lead to false positives for large contracts. Every proven property should + be seen as an important achievement. For advanced users, see :ref:`SMTChecker Tuning ` + to learn a few options that might help proving more complex + properties. ******** Tutorial @@ -202,8 +203,9 @@ Note that in this example the SMTChecker will automatically try to prove three p 3. The assertion is always true. .. note:: - The properties involve loops, which makes it *much much* harder than the previous - examples, so beware of loops! + + The properties involve loops, which makes it *much much* harder than the previous + examples, so beware of loops! All the properties are correctly proven safe. Feel free to change the properties and/or add restrictions on the array to see different results. @@ -233,18 +235,18 @@ gives us: .. code-block:: bash - Warning: CHC: Assertion violation happens here. - Counterexample: + Warning: CHC: Assertion violation happens here. + Counterexample: - _a = [0, 0, 0, 0, 0] - = 0 + _a = [0, 0, 0, 0, 0] + = 0 - Transaction trace: - Test.constructor() - Test.max([0, 0, 0, 0, 0]) - --> max.sol:14:4: - | - 14 | assert(m > _a[i]); + Transaction trace: + Test.constructor() + Test.max([0, 0, 0, 0, 0]) + --> max.sol:14:4: + | + 14 | assert(m > _a[i]); State Properties @@ -323,26 +325,26 @@ the SMTChecker tells us exactly *how* to reach (2, 4): .. code-block:: bash - Warning: CHC: Assertion violation happens here. - Counterexample: - x = 2, y = 4 + Warning: CHC: Assertion violation happens here. + Counterexample: + x = 2, y = 4 - Transaction trace: - Robot.constructor() - State: x = 0, y = 0 - Robot.moveLeftUp() - State: x = (- 1), y = 1 - Robot.moveRightUp() - State: x = 0, y = 2 - Robot.moveRightUp() - State: x = 1, y = 3 - Robot.moveRightUp() - State: x = 2, y = 4 - Robot.reach_2_4() - --> r.sol:35:4: - | - 35 | assert(!(x == 2 && y == 4)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Transaction trace: + Robot.constructor() + State: x = 0, y = 0 + Robot.moveLeftUp() + State: x = (- 1), y = 1 + Robot.moveRightUp() + State: x = 0, y = 2 + Robot.moveRightUp() + State: x = 1, y = 3 + Robot.moveRightUp() + State: x = 2, y = 4 + Robot.reach_2_4() + --> r.sol:35:4: + | + 35 | assert(!(x == 2 && y == 4)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Note that the path above is not necessarily deterministic, as there are other paths that could reach (2, 4). The choice of which path is shown @@ -367,36 +369,36 @@ anything, including reenter the caller contract. pragma solidity >=0.8.0; interface Unknown { - function run() external; + function run() external; } contract Mutex { - uint x; - bool lock; + uint x; + bool lock; - Unknown immutable unknown; + Unknown immutable unknown; - constructor(Unknown _u) { - require(address(_u) != address(0)); - unknown = _u; - } + constructor(Unknown _u) { + require(address(_u) != address(0)); + unknown = _u; + } - modifier mutex { - require(!lock); - lock = true; - _; - lock = false; - } + modifier mutex { + require(!lock); + lock = true; + _; + lock = false; + } - function set(uint _x) mutex public { - x = _x; - } + function set(uint _x) mutex public { + x = _x; + } - function run() mutex public { - uint xPre = x; - unknown.run(); - assert(xPre == x); - } + function run() mutex public { + uint xPre = x; + unknown.run(); + assert(xPre == x); + } } The example above shows a contract that uses a mutex flag to forbid reentrancy. @@ -410,20 +412,20 @@ that the assertion fails: .. code-block:: bash - Warning: CHC: Assertion violation happens here. - Counterexample: - x = 1, lock = true, unknown = 1 + Warning: CHC: Assertion violation happens here. + Counterexample: + x = 1, lock = true, unknown = 1 - Transaction trace: - Mutex.constructor(1) - State: x = 0, lock = false, unknown = 1 - Mutex.run() - unknown.run() -- untrusted external call, synthesized as: - Mutex.set(1) -- reentrant call - --> m.sol:32:3: - | - 32 | assert(xPre == x); - | ^^^^^^^^^^^^^^^^^ + Transaction trace: + Mutex.constructor(1) + State: x = 0, lock = false, unknown = 1 + Mutex.run() + unknown.run() -- untrusted external call, synthesized as: + Mutex.set(1) -- reentrant call + --> m.sol:32:3: + | + 32 | assert(xPre == x); + | ^^^^^^^^^^^^^^^^^ .. _smtchecker_options: @@ -494,12 +496,11 @@ which has the following form: .. code-block:: none - contracts - { - "source1.sol": ["contract1"], - "source2.sol": ["contract2", "contract3"] - } - + contracts + { + "source1.sol": ["contract1"], + "source2.sol": ["contract2", "contract3"] + } .. _smtchecker_engines: diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 5f261c165..536a66fdd 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -136,13 +136,13 @@ key in the ``"settings"`` field: .. code-block:: none - { - "sources": { ... }, - "settings": { - "optimizer": { ... }, - "evmVersion": "" + { + "sources": { ... }, + "settings": { + "optimizer": { ... }, + "evmVersion": "" + } } - } Target Options -------------- @@ -781,7 +781,7 @@ It is recommended to explicitly specify the upgrade modules by using ``--modules .. code-block:: none - $ solidity-upgrade --modules constructor-visibility,now,dotsyntax Source.sol + $ solidity-upgrade --modules constructor-visibility,now,dotsyntax Source.sol The command above applies all changes as shown below. Please review them carefully (the pragmas will have to be updated manually.) From b513ca1f7e302e40bf0f2044a535648b10305a74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 14 Jul 2021 17:55:27 +0200 Subject: [PATCH 40/98] ir-breaking-changes.rst: Fix too short header frame --- docs/ir/ir-breaking-changes.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/ir/ir-breaking-changes.rst b/docs/ir/ir-breaking-changes.rst index 29b548654..c9c0936cc 100644 --- a/docs/ir/ir-breaking-changes.rst +++ b/docs/ir/ir-breaking-changes.rst @@ -1,6 +1,6 @@ -******************************** +********************************* Solidity IR-based Codegen Changes -******************************** +********************************* This section highlights the main differences between the old and the IR-based codegen, along with the reasoning behind the changes and how to update affected code. From f3a5c27852f6c48be858a37bbe1f694e8b7299a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 14 Jul 2021 18:52:39 +0200 Subject: [PATCH 41/98] docs: Replace a few remaining `::` blocks with `.. code-block::` --- docs/cheatsheet.rst | 3 ++- docs/contracts/events.rst | 2 +- docs/internals/optimizer.rst | 12 ++++++------ docs/introduction-to-smart-contracts.rst | 8 ++++++-- docs/ir/ir-breaking-changes.rst | 2 +- docs/layout-of-source-files.rst | 10 +++++----- docs/metadata.rst | 4 +++- docs/path-resolution.rst | 2 +- docs/style-guide.rst | 4 +++- docs/types/value-types.rst | 2 +- docs/using-the-compiler.rst | 2 +- 11 files changed, 30 insertions(+), 21 deletions(-) diff --git a/docs/cheatsheet.rst b/docs/cheatsheet.rst index 10a7ce105..683814b28 100644 --- a/docs/cheatsheet.rst +++ b/docs/cheatsheet.rst @@ -158,7 +158,8 @@ Global Variables Function Visibility Specifiers ============================== -:: +.. code-block:: solidity + :force: function myFunction() returns (bool) { return true; diff --git a/docs/contracts/events.rst b/docs/contracts/events.rst index 3c34d0d1e..6e99a332b 100644 --- a/docs/contracts/events.rst +++ b/docs/contracts/events.rst @@ -97,7 +97,7 @@ four indexed arguments rather than three. The use in the JavaScript API is as follows: -:: +.. code-block:: javascript var abi = /* abi as generated by the compiler */; var ClientReceipt = web3.eth.contract(abi); diff --git a/docs/internals/optimizer.rst b/docs/internals/optimizer.rst index aee7c8493..a10b0e462 100644 --- a/docs/internals/optimizer.rst +++ b/docs/internals/optimizer.rst @@ -696,7 +696,7 @@ operation where ``unused = 0``, ``undecided = 1`` and ``used = 2``. The proper way would be to compute -:: +.. code-block:: none max(s, f(s), f(f(s)), f(f(f(s))), ...) @@ -705,7 +705,7 @@ iterating it has to reach a cycle after at most three iterations, and thus ``f(f(f(s)))`` has to equal one of ``s``, ``f(s)``, or ``f(f(s))`` and thus -:: +.. code-block:: none max(s, f(s), f(f(s))) = max(s, f(s), f(f(s)), f(f(f(s))), ...). @@ -1263,7 +1263,7 @@ Reverses the transformation of ForLoopConditionIntoBody. For any movable ``c``, it turns -:: +.. code-block:: none for { ... } 1 { ... } { if iszero(c) { break } @@ -1272,7 +1272,7 @@ For any movable ``c``, it turns into -:: +.. code-block:: none for { ... } c { ... } { ... @@ -1280,7 +1280,7 @@ into and it turns -:: +.. code-block:: none for { ... } 1 { ... } { if c { break } @@ -1289,7 +1289,7 @@ and it turns into -:: +.. code-block:: none for { ... } iszero(c) { ... } { ... diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 6b427226c..c22d4e6fa 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -140,7 +140,9 @@ of a keypair belonging to :ref:`external accounts`. The keyword ``public`` automatically generates a function that allows you to access the current value of the state variable from outside of the contract. Without this keyword, other contracts have no way to access the variable. The code of the function generated by the compiler is equivalent -to the following (ignore ``external`` and ``view`` for now):: +to the following (ignore ``external`` and ``view`` for now): + +.. code-block:: solidity function minter() external view returns (address) { return minter; } @@ -162,7 +164,9 @@ even better, keep a list, or use a more suitable data type. The :ref:`getter function` created by the ``public`` keyword is more complex in the case of a mapping. It looks like the -following:: +following: + +.. code-block:: solidity function balances(address _account) external view returns (uint) { return balances[_account]; diff --git a/docs/ir/ir-breaking-changes.rst b/docs/ir/ir-breaking-changes.rst index c9c0936cc..5c0c08d51 100644 --- a/docs/ir/ir-breaking-changes.rst +++ b/docs/ir/ir-breaking-changes.rst @@ -174,7 +174,7 @@ This causes differences in some contracts, for example: and left-to-right by the new code generator. For example: - :: + .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >0.8.0; diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst index 9a54a14ef..cd031bb01 100644 --- a/docs/layout-of-source-files.rst +++ b/docs/layout-of-source-files.rst @@ -180,7 +180,7 @@ a `default export , "solc": }`` is stored contain more keys (see below) and the beginning of that encoding is not easy to find, its length is added in a two-byte big-endian encoding. The current version of the Solidity compiler usually adds the following -to the end of the deployed bytecode:: +to the end of the deployed bytecode + +.. code-block:: text 0xa2 0x64 'i' 'p' 'f' 's' 0x58 0x22 <34 bytes IPFS hash> diff --git a/docs/path-resolution.rst b/docs/path-resolution.rst index 5848e3f9a..88f5fd224 100644 --- a/docs/path-resolution.rst +++ b/docs/path-resolution.rst @@ -180,7 +180,7 @@ Direct Imports An import that does not start with ``./`` or ``../`` is a *direct import*. -:: +.. code-block:: solidity import "/project/lib/util.sol"; // source unit name: /project/lib/util.sol import "lib/util.sol"; // source unit name: lib/util.sol diff --git a/docs/style-guide.rst b/docs/style-guide.rst index edb3b25ee..e05f9349a 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -410,7 +410,9 @@ No: spam( ham[ 1 ], Coin( { name: "ham" } ) ); -Exception:: +Exception: + +.. code-block:: solidity function singleLine() public { spam(); } diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index 8ec1edf27..5df4557a7 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -534,7 +534,7 @@ It starts with a newline byte, followed by a double quote, a single quote a backslash character and then (without separator) the character sequence ``abcdef``. -:: +.. code-block:: solidity "\n\"\'\\abc\ def" diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 536a66fdd..acc6bfdd1 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -41,7 +41,7 @@ Base Path and Import Remapping The commandline compiler will automatically read imported files from the filesystem, but it is also possible to provide :ref:`path redirects ` using ``prefix=path`` in the following way: -:: +.. code-block:: bash solc github.com/ethereum/dapp-bin/=/usr/local/lib/dapp-bin/ file.sol From 235a39eaefd34bdc925ba845cabe40fc32927c2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 14 Jul 2021 18:53:15 +0200 Subject: [PATCH 42/98] docs: Fix syntax errors in JSON snippets and enable JavaScript highlighting for them --- docs/metadata.rst | 68 ++++++++++++++++++------------------- docs/smtchecker.rst | 5 ++- docs/using-the-compiler.rst | 30 ++++++++-------- 3 files changed, 51 insertions(+), 52 deletions(-) diff --git a/docs/metadata.rst b/docs/metadata.rst index a1cc3b9cc..1220f7822 100644 --- a/docs/metadata.rst +++ b/docs/metadata.rst @@ -31,24 +31,24 @@ reduce whitespace to a minimum and sort the keys of all objects to arrive at a unique formatting. Comments are not permitted and used here only for explanatory purposes. -.. code-block:: none +.. code-block:: javascript { // Required: The version of the metadata format - version: "1", + "version": "1", // Required: Source code language, basically selects a "sub-version" // of the specification - language: "Solidity", + "language": "Solidity", // Required: Details about the compiler, contents are specific // to the language. - compiler: { + "compiler": { // Required for Solidity: Version of the compiler - version: "0.4.6+commit.2dabbdf0.Emscripten.clang", + "version": "0.4.6+commit.2dabbdf0.Emscripten.clang", // Optional: Hash of the compiler binary which produced this output - keccak256: "0x123..." + "keccak256": "0x123..." }, // Required: Compilation source files/source units, keys are file names - sources: + "sources": { "myFile.sol": { // Required: keccak256 hash of the source file @@ -68,59 +68,59 @@ explanatory purposes. } }, // Required: Compiler settings - settings: + "settings": { // Required for Solidity: Sorted list of remappings - remappings: [ ":g=/dir" ], + "remappings": [ ":g=/dir" ], // Optional: Optimizer settings. The fields "enabled" and "runs" are deprecated // and are only given for backwards-compatibility. - optimizer: { - enabled: true, - runs: 500, - details: { + "optimizer": { + "enabled": true, + "runs": 500, + "details": { // peephole defaults to "true" - peephole: true, + "peephole": true, // inliner defaults to "true" - inliner: true, + "inliner": true, // jumpdestRemover defaults to "true" - jumpdestRemover: true, - orderLiterals: false, - deduplicate: false, - cse: false, - constantOptimizer: false, - yul: true, + "jumpdestRemover": true, + "orderLiterals": false, + "deduplicate": false, + "cse": false, + "constantOptimizer": false, + "yul": true, // Optional: Only present if "yul" is "true" - yulDetails: { - stackAllocation: false, - optimizerSteps: "dhfoDgvulfnTUtnIf..." + "yulDetails": { + "stackAllocation": false, + "optimizerSteps": "dhfoDgvulfnTUtnIf..." } } }, - metadata: { + "metadata": { // Reflects the setting used in the input json, defaults to false - useLiteralContent: true, + "useLiteralContent": true, // Reflects the setting used in the input json, defaults to "ipfs" - bytecodeHash: "ipfs" - } + "bytecodeHash": "ipfs" + }, // Required for Solidity: File and name of the contract or library this // metadata is created for. - compilationTarget: { + "compilationTarget": { "myFile.sol": "MyContract" }, // Required for Solidity: Addresses for libraries used - libraries: { + "libraries": { "MyLib": "0x123123..." } }, // Required: Generated information about the contract. - output: + "output": { // Required: ABI definition of the contract - abi: [ ... ], + "abi": [/* ... */], // Required: NatSpec user documentation of the contract - userdoc: [ ... ], + "userdoc": [/* ... */], // Required: NatSpec developer documentation of the contract - devdoc: [ ... ], + "devdoc": [/* ... */] } } diff --git a/docs/smtchecker.rst b/docs/smtchecker.rst index a4580ce4d..db0522bbd 100644 --- a/docs/smtchecker.rst +++ b/docs/smtchecker.rst @@ -494,10 +494,9 @@ allowed) of : pairs in the CLI: and via the object ``settings.modelChecker.contracts`` in the :ref:`JSON input`, which has the following form: -.. code-block:: none +.. code-block:: json - contracts - { + "contracts": { "source1.sol": ["contract1"], "source2.sol": ["contract2", "contract3"] } diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index acc6bfdd1..97c0e7542 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -134,12 +134,12 @@ On the command line, you can select the EVM version as follows: In the :ref:`standard JSON interface `, use the ``"evmVersion"`` key in the ``"settings"`` field: -.. code-block:: none +.. code-block:: javascript { - "sources": { ... }, + "sources": {/* ... */}, "settings": { - "optimizer": { ... }, + "optimizer": {/* ... */}, "evmVersion": "" } } @@ -198,7 +198,7 @@ Comments are of course not permitted and used here only for explanatory purposes Input Description ----------------- -.. code-block:: none +.. code-block:: javascript { // Required: Source code language. Currently supported are "Solidity" and "Yul". @@ -310,7 +310,7 @@ Input Description // "debug" injects strings for compiler-generated internal reverts, implemented for ABI encoders V1 and V2 for now. // "verboseDebug" even appends further information to user-supplied revert strings (not yet implemented) "revertStrings": "default" - } + }, // Metadata settings (optional) "metadata": { // Use only literal content and not URLs (false by default) @@ -331,7 +331,7 @@ Input Description "myFile.sol": { "MyLib": "0x123123..." } - } + }, // The following can be used to select desired outputs based // on file and contract names. // If this field is omitted, then the compiler loads and does type checking, @@ -395,7 +395,7 @@ Input Description "modelChecker": { // Chose which contracts should be analyzed as the deployed one. - contracts: + "contracts": { "source1.sol": ["contract1"], "source2.sol": ["contract2", "contract3"] @@ -420,7 +420,7 @@ Input Description Output Description ------------------ -.. code-block:: none +.. code-block:: javascript { // Optional: not present if no errors/warnings were encountered @@ -431,7 +431,7 @@ Output Description "file": "sourceFile.sol", "start": 0, "end": 100 - ], + }, // Optional: Further locations (e.g. places of conflicting declarations) "secondarySourceLocations": [ { @@ -463,7 +463,7 @@ Output Description // Identifier of the source (used in source maps) "id": 1, // The AST object - "ast": {}, + "ast": {} } }, // This contains the contract-level outputs. @@ -476,7 +476,7 @@ Output Description // See https://docs.soliditylang.org/en/develop/abi-spec.html "abi": [], // See the Metadata Output documentation (serialised JSON string) - "metadata": "{...}", + "metadata": "{/* ... */}", // User documentation (natspec) "userdoc": {}, // Developer documentation (natspec) @@ -484,7 +484,7 @@ Output Description // Intermediate representation (string) "ir": "", // See the Storage Layout documentation. - "storageLayout": {"storage": [...], "types": {...} }, + "storageLayout": {"storage": [/* ... */], "types": {/* ... */} }, // EVM-related outputs "evm": { // Assembly (string) @@ -514,14 +514,14 @@ Output Description // contains a single Yul file. "generatedSources": [{ // Yul AST - "ast": { ... } + "ast": {/* ... */}, // Source file in its text form (may contain comments) "contents":"{ function abi_decode(start, end) -> data { data := calldataload(start) } }", // Source file ID, used for source references, same "namespace" as the Solidity source files "id": 2, "language": "Yul", "name": "#utility.yul" - }] + }], // If given, this is an unlinked object. "linkReferences": { "libraryFile.sol": { @@ -535,7 +535,7 @@ Output Description } }, "deployedBytecode": { - ..., // The same layout as above. + /* ..., */ // The same layout as above. "immutableReferences": { // There are two references to the immutable with AST ID 3, both 32 bytes long. One is // at bytecode offset 42, the other at bytecode offset 80. From 310a35185a6ffc32219dbf3553e0934bffa705ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 14 Jul 2021 19:05:42 +0200 Subject: [PATCH 43/98] docs: Fix some mislabeled bash and text blocks --- docs/smtchecker.rst | 8 ++++---- docs/using-the-compiler.rst | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/smtchecker.rst b/docs/smtchecker.rst index db0522bbd..f5087d553 100644 --- a/docs/smtchecker.rst +++ b/docs/smtchecker.rst @@ -97,7 +97,7 @@ The SMTChecker will, by default, check every reachable arithmetic operation in the contract for potential underflow and overflow. Here, it reports the following: -.. code-block:: bash +.. code-block:: text Warning: CHC: Overflow (resulting value larger than 2**256 - 1) happens here. Counterexample: @@ -233,7 +233,7 @@ For example, changing the code to gives us: -.. code-block:: bash +.. code-block:: text Warning: CHC: Assertion violation happens here. Counterexample: @@ -323,7 +323,7 @@ reachable, by adding the following function. This property is false, and while proving that the property is false, the SMTChecker tells us exactly *how* to reach (2, 4): -.. code-block:: bash +.. code-block:: text Warning: CHC: Assertion violation happens here. Counterexample: @@ -410,7 +410,7 @@ If we "forget" to use the ``mutex`` modifier on function ``set``, the SMTChecker is able to synthesize the behavior of the externally called code so that the assertion fails: -.. code-block:: bash +.. code-block:: text Warning: CHC: Assertion violation happens here. Counterexample: diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 97c0e7542..9ae6b15c9 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -779,9 +779,9 @@ Running the Upgrade It is recommended to explicitly specify the upgrade modules by using ``--modules`` argument. -.. code-block:: none +.. code-block:: bash - $ solidity-upgrade --modules constructor-visibility,now,dotsyntax Source.sol + solidity-upgrade --modules constructor-visibility,now,dotsyntax Source.sol The command above applies all changes as shown below. Please review them carefully (the pragmas will have to be updated manually.) From d8260a1e9325292f4d399c69cb0b2b47a4113519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 14 Jul 2021 17:57:35 +0200 Subject: [PATCH 44/98] docs: Replace > with >= in pragmas to work around the highligher being unable to process the former --- docs/abi-spec.rst | 2 +- docs/contracts/function-modifiers.rst | 2 +- docs/contracts/functions.rst | 2 +- docs/control-structures.rst | 2 +- docs/ir/ir-breaking-changes.rst | 14 +++++++------- docs/structure-of-a-contract.rst | 2 +- docs/types/reference-types.rst | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 2b0ffbe8e..8ecbedfe1 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -657,7 +657,7 @@ As an example, the code .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.7.4 <0.9.0; + pragma solidity >=0.7.5 <0.9.0; pragma abicoder v2; contract Test { diff --git a/docs/contracts/function-modifiers.rst b/docs/contracts/function-modifiers.rst index 262d80321..0d4c8e8c2 100644 --- a/docs/contracts/function-modifiers.rst +++ b/docs/contracts/function-modifiers.rst @@ -18,7 +18,7 @@ if they are marked ``virtual``. For details, please see .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.7.0 <0.9.0; + pragma solidity >=0.7.1 <0.9.0; contract owned { constructor() { owner = payable(msg.sender); } diff --git a/docs/contracts/functions.rst b/docs/contracts/functions.rst index 56bc85a05..93283e62c 100644 --- a/docs/contracts/functions.rst +++ b/docs/contracts/functions.rst @@ -15,7 +15,7 @@ that call them, similar to internal library functions. .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.7.0 <0.9.0; + pragma solidity >=0.7.1 <0.9.0; function sum(uint[] memory _arr) pure returns (uint s) { for (uint i = 0; i < _arr.length; i++) diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 9da26ca26..d74a863a7 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -786,7 +786,7 @@ A failure in an external call can be caught using a try/catch statement, as foll .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.8.0; + pragma solidity >=0.8.1; interface DataFeed { function getData(address token) external returns (uint value); } diff --git a/docs/ir/ir-breaking-changes.rst b/docs/ir/ir-breaking-changes.rst index 5c0c08d51..cbc797346 100644 --- a/docs/ir/ir-breaking-changes.rst +++ b/docs/ir/ir-breaking-changes.rst @@ -17,7 +17,7 @@ hiding new and different behavior in existing code. .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.7.0; + pragma solidity >=0.7.1; contract C { struct S { @@ -80,7 +80,7 @@ This causes differences in some contracts, for example: .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.7.0; + pragma solidity >=0.7.1; contract A { uint x; @@ -104,7 +104,7 @@ This causes differences in some contracts, for example: .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.8.0; + pragma solidity >=0.8.1; contract C { bytes x; @@ -135,7 +135,7 @@ This causes differences in some contracts, for example: .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.8.0; + pragma solidity >=0.8.1; contract C { function preincr_u8(uint8 _a) public pure returns (uint8) { return ++_a + _a; @@ -155,7 +155,7 @@ This causes differences in some contracts, for example: .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.8.0; + pragma solidity >=0.8.1; contract C { function add(uint8 _a, uint8 _b) public pure returns (uint8) { return _a + _b; @@ -177,7 +177,7 @@ This causes differences in some contracts, for example: .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.8.0; + pragma solidity >=0.8.1; contract C { function f() public pure returns (uint256 aMod, uint256 mMod) { uint256 x = 3; @@ -228,7 +228,7 @@ For example: .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.8.0; + pragma solidity >=0.8.1; contract C { function f(uint8 _a) public pure returns (uint _r1, uint _r2) { diff --git a/docs/structure-of-a-contract.rst b/docs/structure-of-a-contract.rst index e776c4eca..740cce861 100644 --- a/docs/structure-of-a-contract.rst +++ b/docs/structure-of-a-contract.rst @@ -50,7 +50,7 @@ contracts. .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.7.0 <0.9.0; + pragma solidity >=0.7.1 <0.9.0; contract SimpleAuction { function bid() public payable { // Function diff --git a/docs/types/reference-types.rst b/docs/types/reference-types.rst index 6d5240ec8..975189aa0 100644 --- a/docs/types/reference-types.rst +++ b/docs/types/reference-types.rst @@ -492,7 +492,7 @@ Array slices are useful to ABI-decode secondary data passed in function paramete .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.8.4 <0.9.0; + pragma solidity >=0.8.5 <0.9.0; contract Proxy { /// @dev Address of the client contract managed by proxy i.e., this contract address client; From 693b88e5f8e2af23a3784518c012230c8cf4d463 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 14 Jul 2021 19:32:42 +0200 Subject: [PATCH 45/98] docs: Force highlighter to work despite errors on snippets that it cannot parse --- docs/abi-spec.rst | 1 + docs/common-patterns.rst | 2 ++ docs/contracts/libraries.rst | 1 + docs/control-structures.rst | 1 + docs/examples/blind-auction.rst | 1 + docs/ir/ir-breaking-changes.rst | 1 + docs/style-guide.rst | 2 ++ docs/types/mapping-types.rst | 1 + docs/types/value-types.rst | 3 +++ docs/units-and-global-variables.rst | 1 + 10 files changed, 14 insertions(+) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 8ecbedfe1..1b61351fa 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -237,6 +237,7 @@ Examples Given the contract: .. code-block:: solidity + :force: // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.4.16 <0.9.0; diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst index 58fb39ea2..86c81dbfb 100644 --- a/docs/common-patterns.rst +++ b/docs/common-patterns.rst @@ -130,6 +130,7 @@ The use of **function modifiers** makes these restrictions highly readable. .. code-block:: solidity + :force: // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.4; @@ -293,6 +294,7 @@ function finishes. will run even if the function explicitly returns. .. code-block:: solidity + :force: // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.4; diff --git a/docs/contracts/libraries.rst b/docs/contracts/libraries.rst index a6a062501..dbb36f317 100644 --- a/docs/contracts/libraries.rst +++ b/docs/contracts/libraries.rst @@ -130,6 +130,7 @@ internal functions in libraries in order to implement custom types without the overhead of external function calls: .. code-block:: solidity + :force: // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.6.8 <0.9.0; diff --git a/docs/control-structures.rst b/docs/control-structures.rst index d74a863a7..95a55f6c6 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -660,6 +660,7 @@ The following example shows how you can use ``require`` to check conditions on i and ``assert`` for internal error checking. .. code-block:: solidity + :force: // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.5.0 <0.9.0; diff --git a/docs/examples/blind-auction.rst b/docs/examples/blind-auction.rst index ce72cdf18..c00bbf212 100644 --- a/docs/examples/blind-auction.rst +++ b/docs/examples/blind-auction.rst @@ -192,6 +192,7 @@ invalid bids. .. code-block:: solidity + :force: // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.4; diff --git a/docs/ir/ir-breaking-changes.rst b/docs/ir/ir-breaking-changes.rst index cbc797346..5bd6c8bdf 100644 --- a/docs/ir/ir-breaking-changes.rst +++ b/docs/ir/ir-breaking-changes.rst @@ -226,6 +226,7 @@ The new code generator performs cleanup after any operation that can result in d For example: .. code-block:: solidity + :force: // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.1; diff --git a/docs/style-guide.rst b/docs/style-guide.rst index e05f9349a..9e0fba5f6 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -998,6 +998,7 @@ No: Yes: .. code-block:: solidity + :force: x = 3; x = 100 / 10; @@ -1007,6 +1008,7 @@ Yes: No: .. code-block:: solidity + :force: x=3; x = 100/10; diff --git a/docs/types/mapping-types.rst b/docs/types/mapping-types.rst index 29a00a196..c026d73f2 100644 --- a/docs/types/mapping-types.rst +++ b/docs/types/mapping-types.rst @@ -122,6 +122,7 @@ top of them and iterate over that. For example, the code below implements an the ``sum`` function iterates over to sum all the values. .. code-block:: solidity + :force: // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.6.8 <0.9.0; diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index 5df4557a7..008d31683 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -239,6 +239,7 @@ It is possible to query the balance of an address using the property ``balance`` and to send Ether (in units of wei) to a payable address using the ``transfer`` function: .. code-block:: solidity + :force: address payable x = address(0x123); address myAddress = address(this); @@ -535,6 +536,7 @@ quote a backslash character and then (without separator) the character sequence ``abcdef``. .. code-block:: solidity + :force: "\n\"\'\\abc\ def" @@ -641,6 +643,7 @@ be passed via and returned from external function calls. Function types are notated as follows: .. code-block:: solidity + :force: function () {internal|external} [pure|view|payable] [returns ()] diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index df79d1b13..0e2ce34a2 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -10,6 +10,7 @@ Ether Units A literal number can take a suffix of ``wei``, ``gwei`` or ``ether`` to specify a subdenomination of Ether, where Ether numbers without a postfix are assumed to be Wei. .. code-block:: solidity + :force: assert(1 wei == 1); assert(1 gwei == 1e9); From 0f7a185ee3cd6fc5b2fb5047ec32ee47d8ceb1ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 14 Jul 2021 19:10:26 +0200 Subject: [PATCH 46/98] scripts/docs.sh: Install latest versions of packages when available --- scripts/docs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/docs.sh b/scripts/docs.sh index e2493511b..294058bca 100755 --- a/scripts/docs.sh +++ b/scripts/docs.sh @@ -29,6 +29,6 @@ set -e scripts/update_robotstxt.sh cd docs -pip3 install -r requirements.txt +pip3 install -r requirements.txt --upgrade --upgrade-strategy eager sphinx-build -nW -b html -d _build/doctrees . _build/html cd .. From d4a86b2b10fc4960448a46c3ae62f1ef84b73ba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 15 Jul 2021 13:10:04 +0200 Subject: [PATCH 47/98] docs/conf.py: Remove unused /utils directory from python path --- docs/conf.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 4c7a67164..a16d0624c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -24,8 +24,6 @@ from pygments_lexer_solidity import SolidityLexer, YulLexer # documentation root, use os.path.abspath to make it absolute, like shown here. def setup(sphinx): - thisdir = os.path.dirname(os.path.realpath(__file__)) - sys.path.insert(0, thisdir + '/utils') sphinx.add_lexer('Solidity', SolidityLexer) sphinx.add_lexer('Yul', YulLexer) From 57db4c82ba0192c56bd246593caa7a38ab289afd Mon Sep 17 00:00:00 2001 From: Marenz Date: Thu, 15 Jul 2021 12:31:48 +0200 Subject: [PATCH 48/98] Fix: Use correct byte offset separation ":" instead of "," --- libsolidity/codegen/ir/Common.cpp | 2 +- test/cmdlineTests/exp_base_literal/output | 98 +++++++------- test/cmdlineTests/revert_strings/output | 8 +- .../standard_ir_requested/output.json | 8 +- .../standard_viair_requested/output.json | 24 ++-- test/cmdlineTests/viair_abicoder_v1/output | 14 +- .../yul_source_locations/output.json | 124 +++++++++--------- .../yul_string_format_ascii/output.json | 12 +- .../output.json | 12 +- .../output.json | 14 +- .../yul_string_format_ascii_long/output.json | 12 +- .../yul_string_format_hex/output.json | 14 +- 12 files changed, 171 insertions(+), 171 deletions(-) diff --git a/libsolidity/codegen/ir/Common.cpp b/libsolidity/codegen/ir/Common.cpp index 8906fa97e..e9bbaa4e0 100644 --- a/libsolidity/codegen/ir/Common.cpp +++ b/libsolidity/codegen/ir/Common.cpp @@ -134,7 +134,7 @@ string sourceLocationComment(langutil::SourceLocation const& _location, IRGenera + to_string(_context.sourceIndices().at(*_location.sourceName)) + ":" + to_string(_location.start) - + "," + + ":" + to_string(_location.end); } diff --git a/test/cmdlineTests/exp_base_literal/output b/test/cmdlineTests/exp_base_literal/output index eaf776545..479107cf0 100644 --- a/test/cmdlineTests/exp_base_literal/output +++ b/test/cmdlineTests/exp_base_literal/output @@ -9,7 +9,7 @@ IR: object "C_81" { code { - /// @src 0:82,370 + /// @src 0:82:370 mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -26,7 +26,7 @@ object "C_81" { function constructor_C_81() { - /// @src 0:82,370 + /// @src 0:82:370 } @@ -37,7 +37,7 @@ object "C_81" { } object "C_81_deployed" { code { - /// @src 0:82,370 + /// @src 0:82:370 mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -212,122 +212,122 @@ object "C_81" { } function fun_f_80(var_a_4, var_b_6, var_c_8, var_d_10) -> var__13, var__15, var__17, var__19 { - /// @src 0:96,368 - /// @src 0:160,164 + /// @src 0:96:368 + /// @src 0:160:164 let zero_t_uint256_1 := zero_value_for_split_t_uint256() var__13 := zero_t_uint256_1 - /// @src 0:166,169 + /// @src 0:166:169 let zero_t_int256_2 := zero_value_for_split_t_int256() var__15 := zero_t_int256_2 - /// @src 0:171,175 + /// @src 0:171:175 let zero_t_uint256_3 := zero_value_for_split_t_uint256() var__17 := zero_t_uint256_3 - /// @src 0:177,181 + /// @src 0:177:181 let zero_t_uint256_4 := zero_value_for_split_t_uint256() var__19 := zero_t_uint256_4 - /// @src 0:196,197 + /// @src 0:196:197 let expr_23 := 0x02 - /// @src 0:199,200 + /// @src 0:199:200 let _5 := var_a_4 let expr_24 := _5 - /// @src 0:196,200 + /// @src 0:196:200 let _6 := convert_t_rational_2_by_1_to_t_uint256(expr_23) let expr_25 := checked_exp_t_rational_2_by_1_t_uint256(expr_24) - /// @src 0:187,200 + /// @src 0:187:200 let var_w_22 := expr_25 - /// @src 0:213,215 + /// @src 0:213:215 let expr_30 := 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe - /// @src 0:212,216 + /// @src 0:212:216 let expr_31 := expr_30 - /// @src 0:218,219 + /// @src 0:218:219 let _7 := var_b_6 let expr_32 := _7 - /// @src 0:212,219 + /// @src 0:212:219 let _8 := convert_t_rational_minus_2_by_1_to_t_int256(expr_31) let expr_33 := checked_exp_t_rational_minus_2_by_1_t_uint256(expr_32) - /// @src 0:204,219 + /// @src 0:204:219 let var_x_28 := expr_33 - /// @src 0:232,234 + /// @src 0:232:234 let expr_37 := 0x0a - /// @src 0:236,237 + /// @src 0:236:237 let _9 := var_c_8 let expr_38 := _9 - /// @src 0:232,237 + /// @src 0:232:237 let _10 := convert_t_rational_10_by_1_to_t_uint256(expr_37) let expr_39 := checked_exp_t_rational_10_by_1_t_uint256(expr_38) - /// @src 0:223,237 + /// @src 0:223:237 let var_y_36 := expr_39 - /// @src 0:251,260 + /// @src 0:251:260 let expr_47 := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - /// @src 0:250,262 + /// @src 0:250:262 let expr_48 := expr_47 - /// @src 0:264,265 + /// @src 0:264:265 let _11 := var_d_10 let expr_49 := _11 - /// @src 0:250,265 + /// @src 0:250:265 let _12 := convert_t_rational_115792089237316195423570985008687907853269984665640564039457584007913129639935_by_1_to_t_uint256(expr_48) let expr_50 := checked_exp_t_rational_115792089237316195423570985008687907853269984665640564039457584007913129639935_by_1_t_uint256(expr_49) - /// @src 0:241,265 + /// @src 0:241:265 let var_z_42 := expr_50 - /// @src 0:308,309 + /// @src 0:308:309 let expr_53 := 0x00 - /// @src 0:307,310 + /// @src 0:307:310 let expr_54 := expr_53 - /// @src 0:312,313 + /// @src 0:312:313 let _13 := var_a_4 let expr_55 := _13 - /// @src 0:307,313 + /// @src 0:307:313 let _14 := convert_t_rational_0_by_1_to_t_uint256(expr_54) let expr_56 := checked_exp_t_rational_0_by_1_t_uint256(expr_55) - /// @src 0:303,313 + /// @src 0:303:313 var_w_22 := expr_56 let expr_57 := expr_56 - /// @src 0:322,324 + /// @src 0:322:324 let expr_61 := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - /// @src 0:321,325 + /// @src 0:321:325 let expr_62 := expr_61 - /// @src 0:327,328 + /// @src 0:327:328 let _15 := var_b_6 let expr_63 := _15 - /// @src 0:321,328 + /// @src 0:321:328 let _16 := convert_t_rational_minus_1_by_1_to_t_int256(expr_62) let expr_64 := checked_exp_t_rational_minus_1_by_1_t_uint256(expr_63) - /// @src 0:317,328 + /// @src 0:317:328 var_x_28 := expr_64 let expr_65 := expr_64 - /// @src 0:336,337 + /// @src 0:336:337 let expr_68 := 0x01 - /// @src 0:339,340 + /// @src 0:339:340 let _17 := var_c_8 let expr_69 := _17 - /// @src 0:336,340 + /// @src 0:336:340 let _18 := convert_t_rational_1_by_1_to_t_uint256(expr_68) let expr_70 := checked_exp_t_rational_1_by_1_t_uint256(expr_69) - /// @src 0:332,340 + /// @src 0:332:340 var_y_36 := expr_70 let expr_71 := expr_70 - /// @src 0:353,354 + /// @src 0:353:354 let _19 := var_w_22 let expr_73 := _19 - /// @src 0:352,364 + /// @src 0:352:364 let expr_77_component_1 := expr_73 - /// @src 0:356,357 + /// @src 0:356:357 let _20 := var_x_28 let expr_74 := _20 - /// @src 0:352,364 + /// @src 0:352:364 let expr_77_component_2 := expr_74 - /// @src 0:359,360 + /// @src 0:359:360 let _21 := var_y_36 let expr_75 := _21 - /// @src 0:352,364 + /// @src 0:352:364 let expr_77_component_3 := expr_75 - /// @src 0:362,363 + /// @src 0:362:363 let _22 := var_z_42 let expr_76 := _22 - /// @src 0:352,364 + /// @src 0:352:364 let expr_77_component_4 := expr_76 - /// @src 0:345,364 + /// @src 0:345:364 var__13 := expr_77_component_1 var__15 := expr_77_component_2 var__17 := expr_77_component_3 diff --git a/test/cmdlineTests/revert_strings/output b/test/cmdlineTests/revert_strings/output index 5ce644d53..fb4993268 100644 --- a/test/cmdlineTests/revert_strings/output +++ b/test/cmdlineTests/revert_strings/output @@ -9,7 +9,7 @@ IR: object "C_15" { code { - /// @src 0:59,147 + /// @src 0:59:147 mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -26,7 +26,7 @@ object "C_15" { function constructor_C_15() { - /// @src 0:59,147 + /// @src 0:59:147 } @@ -52,7 +52,7 @@ object "C_15" { } object "C_15_deployed" { code { - /// @src 0:59,147 + /// @src 0:59:147 mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -217,7 +217,7 @@ object "C_15" { } function fun_f_14(var__7_mpos, var_e_10) { - /// @src 0:93,145 + /// @src 0:93:145 } diff --git a/test/cmdlineTests/standard_ir_requested/output.json b/test/cmdlineTests/standard_ir_requested/output.json index 0d61f8cca..9165ea7ef 100644 --- a/test/cmdlineTests/standard_ir_requested/output.json +++ b/test/cmdlineTests/standard_ir_requested/output.json @@ -8,7 +8,7 @@ object \"C_7\" { code { - /// @src 0:79,121 + /// @src 0:79:121 mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -25,7 +25,7 @@ object \"C_7\" { function constructor_C_7() { - /// @src 0:79,121 + /// @src 0:79:121 } @@ -36,7 +36,7 @@ object \"C_7\" { } object \"C_7_deployed\" { code { - /// @src 0:79,121 + /// @src 0:79:121 mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -76,7 +76,7 @@ object \"C_7\" { } function fun_f_6() { - /// @src 0:92,119 + /// @src 0:92:119 } diff --git a/test/cmdlineTests/standard_viair_requested/output.json b/test/cmdlineTests/standard_viair_requested/output.json index 4f2399134..86849448f 100644 --- a/test/cmdlineTests/standard_viair_requested/output.json +++ b/test/cmdlineTests/standard_viair_requested/output.json @@ -8,7 +8,7 @@ object \"C_3\" { code { - /// @src 0:79,92 + /// @src 0:79:92 mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -25,7 +25,7 @@ object \"C_3\" { function constructor_C_3() { - /// @src 0:79,92 + /// @src 0:79:92 } @@ -36,7 +36,7 @@ object \"C_3\" { } object \"C_3_deployed\" { code { - /// @src 0:79,92 + /// @src 0:79:92 mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -81,7 +81,7 @@ object \"C_3\" { object \"D_16\" { code { - /// @src 0:93,146 + /// @src 0:93:146 mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -98,7 +98,7 @@ object \"D_16\" { function constructor_D_16() { - /// @src 0:93,146 + /// @src 0:93:146 } @@ -109,7 +109,7 @@ object \"D_16\" { } object \"D_16_deployed\" { code { - /// @src 0:93,146 + /// @src 0:93:146 mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -149,9 +149,9 @@ object \"D_16\" { } function fun_f_15() { - /// @src 0:106,144 + /// @src 0:106:144 - /// @src 0:134,141 + /// @src 0:134:141 let _1 := allocate_unbounded() let _2 := add(_1, datasize(\"C_3\")) if or(gt(_2, 0xffffffffffffffff), lt(_2, _1)) { panic_error_0x41() } @@ -162,7 +162,7 @@ object \"D_16\" { if iszero(expr_12_address) { revert_forward_1() } - /// @src 0:128,141 + /// @src 0:128:141 let var_c_8_address := expr_12_address } @@ -208,7 +208,7 @@ object \"D_16\" { object \"C_3\" { code { - /// @src 0:79,92 + /// @src 0:79:92 mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -225,7 +225,7 @@ object \"D_16\" { function constructor_C_3() { - /// @src 0:79,92 + /// @src 0:79:92 } @@ -236,7 +236,7 @@ object \"D_16\" { } object \"C_3_deployed\" { code { - /// @src 0:79,92 + /// @src 0:79:92 mstore(64, 128) if iszero(lt(calldatasize(), 4)) diff --git a/test/cmdlineTests/viair_abicoder_v1/output b/test/cmdlineTests/viair_abicoder_v1/output index d16d77f2d..1aafd5e56 100644 --- a/test/cmdlineTests/viair_abicoder_v1/output +++ b/test/cmdlineTests/viair_abicoder_v1/output @@ -9,7 +9,7 @@ IR: object "test_11" { code { - /// @src 0:79,169 + /// @src 0:79:169 mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -26,7 +26,7 @@ object "test_11" { function constructor_test_11() { - /// @src 0:79,169 + /// @src 0:79:169 } @@ -37,7 +37,7 @@ object "test_11" { } object "test_11_deployed" { code { - /// @src 0:79,169 + /// @src 0:79:169 mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -87,14 +87,14 @@ object "test_11" { } function fun_f_10() -> var__5 { - /// @src 0:99,167 - /// @src 0:133,137 + /// @src 0:99:167 + /// @src 0:133:137 let zero_t_bool_1 := zero_value_for_split_t_bool() var__5 := zero_t_bool_1 - /// @src 0:156,160 + /// @src 0:156:160 let expr_7 := 0x01 - /// @src 0:149,160 + /// @src 0:149:160 var__5 := expr_7 leave diff --git a/test/cmdlineTests/yul_source_locations/output.json b/test/cmdlineTests/yul_source_locations/output.json index 19f8b3714..7ba5b499a 100644 --- a/test/cmdlineTests/yul_source_locations/output.json +++ b/test/cmdlineTests/yul_source_locations/output.json @@ -8,7 +8,7 @@ object \"C_54\" { code { - /// @src 0:79,428 + /// @src 0:79:428 mstore(64, 160) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -54,17 +54,17 @@ object \"C_54\" { function constructor_C_54(var__init_12) { - /// @src 0:175,223 + /// @src 0:175:223 - /// @src 0:147,149 + /// @src 0:147:149 let expr_7 := 0x2a let _3 := convert_t_rational_42_by_1_to_t_int256(expr_7) mstore(128, _3) - /// @src 0:214,219 + /// @src 0:214:219 let _4 := var__init_12 let expr_16 := _4 - /// @src 0:203,219 + /// @src 0:203:219 update_storage_value_offset_0t_int256_to_t_int256(0x00, expr_16) let expr_17 := expr_16 @@ -147,7 +147,7 @@ object \"C_54\" { } object \"C_54_deployed\" { code { - /// @src 0:79,428 + /// @src 0:79:428 mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -262,9 +262,9 @@ object \"C_54\" { cleaned := and(value, 0xffffffffffffffffffffffffffffffffffffffff) } - /// @src 0:93,119 + /// @src 0:93:119 function constant_constVar_5() -> ret { - /// @src 0:117,119 + /// @src 0:117:119 let expr_4 := 0x29 let _2 := convert_t_rational_41_by_1_to_t_int256(expr_4) @@ -303,8 +303,8 @@ object \"C_54\" { } function fun_f2_53() -> var__42 { - /// @src 0:343,426 - /// @src 0:375,378 + /// @src 0:343:426 + /// @src 0:375:378 let zero_t_int256_4 := zero_value_for_split_t_int256() var__42 := zero_t_int256_4 @@ -312,18 +312,18 @@ object \"C_54\" { } function fun_f2_53_inner(_8) -> var__42 { - /// @src 0:343,426 + /// @src 0:343:426 var__42 := _8 - /// @src 0:392,400 + /// @src 0:392:400 let _9 := read_from_storage_split_offset_0_t_int256(0x00) let expr_44 := _9 - /// @src 0:403,407 + /// @src 0:403:407 let expr_45_address := address() - /// @src 0:403,409 + /// @src 0:403:409 let expr_46_address := convert_t_contract$_C_$54_to_t_address(expr_45_address) let expr_46_functionSelector := 0x26121ff0 - /// @src 0:403,411 + /// @src 0:403:411 if iszero(extcodesize(expr_46_address)) { revert_error_0cc013b6b3b6beabea4e3a74a6d380f0df81852ca99887912475e1f66b2a2c20() } // storage for arguments and returned data @@ -344,43 +344,43 @@ object \"C_54\" { // decode return parameters from external try-call into retVars expr_47 := abi_decode_tuple_t_int256_fromMemory(_10, add(_10, returndatasize())) } - /// @src 0:392,411 + /// @src 0:392:411 let expr_48 := checked_add_t_int256(expr_44, expr_47) - /// @src 0:414,422 + /// @src 0:414:422 let _13 := loadimmutable(\"8\") let expr_49 := _13 - /// @src 0:392,422 + /// @src 0:392:422 let expr_50 := checked_add_t_int256(expr_48, expr_49) - /// @src 0:385,422 + /// @src 0:385:422 var__42 := expr_50 leave } function fun_f_30() -> var__23 { - /// @src 0:226,302 - /// @src 0:262,265 + /// @src 0:226:302 + /// @src 0:262:265 let zero_t_int256_1 := zero_value_for_split_t_int256() var__23 := zero_t_int256_1 - /// @src 0:279,287 + /// @src 0:279:287 let expr_25 := constant_constVar_5() - /// @src 0:290,298 + /// @src 0:290:298 let _3 := loadimmutable(\"8\") let expr_26 := _3 - /// @src 0:279,298 + /// @src 0:279:298 let expr_27 := checked_add_t_int256(expr_25, expr_26) - /// @src 0:272,298 + /// @src 0:272:298 var__23 := expr_27 leave } function getter_fun_stateVar_10() -> ret { - /// @src 0:152,171 + /// @src 0:152:171 let slot := 0 let offset := 0 @@ -396,15 +396,15 @@ object \"C_54\" { } function modifier_m_40(var__42) -> _5 { - /// @src 0:304,341 + /// @src 0:304:341 _5 := var__42 - /// @src 0:322,332 + /// @src 0:322:332 let _7 := read_from_storage_split_offset_0_t_int256(0x00) let _6 := increment_t_int256(_7) update_storage_value_offset_0t_int256_to_t_int256(0x00, _6) let expr_33 := _7 - /// @src 0:336,337 + /// @src 0:336:337 _5 := fun_f2_53_inner(var__42) } @@ -537,7 +537,7 @@ object \"C_54\" { object \"D_72\" { code { - /// @src 1:91,166 + /// @src 1:91:166 mstore(64, 160) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -599,34 +599,34 @@ object \"D_72\" { function constructor_C_54(var__init_12) { - /// @src 0:175,223 + /// @src 0:175:223 - /// @src 0:147,149 + /// @src 0:147:149 let expr_7 := 0x2a let _6 := convert_t_rational_42_by_1_to_t_int256(expr_7) mstore(128, _6) - /// @src 0:214,219 + /// @src 0:214:219 let _7 := var__init_12 let expr_16 := _7 - /// @src 0:203,219 + /// @src 0:203:219 update_storage_value_offset_0t_int256_to_t_int256(0x00, expr_16) let expr_17 := expr_16 } function constructor_D_72(var__init2_63) { - /// @src 1:107,108 + /// @src 1:107:108 let expr_60 := 0x03 let _3 := convert_t_rational_3_by_1_to_t_int256(expr_60) - /// @src 1:113,164 + /// @src 1:113:164 constructor_C_54(_3) - /// @src 1:154,160 + /// @src 1:154:160 let _4 := var__init2_63 let expr_67 := _4 - /// @src 1:142,160 + /// @src 1:142:160 let _5 := read_from_storage_split_offset_0_t_int256(0x00) let expr_68 := checked_add_t_int256(_5, expr_67) @@ -737,7 +737,7 @@ object \"D_72\" { } object \"D_72_deployed\" { code { - /// @src 1:91,166 + /// @src 1:91:166 mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -852,9 +852,9 @@ object \"D_72\" { cleaned := and(value, 0xffffffffffffffffffffffffffffffffffffffff) } - /// @src 0:93,119 + /// @src 0:93:119 function constant_constVar_5() -> ret { - /// @src 0:117,119 + /// @src 0:117:119 let expr_4 := 0x29 let _2 := convert_t_rational_41_by_1_to_t_int256(expr_4) @@ -893,8 +893,8 @@ object \"D_72\" { } function fun_f2_53() -> var__42 { - /// @src 0:343,426 - /// @src 0:375,378 + /// @src 0:343:426 + /// @src 0:375:378 let zero_t_int256_4 := zero_value_for_split_t_int256() var__42 := zero_t_int256_4 @@ -902,18 +902,18 @@ object \"D_72\" { } function fun_f2_53_inner(_8) -> var__42 { - /// @src 0:343,426 + /// @src 0:343:426 var__42 := _8 - /// @src 0:392,400 + /// @src 0:392:400 let _9 := read_from_storage_split_offset_0_t_int256(0x00) let expr_44 := _9 - /// @src 0:403,407 + /// @src 0:403:407 let expr_45_address := address() - /// @src 0:403,409 + /// @src 0:403:409 let expr_46_address := convert_t_contract$_C_$54_to_t_address(expr_45_address) let expr_46_functionSelector := 0x26121ff0 - /// @src 0:403,411 + /// @src 0:403:411 if iszero(extcodesize(expr_46_address)) { revert_error_0cc013b6b3b6beabea4e3a74a6d380f0df81852ca99887912475e1f66b2a2c20() } // storage for arguments and returned data @@ -934,43 +934,43 @@ object \"D_72\" { // decode return parameters from external try-call into retVars expr_47 := abi_decode_tuple_t_int256_fromMemory(_10, add(_10, returndatasize())) } - /// @src 0:392,411 + /// @src 0:392:411 let expr_48 := checked_add_t_int256(expr_44, expr_47) - /// @src 0:414,422 + /// @src 0:414:422 let _13 := loadimmutable(\"8\") let expr_49 := _13 - /// @src 0:392,422 + /// @src 0:392:422 let expr_50 := checked_add_t_int256(expr_48, expr_49) - /// @src 0:385,422 + /// @src 0:385:422 var__42 := expr_50 leave } function fun_f_30() -> var__23 { - /// @src 0:226,302 - /// @src 0:262,265 + /// @src 0:226:302 + /// @src 0:262:265 let zero_t_int256_1 := zero_value_for_split_t_int256() var__23 := zero_t_int256_1 - /// @src 0:279,287 + /// @src 0:279:287 let expr_25 := constant_constVar_5() - /// @src 0:290,298 + /// @src 0:290:298 let _3 := loadimmutable(\"8\") let expr_26 := _3 - /// @src 0:279,298 + /// @src 0:279:298 let expr_27 := checked_add_t_int256(expr_25, expr_26) - /// @src 0:272,298 + /// @src 0:272:298 var__23 := expr_27 leave } function getter_fun_stateVar_10() -> ret { - /// @src 0:152,171 + /// @src 0:152:171 let slot := 0 let offset := 0 @@ -986,15 +986,15 @@ object \"D_72\" { } function modifier_m_40(var__42) -> _5 { - /// @src 0:304,341 + /// @src 0:304:341 _5 := var__42 - /// @src 0:322,332 + /// @src 0:322:332 let _7 := read_from_storage_split_offset_0_t_int256(0x00) let _6 := increment_t_int256(_7) update_storage_value_offset_0t_int256_to_t_int256(0x00, _6) let expr_33 := _7 - /// @src 0:336,337 + /// @src 0:336:337 _5 := fun_f2_53_inner(var__42) } diff --git a/test/cmdlineTests/yul_string_format_ascii/output.json b/test/cmdlineTests/yul_string_format_ascii/output.json index df96ed791..80849e805 100644 --- a/test/cmdlineTests/yul_string_format_ascii/output.json +++ b/test/cmdlineTests/yul_string_format_ascii/output.json @@ -8,7 +8,7 @@ object \"C_11\" { code { - /// @src 0:78,164 + /// @src 0:78:164 mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -25,7 +25,7 @@ object \"C_11\" { function constructor_C_11() { - /// @src 0:78,164 + /// @src 0:78:164 } @@ -36,7 +36,7 @@ object \"C_11\" { } object \"C_11_deployed\" { code { - /// @src 0:78,164 + /// @src 0:78:164 mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -150,12 +150,12 @@ object \"C_11\" { } function fun_f_10() -> var__5_mpos { - /// @src 0:91,162 - /// @src 0:127,140 + /// @src 0:91:162 + /// @src 0:127:140 let zero_t_string_memory_ptr_1_mpos := zero_value_for_split_t_string_memory_ptr() var__5_mpos := zero_t_string_memory_ptr_1_mpos - /// @src 0:144,159 + /// @src 0:144:159 var__5_mpos := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr() leave diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json index 9e47d6e1e..daf27fbe7 100644 --- a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json @@ -8,7 +8,7 @@ object \"C_11\" { code { - /// @src 0:78,158 + /// @src 0:78:158 mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -25,7 +25,7 @@ object \"C_11\" { function constructor_C_11() { - /// @src 0:78,158 + /// @src 0:78:158 } @@ -36,7 +36,7 @@ object \"C_11\" { } object \"C_11_deployed\" { code { - /// @src 0:78,158 + /// @src 0:78:158 mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -90,12 +90,12 @@ object \"C_11\" { } function fun_f_10() -> var__5 { - /// @src 0:91,156 - /// @src 0:127,134 + /// @src 0:91:156 + /// @src 0:127:134 let zero_t_bytes32_1 := zero_value_for_split_t_bytes32() var__5 := zero_t_bytes32_1 - /// @src 0:138,153 + /// @src 0:138:153 var__5 := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_bytes32() leave diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json index 8c5df7044..0ef2438be 100644 --- a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json @@ -8,7 +8,7 @@ object \"C_11\" { code { - /// @src 0:78,159 + /// @src 0:78:159 mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -25,7 +25,7 @@ object \"C_11\" { function constructor_C_11() { - /// @src 0:78,159 + /// @src 0:78:159 } @@ -36,7 +36,7 @@ object \"C_11\" { } object \"C_11_deployed\" { code { - /// @src 0:78,159 + /// @src 0:78:159 mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -94,14 +94,14 @@ object \"C_11\" { } function fun_f_10() -> var__5 { - /// @src 0:91,157 - /// @src 0:127,133 + /// @src 0:91:157 + /// @src 0:127:133 let zero_t_bytes4_1 := zero_value_for_split_t_bytes4() var__5 := zero_t_bytes4_1 - /// @src 0:144,154 + /// @src 0:144:154 let expr_7 := 0x61626364 - /// @src 0:137,154 + /// @src 0:137:154 var__5 := convert_t_rational_1633837924_by_1_to_t_bytes4(expr_7) leave diff --git a/test/cmdlineTests/yul_string_format_ascii_long/output.json b/test/cmdlineTests/yul_string_format_ascii_long/output.json index ad2197252..0628e4dce 100644 --- a/test/cmdlineTests/yul_string_format_ascii_long/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_long/output.json @@ -8,7 +8,7 @@ object \"C_11\" { code { - /// @src 0:78,243 + /// @src 0:78:243 mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -25,7 +25,7 @@ object \"C_11\" { function constructor_C_11() { - /// @src 0:78,243 + /// @src 0:78:243 } @@ -36,7 +36,7 @@ object \"C_11\" { } object \"C_11_deployed\" { code { - /// @src 0:78,243 + /// @src 0:78:243 mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -150,12 +150,12 @@ object \"C_11\" { } function fun_f_10() -> var__5_mpos { - /// @src 0:91,241 - /// @src 0:127,140 + /// @src 0:91:241 + /// @src 0:127:140 let zero_t_string_memory_ptr_1_mpos := zero_value_for_split_t_string_memory_ptr() var__5_mpos := zero_t_string_memory_ptr_1_mpos - /// @src 0:144,238 + /// @src 0:144:238 var__5_mpos := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr() leave diff --git a/test/cmdlineTests/yul_string_format_hex/output.json b/test/cmdlineTests/yul_string_format_hex/output.json index 86eb7ed62..94fa33c38 100644 --- a/test/cmdlineTests/yul_string_format_hex/output.json +++ b/test/cmdlineTests/yul_string_format_hex/output.json @@ -8,7 +8,7 @@ object \"C_11\" { code { - /// @src 0:78,159 + /// @src 0:78:159 mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -25,7 +25,7 @@ object \"C_11\" { function constructor_C_11() { - /// @src 0:78,159 + /// @src 0:78:159 } @@ -36,7 +36,7 @@ object \"C_11\" { } object \"C_11_deployed\" { code { - /// @src 0:78,159 + /// @src 0:78:159 mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -94,14 +94,14 @@ object \"C_11\" { } function fun_f_10() -> var__5 { - /// @src 0:91,157 - /// @src 0:127,133 + /// @src 0:91:157 + /// @src 0:127:133 let zero_t_bytes4_1 := zero_value_for_split_t_bytes4() var__5 := zero_t_bytes4_1 - /// @src 0:144,154 + /// @src 0:144:154 let expr_7 := 0xaabbccdd - /// @src 0:137,154 + /// @src 0:137:154 var__5 := convert_t_rational_2864434397_by_1_to_t_bytes4(expr_7) leave From 2bebc2f8223c1ff9c0b4543be760808ecbc40cea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 15 Jul 2021 13:12:03 +0200 Subject: [PATCH 49/98] Create a local html_extra_template_renderer Sphinx extension --- docs/conf.py | 18 +++++++++- docs/ext/html_extra_template_renderer.py | 45 ++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 docs/ext/html_extra_template_renderer.py diff --git a/docs/conf.py b/docs/conf.py index a16d0624c..01f0d1d3b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -23,6 +23,10 @@ from pygments_lexer_solidity import SolidityLexer, YulLexer # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. +ROOT_PATH = os.path.dirname(os.path.realpath(__file__)) + +sys.path.insert(0, os.path.join(ROOT_PATH, 'ext')) + def setup(sphinx): sphinx.add_lexer('Solidity', SolidityLexer) sphinx.add_lexer('Yul', YulLexer) @@ -37,7 +41,10 @@ def setup(sphinx): # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = [ 'sphinx_a4doc' ] +extensions = [ + 'sphinx_a4doc', + 'html_extra_template_renderer', +] a4_base_path = os.path.dirname(__file__) + '/grammar' @@ -156,6 +163,15 @@ html_js_files = ["js/toggle.js"] # directly to the root of the documentation. html_extra_path = ["_static/css", "_static/robots.txt"] +# List of templates of static files to be included in the HTML output. +# Keys represent paths to input files and values are dicts containing: +# - target: The path where the rendered template should be placed. +# - context: A dictionary listing variables that can be used inside the template. +# All paths must be absolute. +# Rendered templates are automatically added to html_extra_path setting. +html_extra_templates = { +} + # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' diff --git a/docs/ext/html_extra_template_renderer.py b/docs/ext/html_extra_template_renderer.py new file mode 100644 index 000000000..a48f20e5e --- /dev/null +++ b/docs/ext/html_extra_template_renderer.py @@ -0,0 +1,45 @@ +import os.path + + +def render_html_extra_templates(app): + if app.builder.format != 'html': + # Non-HTML builders do not provide .templates.render_string(). Note that a HTML + # builder is still used also when building some other formats like json or epub. + return + + for input_path, template_config in app.config.html_extra_templates.items(): + # Requiring absolute paths simplifies the implementation. + if not os.path.isabs(input_path): + raise RuntimeError(f"Template input path is not absolute: {input_path}") + if not os.path.isabs(template_config['target']): + raise RuntimeError(f"Template target path is not absolute: {template_config['target']}") + + with open(input_path, 'r') as input_file: + # This runs Jinja2, which supports rendering {{ }} tags among other things. + rendered_template = app.builder.templates.render_string( + input_file.read(), + template_config['context'], + ) + + with open(template_config['target'], 'w') as target_file: + target_file.write(rendered_template) + + app.config.html_extra_path.append(template_config['target']) + + +def setup(app): + app.add_config_value('html_extra_templates', default={}, rebuild='', types=dict) + + # Register a handler for the env-before-read-docs event. Any event that's fired before static + # files get copied would do. + app.connect( + 'env-before-read-docs', + lambda app, env, docnames: render_html_extra_templates(app) + ) + + return { + # NOTE: Need to access _raw_config here because setup() runs before app.config is ready. + 'version': app.config._raw_config['version'], # pylint: disable=protected-access + 'parallel_read_safe': True, + 'parallel_write_safe': True, + } From 8c5ae508f91d87648ae7f01d52f48bce4269cc22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 15 Jul 2021 13:12:59 +0200 Subject: [PATCH 50/98] Use html_extra_template_renderer to replace the current robots.txt rendering script --- docs/conf.py | 6 +++++- scripts/docs.sh | 1 - scripts/update_robotstxt.sh | 27 --------------------------- 3 files changed, 5 insertions(+), 29 deletions(-) delete mode 100755 scripts/update_robotstxt.sh diff --git a/docs/conf.py b/docs/conf.py index 01f0d1d3b..573da2465 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -161,7 +161,7 @@ html_js_files = ["js/toggle.js"] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. -html_extra_path = ["_static/css", "_static/robots.txt"] +html_extra_path = ["_static/css"] # List of templates of static files to be included in the HTML output. # Keys represent paths to input files and values are dicts containing: @@ -170,6 +170,10 @@ html_extra_path = ["_static/css", "_static/robots.txt"] # All paths must be absolute. # Rendered templates are automatically added to html_extra_path setting. html_extra_templates = { + os.path.join(ROOT_PATH, "robots.txt.template"): { + 'target': os.path.join(ROOT_PATH, "_static/robots.txt"), + 'context': {'LATEST_VERSION': version}, + } } # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, diff --git a/scripts/docs.sh b/scripts/docs.sh index e2493511b..4d31277da 100755 --- a/scripts/docs.sh +++ b/scripts/docs.sh @@ -27,7 +27,6 @@ #------------------------------------------------------------------------------ set -e -scripts/update_robotstxt.sh cd docs pip3 install -r requirements.txt sphinx-build -nW -b html -d _build/doctrees . _build/html diff --git a/scripts/update_robotstxt.sh b/scripts/update_robotstxt.sh deleted file mode 100755 index cd09c1433..000000000 --- a/scripts/update_robotstxt.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -#------------------------------------------------------------------------------ -# solidity is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# solidity is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with solidity. If not, see -# -# (c) 2021 solidity contributors. -#------------------------------------------------------------------------------ - -set -eu - -repo_root="$(dirname "$0")/.." -robots_template_path="${repo_root}/docs/robots.txt.template" -robots_txt_path="${repo_root}/docs/_static/robots.txt" -latest_version=$("${repo_root}/scripts/get_version.sh") - -sed -E -e "s/\{\{[[:space:]]LATEST_VERSION[[:space:]]\}\}/${latest_version}/g" "$robots_template_path" > "$robots_txt_path" From bf204403d8ab1303bb4dfcc28d5a740e688b2982 Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Tue, 13 Jul 2021 18:38:34 +0200 Subject: [PATCH 51/98] Add "@use-src" header for generated yul code --- libsolidity/codegen/ir/IRGenerator.cpp | 19 ++++++++++++++----- test/cmdlineTests/exp_base_literal/output | 1 + test/cmdlineTests/revert_strings/output | 1 + .../standard_ir_requested/output.json | 1 + .../standard_viair_requested/output.json | 3 +++ test/cmdlineTests/viair_abicoder_v1/output | 1 + .../yul_source_locations/output.json | 2 ++ .../yul_string_format_ascii/output.json | 1 + .../output.json | 1 + .../output.json | 1 + .../yul_string_format_ascii_long/output.json | 1 + .../yul_string_format_hex/output.json | 1 + 12 files changed, 28 insertions(+), 5 deletions(-) diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index af9335adf..258c867fa 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -33,15 +33,13 @@ #include #include -#include -#include -#include #include +#include +#include +#include #include -#include - #include #include @@ -135,6 +133,7 @@ string IRGenerator::generate( }; Whiskers t(R"( + /// @use-src object "" { code { @@ -169,6 +168,16 @@ string IRGenerator::generate( for (VariableDeclaration const* var: ContractType(_contract).immutableVariables()) m_context.registerImmutableVariable(*var); + auto invertedSourceIndicies = invertMap(m_context.sourceIndices()); + + string useSrcMap = joinHumanReadable( + ranges::views::transform(invertedSourceIndicies, [](auto&& _pair) { + return to_string(_pair.first) + ":" + escapeAndQuoteString(_pair.second); + }), + ", " + ); + + t("useSrcMap", useSrcMap); t("sourceLocationComment", sourceLocationComment(_contract, m_context)); t("CreationObject", IRNames::creationObject(_contract)); diff --git a/test/cmdlineTests/exp_base_literal/output b/test/cmdlineTests/exp_base_literal/output index 479107cf0..2eb1998e7 100644 --- a/test/cmdlineTests/exp_base_literal/output +++ b/test/cmdlineTests/exp_base_literal/output @@ -7,6 +7,7 @@ IR: *=====================================================*/ +/// @use-src 0:"exp_base_literal/input.sol", 1:"#utility.yul" object "C_81" { code { /// @src 0:82:370 diff --git a/test/cmdlineTests/revert_strings/output b/test/cmdlineTests/revert_strings/output index fb4993268..c572ef6c2 100644 --- a/test/cmdlineTests/revert_strings/output +++ b/test/cmdlineTests/revert_strings/output @@ -7,6 +7,7 @@ IR: *=====================================================*/ +/// @use-src 0:"revert_strings/input.sol", 1:"#utility.yul" object "C_15" { code { /// @src 0:59:147 diff --git a/test/cmdlineTests/standard_ir_requested/output.json b/test/cmdlineTests/standard_ir_requested/output.json index 9165ea7ef..90f54bb21 100644 --- a/test/cmdlineTests/standard_ir_requested/output.json +++ b/test/cmdlineTests/standard_ir_requested/output.json @@ -6,6 +6,7 @@ *=====================================================*/ +/// @use-src 0:\"A\", 1:\"#utility.yul\" object \"C_7\" { code { /// @src 0:79:121 diff --git a/test/cmdlineTests/standard_viair_requested/output.json b/test/cmdlineTests/standard_viair_requested/output.json index 86849448f..e159ebe96 100644 --- a/test/cmdlineTests/standard_viair_requested/output.json +++ b/test/cmdlineTests/standard_viair_requested/output.json @@ -6,6 +6,7 @@ *=====================================================*/ +/// @use-src 0:\"A\", 1:\"#utility.yul\" object \"C_3\" { code { /// @src 0:79:92 @@ -79,6 +80,7 @@ object \"C_3\" { *=====================================================*/ +/// @use-src 0:\"A\", 1:\"#utility.yul\" object \"D_16\" { code { /// @src 0:93:146 @@ -206,6 +208,7 @@ object \"D_16\" { * !USE AT YOUR OWN RISK! * *=====================================================*/ + /// @use-src 0:\"A\", 1:\"#utility.yul\" object \"C_3\" { code { /// @src 0:79:92 diff --git a/test/cmdlineTests/viair_abicoder_v1/output b/test/cmdlineTests/viair_abicoder_v1/output index 1aafd5e56..17d53335c 100644 --- a/test/cmdlineTests/viair_abicoder_v1/output +++ b/test/cmdlineTests/viair_abicoder_v1/output @@ -7,6 +7,7 @@ IR: *=====================================================*/ +/// @use-src 0:"viair_abicoder_v1/input.sol", 1:"#utility.yul" object "test_11" { code { /// @src 0:79:169 diff --git a/test/cmdlineTests/yul_source_locations/output.json b/test/cmdlineTests/yul_source_locations/output.json index 7ba5b499a..b1ddda7a2 100644 --- a/test/cmdlineTests/yul_source_locations/output.json +++ b/test/cmdlineTests/yul_source_locations/output.json @@ -6,6 +6,7 @@ *=====================================================*/ +/// @use-src 0:\"C\", 1:\"D\", 2:\"#utility.yul\" object \"C_54\" { code { /// @src 0:79:428 @@ -535,6 +536,7 @@ object \"C_54\" { *=====================================================*/ +/// @use-src 0:\"C\", 1:\"D\", 2:\"#utility.yul\" object \"D_72\" { code { /// @src 1:91:166 diff --git a/test/cmdlineTests/yul_string_format_ascii/output.json b/test/cmdlineTests/yul_string_format_ascii/output.json index 80849e805..08c864157 100644 --- a/test/cmdlineTests/yul_string_format_ascii/output.json +++ b/test/cmdlineTests/yul_string_format_ascii/output.json @@ -6,6 +6,7 @@ *=====================================================*/ +/// @use-src 0:\"A\", 1:\"#utility.yul\" object \"C_11\" { code { /// @src 0:78:164 diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json index daf27fbe7..25bdeaec3 100644 --- a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json @@ -6,6 +6,7 @@ *=====================================================*/ +/// @use-src 0:\"A\", 1:\"#utility.yul\" object \"C_11\" { code { /// @src 0:78:158 diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json index 0ef2438be..776944b3c 100644 --- a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json @@ -6,6 +6,7 @@ *=====================================================*/ +/// @use-src 0:\"A\", 1:\"#utility.yul\" object \"C_11\" { code { /// @src 0:78:159 diff --git a/test/cmdlineTests/yul_string_format_ascii_long/output.json b/test/cmdlineTests/yul_string_format_ascii_long/output.json index 0628e4dce..df7065d2d 100644 --- a/test/cmdlineTests/yul_string_format_ascii_long/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_long/output.json @@ -6,6 +6,7 @@ *=====================================================*/ +/// @use-src 0:\"A\", 1:\"#utility.yul\" object \"C_11\" { code { /// @src 0:78:243 diff --git a/test/cmdlineTests/yul_string_format_hex/output.json b/test/cmdlineTests/yul_string_format_hex/output.json index 94fa33c38..de11d0fe9 100644 --- a/test/cmdlineTests/yul_string_format_hex/output.json +++ b/test/cmdlineTests/yul_string_format_hex/output.json @@ -6,6 +6,7 @@ *=====================================================*/ +/// @use-src 0:\"A\", 1:\"#utility.yul\" object \"C_11\" { code { /// @src 0:78:159 From f3707f2ab0f856f0101dda87eca6996f21d286af Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 11 Jun 2021 19:17:41 +0200 Subject: [PATCH 52/98] Control flow graph for Yul. --- libsolutil/cxx20.h | 11 + libyul/CMakeLists.txt | 3 + libyul/Scope.cpp | 3 +- libyul/Scope.h | 7 +- libyul/backends/evm/ControlFlowGraph.h | 210 ++++++++ .../backends/evm/ControlFlowGraphBuilder.cpp | 470 ++++++++++++++++++ libyul/backends/evm/ControlFlowGraphBuilder.h | 86 ++++ test/CMakeLists.txt | 2 + test/InteractiveTests.h | 30 +- test/libyul/ControlFlowGraphTest.cpp | 273 ++++++++++ test/libyul/ControlFlowGraphTest.h | 43 ++ .../yulControlFlowGraph/ambiguous_names.yul | 65 +++ test/libyul/yulControlFlowGraph/break.yul | 87 ++++ test/libyul/yulControlFlowGraph/complex.yul | 195 ++++++++ test/libyul/yulControlFlowGraph/for.yul | 51 ++ test/libyul/yulControlFlowGraph/function.yul | 75 +++ test/libyul/yulControlFlowGraph/if.yul | 37 ++ test/libyul/yulControlFlowGraph/leave.yul | 51 ++ .../nested_loop_complex.yul | 193 +++++++ test/libyul/yulControlFlowGraph/stub.yul | 15 + test/libyul/yulControlFlowGraph/switch.yul | 68 +++ test/libyul/yulControlFlowGraph/variables.yul | 31 ++ test/libyul/yulControlFlowGraph/verbatim.yul | 26 + test/tools/CMakeLists.txt | 4 +- 24 files changed, 2019 insertions(+), 17 deletions(-) create mode 100644 libyul/backends/evm/ControlFlowGraph.h create mode 100644 libyul/backends/evm/ControlFlowGraphBuilder.cpp create mode 100644 libyul/backends/evm/ControlFlowGraphBuilder.h create mode 100644 test/libyul/ControlFlowGraphTest.cpp create mode 100644 test/libyul/ControlFlowGraphTest.h create mode 100644 test/libyul/yulControlFlowGraph/ambiguous_names.yul create mode 100644 test/libyul/yulControlFlowGraph/break.yul create mode 100644 test/libyul/yulControlFlowGraph/complex.yul create mode 100644 test/libyul/yulControlFlowGraph/for.yul create mode 100644 test/libyul/yulControlFlowGraph/function.yul create mode 100644 test/libyul/yulControlFlowGraph/if.yul create mode 100644 test/libyul/yulControlFlowGraph/leave.yul create mode 100644 test/libyul/yulControlFlowGraph/nested_loop_complex.yul create mode 100644 test/libyul/yulControlFlowGraph/stub.yul create mode 100644 test/libyul/yulControlFlowGraph/switch.yul create mode 100644 test/libyul/yulControlFlowGraph/variables.yul create mode 100644 test/libyul/yulControlFlowGraph/verbatim.yul diff --git a/libsolutil/cxx20.h b/libsolutil/cxx20.h index bbe9817aa..c65b0454b 100644 --- a/libsolutil/cxx20.h +++ b/libsolutil/cxx20.h @@ -49,4 +49,15 @@ erase_if(std::unordered_map& _c, Pred _pred) return old_size - _c.size(); } +// Taken from https://en.cppreference.com/w/cpp/container/vector/erase2 +template +constexpr typename std::vector::size_type +erase_if(std::vector& c, Pred pred) +{ + auto it = std::remove_if(c.begin(), c.end(), pred); + auto r = std::distance(it, c.end()); + c.erase(it, c.end()); + return static_cast::size_type>(r); +} + } diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 59fb6d468..7b9d102e8 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -53,6 +53,9 @@ add_library(yul backends/evm/AsmCodeGen.h backends/evm/ConstantOptimiser.cpp backends/evm/ConstantOptimiser.h + backends/evm/ControlFlowGraph.h + backends/evm/ControlFlowGraphBuilder.cpp + backends/evm/ControlFlowGraphBuilder.h backends/evm/EthAssemblyAdapter.cpp backends/evm/EthAssemblyAdapter.h backends/evm/EVMCodeTransform.cpp diff --git a/libyul/Scope.cpp b/libyul/Scope.cpp index 3425e81ae..4a9520f98 100644 --- a/libyul/Scope.cpp +++ b/libyul/Scope.cpp @@ -32,6 +32,7 @@ bool Scope::registerVariable(YulString _name, YulType const& _type) return false; Variable variable; variable.type = _type; + variable.name = _name; identifiers[_name] = variable; return true; } @@ -40,7 +41,7 @@ bool Scope::registerFunction(YulString _name, std::vector _arguments, s { if (exists(_name)) return false; - identifiers[_name] = Function{std::move(_arguments), std::move(_returns)}; + identifiers[_name] = Function{std::move(_arguments), std::move(_returns), _name}; return true; } diff --git a/libyul/Scope.h b/libyul/Scope.h index a64906f2d..f9ac3830a 100644 --- a/libyul/Scope.h +++ b/libyul/Scope.h @@ -37,11 +37,16 @@ struct Scope { using YulType = YulString; - struct Variable { YulType type; }; + struct Variable + { + YulType type; + YulString name; + }; struct Function { std::vector arguments; std::vector returns; + YulString name; }; using Identifier = std::variant; diff --git a/libyul/backends/evm/ControlFlowGraph.h b/libyul/backends/evm/ControlFlowGraph.h new file mode 100644 index 000000000..60bec498b --- /dev/null +++ b/libyul/backends/evm/ControlFlowGraph.h @@ -0,0 +1,210 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +/** + * Control flow graph and stack layout structures used during code generation. + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +namespace solidity::yul +{ + +/// The following structs describe different kinds of stack slots. +/// Each stack slot is equality- and less-than-comparable and +/// specifies an attribute ``canBeFreelyGenerated`` that is true, +/// if a slot of this kind always has a known value at compile time and +/// therefore can safely be removed from the stack at any time and then +/// regenerated later. + +/// The label pushed as return label before a function call, i.e. the label the call is supposed to return to. +struct FunctionCallReturnLabelSlot +{ + std::reference_wrapper call; + bool operator==(FunctionCallReturnLabelSlot const& _rhs) const { return &call.get() == &_rhs.call.get(); } + bool operator<(FunctionCallReturnLabelSlot const& _rhs) const { return &call.get() < &_rhs.call.get(); } + static constexpr bool canBeFreelyGenerated = true; +}; +/// The return jump target of a function while generating the code of the function body. +/// I.e. the caller of a function pushes a ``FunctionCallReturnLabelSlot`` (see above) before jumping to the function and +/// this very slot is viewed as ``FunctionReturnLabelSlot`` inside the function body and jumped to when returning from +/// the function. +struct FunctionReturnLabelSlot +{ + bool operator==(FunctionReturnLabelSlot const&) const { return true; } + bool operator<(FunctionReturnLabelSlot const&) const { return false; } + static constexpr bool canBeFreelyGenerated = false; +}; +/// A slot containing the current value of a particular variable. +struct VariableSlot +{ + std::reference_wrapper variable; + std::shared_ptr debugData{}; + bool operator==(VariableSlot const& _rhs) const { return &variable.get() == &_rhs.variable.get(); } + bool operator<(VariableSlot const& _rhs) const { return &variable.get() < &_rhs.variable.get(); } + static constexpr bool canBeFreelyGenerated = false; +}; +/// A slot containing a literal value. +struct LiteralSlot +{ + u256 value; + std::shared_ptr debugData{}; + bool operator==(LiteralSlot const& _rhs) const { return value == _rhs.value; } + bool operator<(LiteralSlot const& _rhs) const { return value < _rhs.value; } + static constexpr bool canBeFreelyGenerated = true; +}; +/// A slot containing the index-th return value of a previous call. +struct TemporarySlot +{ + /// The call that returned this slot. + std::reference_wrapper call; + /// Specifies to which of the values returned by the call this slot refers. + /// index == 0 refers to the slot deepest in the stack after the call. + size_t index = 0; + bool operator==(TemporarySlot const& _rhs) const { return &call.get() == &_rhs.call.get() && index == _rhs.index; } + bool operator<(TemporarySlot const& _rhs) const { return std::make_pair(&call.get(), index) < std::make_pair(&_rhs.call.get(), _rhs.index); } + static constexpr bool canBeFreelyGenerated = false; +}; +/// A slot containing an arbitrary value that is always eventually popped and never used. +/// Used to maintain stack balance on control flow joins. +struct JunkSlot +{ + bool operator==(JunkSlot const&) const { return true; } + bool operator<(JunkSlot const&) const { return false; } + static constexpr bool canBeFreelyGenerated = true; +}; +using StackSlot = std::variant; +/// The stack top is usually the last element of the vector. +using Stack = std::vector; + +/// @returns true if @a _slot can be generated on the stack at any time. +inline bool canBeFreelyGenerated(StackSlot const& _slot) +{ + return std::visit([](auto const& _typedSlot) { return std::decay_t::canBeFreelyGenerated; }, _slot); +} + +/// Control flow graph consisting of ``CFG::BasicBlock``s connected by control flow. +struct CFG +{ + explicit CFG() {} + CFG(CFG const&) = delete; + CFG(CFG&&) = delete; + CFG& operator=(CFG const&) = delete; + CFG& operator=(CFG&&) = delete; + ~CFG() = default; + + struct BuiltinCall + { + std::shared_ptr debugData; + std::reference_wrapper builtin; + std::reference_wrapper functionCall; + /// Number of proper arguments with a position on the stack, excluding literal arguments. + /// Literal arguments (like the literal string in ``datasize``) do not have a location on the stack, + /// but are handled internally by the builtin's code generation function. + size_t arguments = 0; + }; + struct FunctionCall + { + std::shared_ptr debugData; + std::reference_wrapper function; + std::reference_wrapper functionCall; + }; + struct Assignment + { + std::shared_ptr debugData; + /// The variables being assigned to also occur as ``output`` in the ``Operation`` containing + /// the assignment, but are also stored here for convenience. + std::vector variables; + }; + + struct Operation + { + /// Stack slots this operation expects at the top of the stack and consumes. + Stack input; + /// Stack slots this operation leaves on the stack as output. + Stack output; + std::variant operation; + }; + + struct FunctionInfo; + /// A basic control flow block containing ``Operation``s acting on the stack. + /// Maintains a list of entry blocks and a typed exit. + struct BasicBlock + { + struct MainExit {}; + struct ConditionalJump + { + StackSlot condition; + BasicBlock* nonZero = nullptr; + BasicBlock* zero = nullptr; + }; + struct Jump + { + BasicBlock* target = nullptr; + /// The only backwards jumps are jumps from loop post to loop condition. + bool backwards = false; + }; + struct FunctionReturn { CFG::FunctionInfo* info = nullptr; }; + struct Terminated {}; + std::vector entries; + std::vector operations; + std::variant exit = MainExit{}; + }; + + struct FunctionInfo + { + std::shared_ptr debugData; + Scope::Function const& function; + BasicBlock* entry = nullptr; + std::vector parameters; + std::vector returnVariables; + }; + + /// The main entry point, i.e. the start of the outermost Yul block. + BasicBlock* entry = nullptr; + /// Subgraphs for functions. + std::map functionInfo; + /// List of functions in order of declaration. + std::list functions; + + /// Container for blocks for explicit ownership. + std::list blocks; + /// Container for generated variables for explicit ownership. + /// Ghost variables are generated to store switch conditions when transforming the control flow + /// of a switch to a sequence of conditional jumps. + std::list ghostVariables; + /// Container for generated calls for explicit ownership. + /// Ghost calls are used for the equality comparisons of the switch condition ghost variable with + /// the switch case literals when transforming the control flow of a switch to a sequence of conditional jumps. + std::list ghostCalls; + + BasicBlock& makeBlock() + { + return blocks.emplace_back(BasicBlock{}); + } +}; + +} diff --git a/libyul/backends/evm/ControlFlowGraphBuilder.cpp b/libyul/backends/evm/ControlFlowGraphBuilder.cpp new file mode 100644 index 000000000..f3a1d5823 --- /dev/null +++ b/libyul/backends/evm/ControlFlowGraphBuilder.cpp @@ -0,0 +1,470 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +/** + * Transformation of a Yul AST into a control flow graph. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace solidity; +using namespace solidity::yul; +using namespace std; + +std::unique_ptr ControlFlowGraphBuilder::build( + AsmAnalysisInfo const& _analysisInfo, + Dialect const& _dialect, + Block const& _block +) +{ + auto result = std::make_unique(); + result->entry = &result->makeBlock(); + + ControlFlowGraphBuilder builder(*result, _analysisInfo, _dialect); + builder.m_currentBlock = result->entry; + builder(_block); + + // Determine which blocks are reachable from the entry. + util::BreadthFirstSearch reachabilityCheck{{result->entry}}; + for (auto const& functionInfo: result->functionInfo | ranges::views::values) + reachabilityCheck.verticesToTraverse.emplace_back(functionInfo.entry); + + reachabilityCheck.run([&](CFG::BasicBlock* _node, auto&& _addChild) { + visit(util::GenericVisitor{ + [&](CFG::BasicBlock::Jump const& _jump) { + _addChild(_jump.target); + }, + [&](CFG::BasicBlock::ConditionalJump const& _jump) { + _addChild(_jump.zero); + _addChild(_jump.nonZero); + }, + [](CFG::BasicBlock::FunctionReturn const&) {}, + [](CFG::BasicBlock::Terminated const&) {}, + [](CFG::BasicBlock::MainExit const&) {} + }, _node->exit); + }); + + // Remove all entries from unreachable nodes from the graph. + for (CFG::BasicBlock* node: reachabilityCheck.visited) + cxx20::erase_if(node->entries, [&](CFG::BasicBlock* entry) -> bool { + return !reachabilityCheck.visited.count(entry); + }); + + // TODO: It might be worthwhile to run some further simplifications on the graph itself here. + // E.g. if there is a jump to a node that has the jumping node as its only entry, the nodes can be fused, etc. + + return result; +} + +ControlFlowGraphBuilder::ControlFlowGraphBuilder( + CFG& _graph, + AsmAnalysisInfo const& _analysisInfo, + Dialect const& _dialect +): + m_graph(_graph), + m_info(_analysisInfo), + m_dialect(_dialect) +{ +} + +StackSlot ControlFlowGraphBuilder::operator()(Literal const& _literal) +{ + return LiteralSlot{valueOfLiteral(_literal), _literal.debugData}; +} + +StackSlot ControlFlowGraphBuilder::operator()(Identifier const& _identifier) +{ + return VariableSlot{lookupVariable(_identifier.name), _identifier.debugData}; +} + +StackSlot ControlFlowGraphBuilder::operator()(Expression const& _expression) +{ + return std::visit(*this, _expression); +} + +StackSlot ControlFlowGraphBuilder::operator()(FunctionCall const& _call) +{ + CFG::Operation const& operation = visitFunctionCall(_call); + yulAssert(operation.output.size() == 1, ""); + return operation.output.front(); +} + +void ControlFlowGraphBuilder::operator()(VariableDeclaration const& _varDecl) +{ + yulAssert(m_currentBlock, ""); + auto declaredVariables = _varDecl.variables | ranges::views::transform([&](TypedName const& _var) { + return VariableSlot{lookupVariable(_var.name), _var.debugData}; + }) | ranges::to>; + Stack input; + if (_varDecl.value) + input = visitAssignmentRightHandSide(*_varDecl.value, declaredVariables.size()); + else + input = Stack(_varDecl.variables.size(), LiteralSlot{0, _varDecl.debugData}); + m_currentBlock->operations.emplace_back(CFG::Operation{ + std::move(input), + declaredVariables | ranges::to, + CFG::Assignment{_varDecl.debugData, declaredVariables} + }); +} +void ControlFlowGraphBuilder::operator()(Assignment const& _assignment) +{ + auto assignedVariables = _assignment.variableNames | ranges::views::transform([&](Identifier const& _var) { + return VariableSlot{lookupVariable(_var.name), _var.debugData}; + }) | ranges::to>; + + yulAssert(m_currentBlock, ""); + m_currentBlock->operations.emplace_back(CFG::Operation{ + // input + visitAssignmentRightHandSide(*_assignment.value, assignedVariables.size()), + // output + assignedVariables | ranges::to, + // operation + CFG::Assignment{_assignment.debugData, assignedVariables} + }); +} +void ControlFlowGraphBuilder::operator()(ExpressionStatement const& _exprStmt) +{ + yulAssert(m_currentBlock, ""); + std::visit(util::GenericVisitor{ + [&](FunctionCall const& _call) { + CFG::Operation const& operation = visitFunctionCall(_call); + yulAssert(operation.output.empty(), ""); + }, + [&](auto const&) { yulAssert(false, ""); } + }, _exprStmt.expression); + + // TODO: Ideally this would be done on the expression label and for all functions that always revert, + // not only for builtins. + if (auto const* funCall = get_if(&_exprStmt.expression)) + if (BuiltinFunction const* builtin = m_dialect.builtin(funCall->functionName.name)) + if (builtin->controlFlowSideEffects.terminates) + { + m_currentBlock->exit = CFG::BasicBlock::Terminated{}; + m_currentBlock = &m_graph.makeBlock(); + } +} + +void ControlFlowGraphBuilder::operator()(Block const& _block) +{ + ScopedSaveAndRestore saveScope(m_scope, m_info.scopes.at(&_block).get()); + for (auto const& statement: _block.statements) + std::visit(*this, statement); +} + +void ControlFlowGraphBuilder::operator()(If const& _if) +{ + auto&& [ifBranch, afterIf] = makeConditionalJump(std::visit(*this, *_if.condition)); + m_currentBlock = ifBranch; + (*this)(_if.body); + jump(*afterIf); +} + +void ControlFlowGraphBuilder::operator()(Switch const& _switch) +{ + yulAssert(m_currentBlock, ""); + auto ghostVariableId = m_graph.ghostVariables.size(); + YulString ghostVariableName("GHOST[" + to_string(ghostVariableId) + "]"); + auto& ghostVar = m_graph.ghostVariables.emplace_back(Scope::Variable{""_yulstring, ghostVariableName}); + + // Artificially generate: + // let := + VariableSlot ghostVarSlot{ghostVar, debugDataOf(*_switch.expression)}; + m_currentBlock->operations.emplace_back(CFG::Operation{ + Stack{std::visit(*this, *_switch.expression)}, + Stack{ghostVarSlot}, + CFG::Assignment{_switch.debugData, {ghostVarSlot}} + }); + + BuiltinFunction const* equalityBuiltin = m_dialect.equalityFunction({}); + yulAssert(equalityBuiltin, ""); + + // Artificially generate: + // eq(, ) + auto makeValueCompare = [&](Literal const& _value) { + yul::FunctionCall const& ghostCall = m_graph.ghostCalls.emplace_back(yul::FunctionCall{ + _value.debugData, + yul::Identifier{{}, "eq"_yulstring}, + {_value, Identifier{{}, ghostVariableName}} + }); + CFG::Operation& operation = m_currentBlock->operations.emplace_back(CFG::Operation{ + Stack{ghostVarSlot, LiteralSlot{valueOfLiteral(_value), _value.debugData}}, + Stack{TemporarySlot{ghostCall, 0}}, + CFG::BuiltinCall{_switch.debugData, *equalityBuiltin, ghostCall, 2}, + }); + return operation.output.front(); + }; + CFG::BasicBlock& afterSwitch = m_graph.makeBlock(); + yulAssert(!_switch.cases.empty(), ""); + for (auto const& switchCase: _switch.cases | ranges::views::drop_last(1)) + { + yulAssert(switchCase.value, ""); + auto&& [caseBranch, elseBranch] = makeConditionalJump(makeValueCompare(*switchCase.value)); + m_currentBlock = caseBranch; + (*this)(switchCase.body); + jump(afterSwitch); + m_currentBlock = elseBranch; + } + Case const& switchCase = _switch.cases.back(); + if (switchCase.value) + { + CFG::BasicBlock& caseBranch = m_graph.makeBlock(); + makeConditionalJump(makeValueCompare(*switchCase.value), caseBranch, afterSwitch); + m_currentBlock = &caseBranch; + } + (*this)(switchCase.body); + jump(afterSwitch); +} + +void ControlFlowGraphBuilder::operator()(ForLoop const& _loop) +{ + ScopedSaveAndRestore scopeRestore(m_scope, m_info.scopes.at(&_loop.pre).get()); + (*this)(_loop.pre); + + std::optional constantCondition; + if (auto const* literalCondition = get_if(_loop.condition.get())) + constantCondition = valueOfLiteral(*literalCondition) != 0; + + CFG::BasicBlock& loopCondition = m_graph.makeBlock(); + CFG::BasicBlock& loopBody = m_graph.makeBlock(); + CFG::BasicBlock& post = m_graph.makeBlock(); + CFG::BasicBlock& afterLoop = m_graph.makeBlock(); + + ScopedSaveAndRestore scopedSaveAndRestore(m_forLoopInfo, ForLoopInfo{afterLoop, post}); + + if (constantCondition.has_value()) + { + if (*constantCondition) + { + jump(loopBody); + (*this)(_loop.body); + jump(post); + (*this)(_loop.post); + jump(loopBody, true); + } + else + jump(afterLoop); + } + else + { + jump(loopCondition); + makeConditionalJump(std::visit(*this, *_loop.condition), loopBody, afterLoop); + m_currentBlock = &loopBody; + (*this)(_loop.body); + jump(post); + (*this)(_loop.post); + jump(loopCondition, true); + } + + m_currentBlock = &afterLoop; +} + +void ControlFlowGraphBuilder::operator()(Break const&) +{ + yulAssert(m_forLoopInfo.has_value(), ""); + jump(m_forLoopInfo->afterLoop); + m_currentBlock = &m_graph.makeBlock(); +} + +void ControlFlowGraphBuilder::operator()(Continue const&) +{ + yulAssert(m_forLoopInfo.has_value(), ""); + jump(m_forLoopInfo->post); + m_currentBlock = &m_graph.makeBlock(); +} + +void ControlFlowGraphBuilder::operator()(Leave const&) +{ + yulAssert(m_currentFunctionExit.has_value(), ""); + m_currentBlock->exit = *m_currentFunctionExit; + m_currentBlock = &m_graph.makeBlock(); +} + +void ControlFlowGraphBuilder::operator()(FunctionDefinition const& _function) +{ + yulAssert(m_scope, ""); + yulAssert(m_scope->identifiers.count(_function.name), ""); + Scope::Function& function = std::get(m_scope->identifiers.at(_function.name)); + m_graph.functions.emplace_back(&function); + + yulAssert(m_info.scopes.at(&_function.body), ""); + Scope* virtualFunctionScope = m_info.scopes.at(m_info.virtualBlocks.at(&_function).get()).get(); + yulAssert(virtualFunctionScope, ""); + + auto&& [it, inserted] = m_graph.functionInfo.emplace(std::make_pair(&function, CFG::FunctionInfo{ + _function.debugData, + function, + &m_graph.makeBlock(), + _function.parameters | ranges::views::transform([&](auto const& _param) { + return VariableSlot{ + std::get(virtualFunctionScope->identifiers.at(_param.name)), + _param.debugData + }; + }) | ranges::to, + _function.returnVariables | ranges::views::transform([&](auto const& _retVar) { + return VariableSlot{ + std::get(virtualFunctionScope->identifiers.at(_retVar.name)), + _retVar.debugData + }; + }) | ranges::to + })); + yulAssert(inserted, ""); + CFG::FunctionInfo& functionInfo = it->second; + + ControlFlowGraphBuilder builder{m_graph, m_info, m_dialect}; + builder.m_currentFunctionExit = CFG::BasicBlock::FunctionReturn{&functionInfo}; + builder.m_currentBlock = functionInfo.entry; + builder(_function.body); + builder.m_currentBlock->exit = CFG::BasicBlock::FunctionReturn{&functionInfo}; +} + + +CFG::Operation const& ControlFlowGraphBuilder::visitFunctionCall(FunctionCall const& _call) +{ + yulAssert(m_scope, ""); + yulAssert(m_currentBlock, ""); + + if (BuiltinFunction const* builtin = m_dialect.builtin(_call.functionName.name)) + { + Stack inputs; + for (auto&& [idx, arg]: _call.arguments | ranges::views::enumerate | ranges::views::reverse) + if (!builtin->literalArgument(idx).has_value()) + inputs.emplace_back(std::visit(*this, arg)); + CFG::BuiltinCall builtinCall{_call.debugData, *builtin, _call, inputs.size()}; + return m_currentBlock->operations.emplace_back(CFG::Operation{ + // input + std::move(inputs), + // output + ranges::views::iota(0u, builtin->returns.size()) | ranges::views::transform([&](size_t _i) { + return TemporarySlot{_call, _i}; + }) | ranges::to, + // operation + move(builtinCall) + }); + } + else + { + Scope::Function const& function = lookupFunction(_call.functionName.name); + Stack inputs{FunctionCallReturnLabelSlot{_call}}; + for (auto const& arg: _call.arguments | ranges::views::reverse) + inputs.emplace_back(std::visit(*this, arg)); + return m_currentBlock->operations.emplace_back(CFG::Operation{ + // input + std::move(inputs), + // output + ranges::views::iota(0u, function.returns.size()) | ranges::views::transform([&](size_t _i) { + return TemporarySlot{_call, _i}; + }) | ranges::to, + // operation + CFG::FunctionCall{_call.debugData, function, _call} + }); + } +} + +Stack ControlFlowGraphBuilder::visitAssignmentRightHandSide(Expression const& _expression, size_t _expectedSlotCount) +{ + return std::visit(util::GenericVisitor{ + [&](FunctionCall const& _call) -> Stack { + CFG::Operation const& operation = visitFunctionCall(_call); + yulAssert(_expectedSlotCount == operation.output.size(), ""); + return operation.output; + }, + [&](auto const& _identifierOrLiteral) -> Stack { + yulAssert(_expectedSlotCount == 1, ""); + return {(*this)(_identifierOrLiteral)}; + } + }, _expression); +} + +Scope::Function const& ControlFlowGraphBuilder::lookupFunction(YulString _name) const +{ + Scope::Function const* function = nullptr; + yulAssert(m_scope->lookup(_name, util::GenericVisitor{ + [](Scope::Variable&) { yulAssert(false, "Expected function name."); }, + [&](Scope::Function& _function) { function = &_function; } + }), "Function name not found."); + yulAssert(function, ""); + return *function; +} + +Scope::Variable const& ControlFlowGraphBuilder::lookupVariable(YulString _name) const +{ + yulAssert(m_scope, ""); + Scope::Variable const* var = nullptr; + if (m_scope->lookup(_name, util::GenericVisitor{ + [&](Scope::Variable& _var) { var = &_var; }, + [](Scope::Function&) + { + yulAssert(false, "Function not removed during desugaring."); + } + })) + { + yulAssert(var, ""); + return *var; + }; + yulAssert(false, "External identifier access unimplemented."); +} + +std::pair ControlFlowGraphBuilder::makeConditionalJump(StackSlot _condition) +{ + CFG::BasicBlock& nonZero = m_graph.makeBlock(); + CFG::BasicBlock& zero = m_graph.makeBlock(); + makeConditionalJump(move(_condition), nonZero, zero); + return {&nonZero, &zero}; +} + +void ControlFlowGraphBuilder::makeConditionalJump(StackSlot _condition, CFG::BasicBlock& _nonZero, CFG::BasicBlock& _zero) +{ + yulAssert(m_currentBlock, ""); + m_currentBlock->exit = CFG::BasicBlock::ConditionalJump{ + move(_condition), + &_nonZero, + &_zero + }; + _nonZero.entries.emplace_back(m_currentBlock); + _zero.entries.emplace_back(m_currentBlock); + m_currentBlock = nullptr; +} + +void ControlFlowGraphBuilder::jump(CFG::BasicBlock& _target, bool backwards) +{ + yulAssert(m_currentBlock, ""); + m_currentBlock->exit = CFG::BasicBlock::Jump{&_target, backwards}; + _target.entries.emplace_back(m_currentBlock); + m_currentBlock = &_target; +} diff --git a/libyul/backends/evm/ControlFlowGraphBuilder.h b/libyul/backends/evm/ControlFlowGraphBuilder.h new file mode 100644 index 000000000..2a70a9472 --- /dev/null +++ b/libyul/backends/evm/ControlFlowGraphBuilder.h @@ -0,0 +1,86 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +/** + * Transformation of a Yul AST into a control flow graph. + */ +#pragma once + +#include + +namespace solidity::yul +{ + +class ControlFlowGraphBuilder +{ +public: + ControlFlowGraphBuilder(ControlFlowGraphBuilder const&) = delete; + ControlFlowGraphBuilder& operator=(ControlFlowGraphBuilder const&) = delete; + static std::unique_ptr build(AsmAnalysisInfo const& _analysisInfo, Dialect const& _dialect, Block const& _block); + + StackSlot operator()(Expression const& _literal); + StackSlot operator()(Literal const& _literal); + StackSlot operator()(Identifier const& _identifier); + StackSlot operator()(FunctionCall const&); + + void operator()(VariableDeclaration const& _varDecl); + void operator()(Assignment const& _assignment); + void operator()(ExpressionStatement const& _statement); + + void operator()(Block const& _block); + + void operator()(If const& _if); + void operator()(Switch const& _switch); + void operator()(ForLoop const&); + void operator()(Break const&); + void operator()(Continue const&); + void operator()(Leave const&); + void operator()(FunctionDefinition const&); + +private: + ControlFlowGraphBuilder( + CFG& _graph, + AsmAnalysisInfo const& _analysisInfo, + Dialect const& _dialect + ); + CFG::Operation const& visitFunctionCall(FunctionCall const&); + Stack visitAssignmentRightHandSide(Expression const& _expression, size_t _expectedSlotCount); + + Scope::Function const& lookupFunction(YulString _name) const; + Scope::Variable const& lookupVariable(YulString _name) const; + /// @returns a pair of newly created blocks, the first element being the non-zero branch, the second element the + /// zero branch. + /// Resets m_currentBlock to enforce a subsequent explicit reassignment. + std::pair makeConditionalJump(StackSlot _condition); + /// Resets m_currentBlock to enforce a subsequent explicit reassignment. + void makeConditionalJump(StackSlot _condition, CFG::BasicBlock& _nonZero, CFG::BasicBlock& _zero); + void jump(CFG::BasicBlock& _target, bool _backwards = false); + CFG& m_graph; + AsmAnalysisInfo const& m_info; + Dialect const& m_dialect; + CFG::BasicBlock* m_currentBlock = nullptr; + Scope* m_scope = nullptr; + struct ForLoopInfo + { + std::reference_wrapper afterLoop; + std::reference_wrapper post; + }; + std::optional m_forLoopInfo; + std::optional m_currentFunctionExit; +}; + +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c82f1ee7b..17300c660 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -126,6 +126,8 @@ set(libyul_sources libyul/Common.cpp libyul/Common.h libyul/CompilabilityChecker.cpp + libyul/ControlFlowGraphTest.cpp + libyul/ControlFlowGraphTest.h libyul/EVMCodeTransformTest.cpp libyul/EVMCodeTransformTest.h libyul/EwasmTranslationTest.cpp diff --git a/test/InteractiveTests.h b/test/InteractiveTests.h index 68ca721a5..4ffdf5a74 100644 --- a/test/InteractiveTests.h +++ b/test/InteractiveTests.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -55,20 +56,21 @@ struct Testsuite Testsuite const g_interactiveTestsuites[] = { /* Title Path Subpath SMT NeedsVM Creator function */ - {"Ewasm Translation", "libyul", "ewasmTranslationTests",false,false, &yul::test::EwasmTranslationTest::create}, - {"Yul Optimizer", "libyul", "yulOptimizerTests", false, false, &yul::test::YulOptimizerTest::create}, - {"Yul Interpreter", "libyul", "yulInterpreterTests", false, false, &yul::test::YulInterpreterTest::create}, - {"Yul Object Compiler", "libyul", "objectCompiler", false, false, &yul::test::ObjectCompilerTest::create}, - {"Function Side Effects","libyul", "functionSideEffects", false, false, &yul::test::FunctionSideEffects::create}, - {"Yul Syntax", "libyul", "yulSyntaxTests", false, false, &yul::test::SyntaxTest::create}, - {"EVM Code Transform", "libyul", "evmCodeTransform", false, false, &yul::test::EVMCodeTransformTest::create, {"nooptions"}}, - {"Syntax", "libsolidity", "syntaxTests", false, false, &SyntaxTest::create}, - {"Error Recovery", "libsolidity", "errorRecoveryTests", false, false, &SyntaxTest::createErrorRecovery}, - {"Semantic", "libsolidity", "semanticTests", false, true, &SemanticTest::create}, - {"JSON AST", "libsolidity", "ASTJSON", false, false, &ASTJSONTest::create}, - {"JSON ABI", "libsolidity", "ABIJson", false, false, &ABIJsonTest::create}, - {"SMT Checker", "libsolidity", "smtCheckerTests", true, false, &SMTCheckerTest::create}, - {"Gas Estimates", "libsolidity", "gasTests", false, false, &GasTest::create} + {"Ewasm Translation", "libyul", "ewasmTranslationTests", false, false, &yul::test::EwasmTranslationTest::create}, + {"Yul Optimizer", "libyul", "yulOptimizerTests", false, false, &yul::test::YulOptimizerTest::create}, + {"Yul Interpreter", "libyul", "yulInterpreterTests", false, false, &yul::test::YulInterpreterTest::create}, + {"Yul Object Compiler", "libyul", "objectCompiler", false, false, &yul::test::ObjectCompilerTest::create}, + {"Yul Control Flow Graph", "libyul", "yulControlFlowGraph", false, false, &yul::test::ControlFlowGraphTest::create}, + {"Function Side Effects", "libyul", "functionSideEffects", false, false, &yul::test::FunctionSideEffects::create}, + {"Yul Syntax", "libyul", "yulSyntaxTests", false, false, &yul::test::SyntaxTest::create}, + {"EVM Code Transform", "libyul", "evmCodeTransform", false, false, &yul::test::EVMCodeTransformTest::create, {"nooptions"}}, + {"Syntax", "libsolidity", "syntaxTests", false, false, &SyntaxTest::create}, + {"Error Recovery", "libsolidity", "errorRecoveryTests", false, false, &SyntaxTest::createErrorRecovery}, + {"Semantic", "libsolidity", "semanticTests", false, true, &SemanticTest::create}, + {"JSON AST", "libsolidity", "ASTJSON", false, false, &ASTJSONTest::create}, + {"JSON ABI", "libsolidity", "ABIJson", false, false, &ABIJsonTest::create}, + {"SMT Checker", "libsolidity", "smtCheckerTests", true, false, &SMTCheckerTest::create}, + {"Gas Estimates", "libsolidity", "gasTests", false, false, &GasTest::create} }; } diff --git a/test/libyul/ControlFlowGraphTest.cpp b/test/libyul/ControlFlowGraphTest.cpp new file mode 100644 index 000000000..1107f4ca2 --- /dev/null +++ b/test/libyul/ControlFlowGraphTest.cpp @@ -0,0 +1,273 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef ISOLTEST +#include +#endif + +using namespace solidity; +using namespace solidity::util; +using namespace solidity::langutil; +using namespace solidity::yul; +using namespace solidity::yul::test; +using namespace solidity::frontend; +using namespace solidity::frontend::test; +using namespace std; + +ControlFlowGraphTest::ControlFlowGraphTest(string const& _filename): + TestCase(_filename) +{ + m_source = m_reader.source(); + auto dialectName = m_reader.stringSetting("dialect", "evm"); + m_dialect = &dialect(dialectName, solidity::test::CommonOptions::get().evmVersion()); + m_expectation = m_reader.simpleExpectations(); +} + +namespace +{ +static std::string stackSlotToString(StackSlot const& _slot) +{ + return std::visit(util::GenericVisitor{ + [](FunctionCallReturnLabelSlot const& _ret) -> std::string { return "RET[" + _ret.call.get().functionName.name.str() + "]"; }, + [](FunctionReturnLabelSlot const&) -> std::string { return "RET"; }, + [](VariableSlot const& _var) { return _var.variable.get().name.str(); }, + [](LiteralSlot const& _lit) { return util::toCompactHexWithPrefix(_lit.value); }, + [](TemporarySlot const& _tmp) -> std::string { return "TMP[" + _tmp.call.get().functionName.name.str() + ", " + std::to_string(_tmp.index) + "]"; }, + [](JunkSlot const&) -> std::string { return "JUNK"; } + }, _slot); +} + +static std::string stackToString(Stack const& _stack) +{ + std::string result("[ "); + for (auto const& slot: _stack) + result += stackSlotToString(slot) + ' '; + result += ']'; + return result; +} +static std::string variableSlotToString(VariableSlot const& _slot) +{ + return _slot.variable.get().name.str(); +} +} + +class ControlFlowGraphPrinter +{ +public: + ControlFlowGraphPrinter(std::ostream& _stream): + m_stream(_stream) + { + } + void operator()(CFG::BasicBlock const& _block, bool _isMainEntry = true) + { + if (_isMainEntry) + { + m_stream << "Entry [label=\"Entry\"];\n"; + m_stream << "Entry -> Block" << getBlockId(_block) << ";\n"; + } + while (!m_blocksToPrint.empty()) + { + CFG::BasicBlock const* block = *m_blocksToPrint.begin(); + m_blocksToPrint.erase(m_blocksToPrint.begin()); + printBlock(*block); + } + + } + void operator()( + CFG::FunctionInfo const& _info + ) + { + m_stream << "FunctionEntry_" << _info.function.name.str() << "_" << getBlockId(*_info.entry) << " [label=\""; + m_stream << "function " << _info.function.name.str() << "("; + m_stream << joinHumanReadable(_info.parameters | ranges::views::transform(variableSlotToString)); + m_stream << ")"; + if (!_info.returnVariables.empty()) + { + m_stream << " -> "; + m_stream << joinHumanReadable(_info.returnVariables | ranges::views::transform(variableSlotToString)); + } + m_stream << "\"];\n"; + m_stream << "FunctionEntry_" << _info.function.name.str() << "_" << getBlockId(*_info.entry) << " -> Block" << getBlockId(*_info.entry) << ";\n"; + (*this)(*_info.entry, false); + } + +private: + void printBlock(CFG::BasicBlock const& _block) + { + m_stream << "Block" << getBlockId(_block) << " [label=\"\\\n"; + + // Verify that the entries of this block exit into this block. + for (auto const& entry: _block.entries) + std::visit(util::GenericVisitor{ + [&](CFG::BasicBlock::Jump const& _jump) + { + soltestAssert(_jump.target == &_block, "Invalid control flow graph."); + }, + [&](CFG::BasicBlock::ConditionalJump const& _conditionalJump) + { + soltestAssert( + _conditionalJump.zero == &_block || _conditionalJump.nonZero == &_block, + "Invalid control flow graph." + ); + }, + [&](auto const&) + { + soltestAssert(false, "Invalid control flow graph."); + } + }, entry->exit); + + for (auto const& operation: _block.operations) + { + std::visit(util::GenericVisitor{ + [&](CFG::FunctionCall const& _call) { + m_stream << _call.function.get().name.str() << ": "; + }, + [&](CFG::BuiltinCall const& _call) { + m_stream << _call.functionCall.get().functionName.name.str() << ": "; + + }, + [&](CFG::Assignment const& _assignment) { + m_stream << "Assignment("; + m_stream << joinHumanReadable(_assignment.variables | ranges::views::transform(variableSlotToString)); + m_stream << "): "; + } + }, operation.operation); + m_stream << stackToString(operation.input) << " => " << stackToString(operation.output) << "\\l\\\n"; + } + m_stream << "\"];\n"; + std::visit(util::GenericVisitor{ + [&](CFG::BasicBlock::MainExit const&) + { + m_stream << "Block" << getBlockId(_block) << "Exit [label=\"MainExit\"];\n"; + m_stream << "Block" << getBlockId(_block) << " -> Block" << getBlockId(_block) << "Exit;\n"; + }, + [&](CFG::BasicBlock::Jump const& _jump) + { + m_stream << "Block" << getBlockId(_block) << " -> Block" << getBlockId(_block) << "Exit [arrowhead=none];\n"; + m_stream << "Block" << getBlockId(_block) << "Exit [label=\""; + if (_jump.backwards) + m_stream << "Backwards"; + m_stream << "Jump\" shape=oval];\n"; + m_stream << "Block" << getBlockId(_block) << "Exit -> Block" << getBlockId(*_jump.target) << ";\n"; + }, + [&](CFG::BasicBlock::ConditionalJump const& _conditionalJump) + { + m_stream << "Block" << getBlockId(_block) << " -> Block" << getBlockId(_block) << "Exit;\n"; + m_stream << "Block" << getBlockId(_block) << "Exit [label=\"{ "; + m_stream << stackSlotToString(_conditionalJump.condition); + m_stream << "| { <0> Zero | <1> NonZero }}\" shape=Mrecord];\n"; + m_stream << "Block" << getBlockId(_block); + m_stream << "Exit:0 -> Block" << getBlockId(*_conditionalJump.zero) << ";\n"; + m_stream << "Block" << getBlockId(_block); + m_stream << "Exit:1 -> Block" << getBlockId(*_conditionalJump.nonZero) << ";\n"; + }, + [&](CFG::BasicBlock::FunctionReturn const& _return) + { + m_stream << "Block" << getBlockId(_block) << "Exit [label=\"FunctionReturn[" << _return.info->function.name.str() << "]\"];\n"; + m_stream << "Block" << getBlockId(_block) << " -> Block" << getBlockId(_block) << "Exit;\n"; + }, + [&](CFG::BasicBlock::Terminated const&) + { + m_stream << "Block" << getBlockId(_block) << "Exit [label=\"Terminated\"];\n"; + m_stream << "Block" << getBlockId(_block) << " -> Block" << getBlockId(_block) << "Exit;\n"; + } + }, _block.exit); + m_stream << "\n"; + } + size_t getBlockId(CFG::BasicBlock const& _block) + { + if (size_t* id = util::valueOrNullptr(m_blockIds, &_block)) + return *id; + size_t id = m_blockIds[&_block] = m_blockCount++; + m_blocksToPrint.emplace_back(&_block); + return id; + } + std::ostream& m_stream; + std::map m_blockIds; + size_t m_blockCount = 0; + std::list m_blocksToPrint; +}; + +TestCase::TestResult ControlFlowGraphTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) +{ + ErrorList errors; + auto [object, analysisInfo] = parse(m_source, *m_dialect, errors); + if (!object || !analysisInfo || !Error::containsOnlyWarnings(errors)) + { + AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; + return TestResult::FatalError; + } + + std::ostringstream output; + + std::unique_ptr cfg = ControlFlowGraphBuilder::build(*analysisInfo, *m_dialect, *object->code); + + output << "digraph CFG {\nnodesep=0.7;\nnode[shape=box];\n\n"; + ControlFlowGraphPrinter printer{output}; + printer(*cfg->entry); + for (auto function: cfg->functions) + printer(cfg->functionInfo.at(function)); + output << "}\n"; + + m_obtainedResult = output.str(); + + auto result = checkResult(_stream, _linePrefix, _formatted); + +#ifdef ISOLTEST + char* graphDisplayer = nullptr; + // The environment variables specify an optional command that will receive the graph encoded in DOT through stdin. + // Examples for suitable commands are ``dot -Tx11:cairo`` or ``xdot -``. + if (result == TestResult::Failure) + // ISOLTEST_DISPLAY_GRAPHS_ON_FAILURE_COMMAND will run on all failing tests (intended for use during modifications). + graphDisplayer = getenv("ISOLTEST_DISPLAY_GRAPHS_ON_FAILURE_COMMAND"); + else if (result == TestResult::Success) + // ISOLTEST_DISPLAY_GRAPHS_ON_FAILURE_COMMAND will run on all succeeding tests (intended for use during reviews). + graphDisplayer = getenv("ISOLTEST_DISPLAY_GRAPHS_ON_SUCCESS_COMMAND"); + + if (graphDisplayer) + { + if (result == TestResult::Success) + std::cout << std::endl << m_source << std::endl; + boost::process::opstream pipe; + boost::process::child child(graphDisplayer, boost::process::std_in < pipe); + + pipe << output.str(); + pipe.flush(); + pipe.pipe().close(); + if (result == TestResult::Success) + child.wait(); + else + child.detach(); + } +#endif + + return result; + +} diff --git a/test/libyul/ControlFlowGraphTest.h b/test/libyul/ControlFlowGraphTest.h new file mode 100644 index 000000000..44cea06d3 --- /dev/null +++ b/test/libyul/ControlFlowGraphTest.h @@ -0,0 +1,43 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include + +namespace solidity::yul +{ +struct Dialect; + +namespace test +{ + +class ControlFlowGraphTest: public solidity::frontend::test::TestCase +{ +public: + static std::unique_ptr create(Config const& _config) + { + return std::make_unique(_config.filename); + } + explicit ControlFlowGraphTest(std::string const& _filename); + TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) override; +private: + Dialect const* m_dialect = nullptr; +}; +} +} diff --git a/test/libyul/yulControlFlowGraph/ambiguous_names.yul b/test/libyul/yulControlFlowGraph/ambiguous_names.yul new file mode 100644 index 000000000..fabeda45b --- /dev/null +++ b/test/libyul/yulControlFlowGraph/ambiguous_names.yul @@ -0,0 +1,65 @@ +{ + a() + c() + function a() { + let x := 42 + sstore(x,x) + b() + function b() {} + } + function c() { + let x := 21 + mstore(x,x) + b() + function b() {} + } +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// a: [ RET[a] ] => [ ]\l\ +// c: [ RET[c] ] => [ ]\l\ +// "]; +// Block0Exit [label="MainExit"]; +// Block0 -> Block0Exit; +// +// FunctionEntry_a_1 [label="function a()"]; +// FunctionEntry_a_1 -> Block1; +// Block1 [label="\ +// Assignment(x): [ 0x2a ] => [ x ]\l\ +// sstore: [ x x ] => [ ]\l\ +// b: [ RET[b] ] => [ ]\l\ +// "]; +// Block1Exit [label="FunctionReturn[a]"]; +// Block1 -> Block1Exit; +// +// FunctionEntry_b_2 [label="function b()"]; +// FunctionEntry_b_2 -> Block2; +// Block2 [label="\ +// "]; +// Block2Exit [label="FunctionReturn[b]"]; +// Block2 -> Block2Exit; +// +// FunctionEntry_c_3 [label="function c()"]; +// FunctionEntry_c_3 -> Block3; +// Block3 [label="\ +// Assignment(x): [ 0x15 ] => [ x ]\l\ +// mstore: [ x x ] => [ ]\l\ +// b: [ RET[b] ] => [ ]\l\ +// "]; +// Block3Exit [label="FunctionReturn[c]"]; +// Block3 -> Block3Exit; +// +// FunctionEntry_b_4 [label="function b()"]; +// FunctionEntry_b_4 -> Block4; +// Block4 [label="\ +// "]; +// Block4Exit [label="FunctionReturn[b]"]; +// Block4 -> Block4Exit; +// +// } diff --git a/test/libyul/yulControlFlowGraph/break.yul b/test/libyul/yulControlFlowGraph/break.yul new file mode 100644 index 000000000..ceca7cce0 --- /dev/null +++ b/test/libyul/yulControlFlowGraph/break.yul @@ -0,0 +1,87 @@ +{ + sstore(0x01, 0x0101) + for { sstore(0x02, 0x0202) } sload(0x03) { sstore(0x04, 0x0404) } { + sstore(0x05, 0x0505) + if sload(0x06) { sstore(0x07,0x0707) break } + sstore(0x08, 0x0808) + if sload(0x09) { sstore(0x0A,0x0A0A) continue } + sstore(0x0B, 0x0B0B) + } + sstore(0x0C, 0x0C0C) +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// sstore: [ 0x0101 0x01 ] => [ ]\l\ +// sstore: [ 0x0202 0x02 ] => [ ]\l\ +// "]; +// Block0 -> Block0Exit [arrowhead=none]; +// Block0Exit [label="Jump" shape=oval]; +// Block0Exit -> Block1; +// +// Block1 [label="\ +// sload: [ 0x03 ] => [ TMP[sload, 0] ]\l\ +// "]; +// Block1 -> Block1Exit; +// Block1Exit [label="{ TMP[sload, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1Exit:0 -> Block2; +// Block1Exit:1 -> Block3; +// +// Block2 [label="\ +// sstore: [ 0x0c0c 0x0c ] => [ ]\l\ +// "]; +// Block2Exit [label="MainExit"]; +// Block2 -> Block2Exit; +// +// Block3 [label="\ +// sstore: [ 0x0505 0x05 ] => [ ]\l\ +// sload: [ 0x06 ] => [ TMP[sload, 0] ]\l\ +// "]; +// Block3 -> Block3Exit; +// Block3Exit [label="{ TMP[sload, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block3Exit:0 -> Block4; +// Block3Exit:1 -> Block5; +// +// Block4 [label="\ +// sstore: [ 0x0808 0x08 ] => [ ]\l\ +// sload: [ 0x09 ] => [ TMP[sload, 0] ]\l\ +// "]; +// Block4 -> Block4Exit; +// Block4Exit [label="{ TMP[sload, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block4Exit:0 -> Block6; +// Block4Exit:1 -> Block7; +// +// Block5 [label="\ +// sstore: [ 0x0707 0x07 ] => [ ]\l\ +// "]; +// Block5 -> Block5Exit [arrowhead=none]; +// Block5Exit [label="Jump" shape=oval]; +// Block5Exit -> Block2; +// +// Block6 [label="\ +// sstore: [ 0x0b0b 0x0b ] => [ ]\l\ +// "]; +// Block6 -> Block6Exit [arrowhead=none]; +// Block6Exit [label="Jump" shape=oval]; +// Block6Exit -> Block8; +// +// Block7 [label="\ +// sstore: [ 0x0a0a 0x0a ] => [ ]\l\ +// "]; +// Block7 -> Block7Exit [arrowhead=none]; +// Block7Exit [label="Jump" shape=oval]; +// Block7Exit -> Block8; +// +// Block8 [label="\ +// sstore: [ 0x0404 0x04 ] => [ ]\l\ +// "]; +// Block8 -> Block8Exit [arrowhead=none]; +// Block8Exit [label="BackwardsJump" shape=oval]; +// Block8Exit -> Block1; +// +// } diff --git a/test/libyul/yulControlFlowGraph/complex.yul b/test/libyul/yulControlFlowGraph/complex.yul new file mode 100644 index 000000000..4001a9309 --- /dev/null +++ b/test/libyul/yulControlFlowGraph/complex.yul @@ -0,0 +1,195 @@ +{ + function f(a, b) -> c { + for { let x := 42 } lt(x, a) { + x := add(x, 1) + if calldataload(x) + { + sstore(0, x) + leave + sstore(0x01, 0x0101) + } + sstore(0xFF, 0xFFFF) + } + { + switch mload(x) + case 0 { + sstore(0x02, 0x0202) + break + sstore(0x03, 0x0303) + } + case 1 { + sstore(0x04, 0x0404) + leave + sstore(0x05, 0x0505) + } + case 2 { + sstore(0x06, 0x0606) + revert(0, 0) + sstore(0x07, 0x0707) + } + case 3 { + sstore(0x08, 0x0808) + } + default { + if mload(b) { + return(0, 0) + sstore(0x09, 0x0909) + } + sstore(0x0A, 0x0A0A) + } + sstore(0x0B, 0x0B0B) + } + sstore(0x0C, 0x0C0C) + } + pop(f(1,2)) +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// f: [ RET[f] 0x02 0x01 ] => [ TMP[f, 0] ]\l\ +// pop: [ TMP[f, 0] ] => [ ]\l\ +// "]; +// Block0Exit [label="MainExit"]; +// Block0 -> Block0Exit; +// +// FunctionEntry_f_1 [label="function f(a, b) -> c"]; +// FunctionEntry_f_1 -> Block1; +// Block1 [label="\ +// Assignment(x): [ 0x2a ] => [ x ]\l\ +// "]; +// Block1 -> Block1Exit [arrowhead=none]; +// Block1Exit [label="Jump" shape=oval]; +// Block1Exit -> Block2; +// +// Block2 [label="\ +// lt: [ a x ] => [ TMP[lt, 0] ]\l\ +// "]; +// Block2 -> Block2Exit; +// Block2Exit [label="{ TMP[lt, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block2Exit:0 -> Block3; +// Block2Exit:1 -> Block4; +// +// Block3 [label="\ +// sstore: [ 0x0c0c 0x0c ] => [ ]\l\ +// "]; +// Block3Exit [label="FunctionReturn[f]"]; +// Block3 -> Block3Exit; +// +// Block4 [label="\ +// mload: [ x ] => [ TMP[mload, 0] ]\l\ +// Assignment(GHOST[0]): [ TMP[mload, 0] ] => [ GHOST[0] ]\l\ +// eq: [ GHOST[0] 0x00 ] => [ TMP[eq, 0] ]\l\ +// "]; +// Block4 -> Block4Exit; +// Block4Exit [label="{ TMP[eq, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block4Exit:0 -> Block5; +// Block4Exit:1 -> Block6; +// +// Block5 [label="\ +// eq: [ GHOST[0] 0x01 ] => [ TMP[eq, 0] ]\l\ +// "]; +// Block5 -> Block5Exit; +// Block5Exit [label="{ TMP[eq, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block5Exit:0 -> Block7; +// Block5Exit:1 -> Block8; +// +// Block6 [label="\ +// sstore: [ 0x0202 0x02 ] => [ ]\l\ +// "]; +// Block6 -> Block6Exit [arrowhead=none]; +// Block6Exit [label="Jump" shape=oval]; +// Block6Exit -> Block3; +// +// Block7 [label="\ +// eq: [ GHOST[0] 0x02 ] => [ TMP[eq, 0] ]\l\ +// "]; +// Block7 -> Block7Exit; +// Block7Exit [label="{ TMP[eq, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block7Exit:0 -> Block9; +// Block7Exit:1 -> Block10; +// +// Block8 [label="\ +// sstore: [ 0x0404 0x04 ] => [ ]\l\ +// "]; +// Block8Exit [label="FunctionReturn[f]"]; +// Block8 -> Block8Exit; +// +// Block9 [label="\ +// eq: [ GHOST[0] 0x03 ] => [ TMP[eq, 0] ]\l\ +// "]; +// Block9 -> Block9Exit; +// Block9Exit [label="{ TMP[eq, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block9Exit:0 -> Block11; +// Block9Exit:1 -> Block12; +// +// Block10 [label="\ +// sstore: [ 0x0606 0x06 ] => [ ]\l\ +// revert: [ 0x00 0x00 ] => [ ]\l\ +// "]; +// Block10Exit [label="Terminated"]; +// Block10 -> Block10Exit; +// +// Block11 [label="\ +// mload: [ b ] => [ TMP[mload, 0] ]\l\ +// "]; +// Block11 -> Block11Exit; +// Block11Exit [label="{ TMP[mload, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block11Exit:0 -> Block13; +// Block11Exit:1 -> Block14; +// +// Block12 [label="\ +// sstore: [ 0x0808 0x08 ] => [ ]\l\ +// "]; +// Block12 -> Block12Exit [arrowhead=none]; +// Block12Exit [label="Jump" shape=oval]; +// Block12Exit -> Block15; +// +// Block13 [label="\ +// sstore: [ 0x0a0a 0x0a ] => [ ]\l\ +// "]; +// Block13 -> Block13Exit [arrowhead=none]; +// Block13Exit [label="Jump" shape=oval]; +// Block13Exit -> Block15; +// +// Block14 [label="\ +// return: [ 0x00 0x00 ] => [ ]\l\ +// "]; +// Block14Exit [label="Terminated"]; +// Block14 -> Block14Exit; +// +// Block15 [label="\ +// sstore: [ 0x0b0b 0x0b ] => [ ]\l\ +// "]; +// Block15 -> Block15Exit [arrowhead=none]; +// Block15Exit [label="Jump" shape=oval]; +// Block15Exit -> Block16; +// +// Block16 [label="\ +// add: [ 0x01 x ] => [ TMP[add, 0] ]\l\ +// Assignment(x): [ TMP[add, 0] ] => [ x ]\l\ +// calldataload: [ x ] => [ TMP[calldataload, 0] ]\l\ +// "]; +// Block16 -> Block16Exit; +// Block16Exit [label="{ TMP[calldataload, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block16Exit:0 -> Block17; +// Block16Exit:1 -> Block18; +// +// Block17 [label="\ +// sstore: [ 0xffff 0xff ] => [ ]\l\ +// "]; +// Block17 -> Block17Exit [arrowhead=none]; +// Block17Exit [label="BackwardsJump" shape=oval]; +// Block17Exit -> Block2; +// +// Block18 [label="\ +// sstore: [ x 0x00 ] => [ ]\l\ +// "]; +// Block18Exit [label="FunctionReturn[f]"]; +// Block18 -> Block18Exit; +// +// } diff --git a/test/libyul/yulControlFlowGraph/for.yul b/test/libyul/yulControlFlowGraph/for.yul new file mode 100644 index 000000000..52658a128 --- /dev/null +++ b/test/libyul/yulControlFlowGraph/for.yul @@ -0,0 +1,51 @@ +{ + sstore(0x01, 0x0101) + for { sstore(0x02, 0x0202) } sload(0x03) { sstore(0x04, 0x0404) } { + sstore(0x05, 0x0505) + } + sstore(0x06, 0x0506) +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// sstore: [ 0x0101 0x01 ] => [ ]\l\ +// sstore: [ 0x0202 0x02 ] => [ ]\l\ +// "]; +// Block0 -> Block0Exit [arrowhead=none]; +// Block0Exit [label="Jump" shape=oval]; +// Block0Exit -> Block1; +// +// Block1 [label="\ +// sload: [ 0x03 ] => [ TMP[sload, 0] ]\l\ +// "]; +// Block1 -> Block1Exit; +// Block1Exit [label="{ TMP[sload, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1Exit:0 -> Block2; +// Block1Exit:1 -> Block3; +// +// Block2 [label="\ +// sstore: [ 0x0506 0x06 ] => [ ]\l\ +// "]; +// Block2Exit [label="MainExit"]; +// Block2 -> Block2Exit; +// +// Block3 [label="\ +// sstore: [ 0x0505 0x05 ] => [ ]\l\ +// "]; +// Block3 -> Block3Exit [arrowhead=none]; +// Block3Exit [label="Jump" shape=oval]; +// Block3Exit -> Block4; +// +// Block4 [label="\ +// sstore: [ 0x0404 0x04 ] => [ ]\l\ +// "]; +// Block4 -> Block4Exit [arrowhead=none]; +// Block4Exit [label="BackwardsJump" shape=oval]; +// Block4Exit -> Block1; +// +// } diff --git a/test/libyul/yulControlFlowGraph/function.yul b/test/libyul/yulControlFlowGraph/function.yul new file mode 100644 index 000000000..862b1b9e6 --- /dev/null +++ b/test/libyul/yulControlFlowGraph/function.yul @@ -0,0 +1,75 @@ +{ + function f(a, b) -> r { + let x := add(a,b) + r := sub(x,a) + } + function g() { + sstore(0x01, 0x0101) + } + function h(x) { + h(f(x, 0)) + g() + } + function i() -> v, w { + v := 0x0202 + w := 0x0303 + } + let x, y := i() + h(x) + h(y) +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// i: [ RET[i] ] => [ TMP[i, 0] TMP[i, 1] ]\l\ +// Assignment(x, y): [ TMP[i, 0] TMP[i, 1] ] => [ x y ]\l\ +// h: [ RET[h] x ] => [ ]\l\ +// h: [ RET[h] y ] => [ ]\l\ +// "]; +// Block0Exit [label="MainExit"]; +// Block0 -> Block0Exit; +// +// FunctionEntry_f_1 [label="function f(a, b) -> r"]; +// FunctionEntry_f_1 -> Block1; +// Block1 [label="\ +// add: [ b a ] => [ TMP[add, 0] ]\l\ +// Assignment(x): [ TMP[add, 0] ] => [ x ]\l\ +// sub: [ a x ] => [ TMP[sub, 0] ]\l\ +// Assignment(r): [ TMP[sub, 0] ] => [ r ]\l\ +// "]; +// Block1Exit [label="FunctionReturn[f]"]; +// Block1 -> Block1Exit; +// +// FunctionEntry_g_2 [label="function g()"]; +// FunctionEntry_g_2 -> Block2; +// Block2 [label="\ +// sstore: [ 0x0101 0x01 ] => [ ]\l\ +// "]; +// Block2Exit [label="FunctionReturn[g]"]; +// Block2 -> Block2Exit; +// +// FunctionEntry_h_3 [label="function h(x)"]; +// FunctionEntry_h_3 -> Block3; +// Block3 [label="\ +// f: [ RET[f] 0x00 x ] => [ TMP[f, 0] ]\l\ +// h: [ RET[h] TMP[f, 0] ] => [ ]\l\ +// g: [ RET[g] ] => [ ]\l\ +// "]; +// Block3Exit [label="FunctionReturn[h]"]; +// Block3 -> Block3Exit; +// +// FunctionEntry_i_4 [label="function i() -> v, w"]; +// FunctionEntry_i_4 -> Block4; +// Block4 [label="\ +// Assignment(v): [ 0x0202 ] => [ v ]\l\ +// Assignment(w): [ 0x0303 ] => [ w ]\l\ +// "]; +// Block4Exit [label="FunctionReturn[i]"]; +// Block4 -> Block4Exit; +// +// } diff --git a/test/libyul/yulControlFlowGraph/if.yul b/test/libyul/yulControlFlowGraph/if.yul new file mode 100644 index 000000000..a313c53ee --- /dev/null +++ b/test/libyul/yulControlFlowGraph/if.yul @@ -0,0 +1,37 @@ +{ + sstore(0x01, 0x0101) + if calldataload(0) { + sstore(0x02, 0x0202) + } + sstore(0x03, 0x003) +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// sstore: [ 0x0101 0x01 ] => [ ]\l\ +// calldataload: [ 0x00 ] => [ TMP[calldataload, 0] ]\l\ +// "]; +// Block0 -> Block0Exit; +// Block0Exit [label="{ TMP[calldataload, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block0Exit:0 -> Block1; +// Block0Exit:1 -> Block2; +// +// Block1 [label="\ +// sstore: [ 0x03 0x03 ] => [ ]\l\ +// "]; +// Block1Exit [label="MainExit"]; +// Block1 -> Block1Exit; +// +// Block2 [label="\ +// sstore: [ 0x0202 0x02 ] => [ ]\l\ +// "]; +// Block2 -> Block2Exit [arrowhead=none]; +// Block2Exit [label="Jump" shape=oval]; +// Block2Exit -> Block1; +// +// } diff --git a/test/libyul/yulControlFlowGraph/leave.yul b/test/libyul/yulControlFlowGraph/leave.yul new file mode 100644 index 000000000..4d0a63af4 --- /dev/null +++ b/test/libyul/yulControlFlowGraph/leave.yul @@ -0,0 +1,51 @@ +{ + function f(a, b) -> c { + sstore(0x01, 0x0101) + if lt(a,b) { + sstore(0x02, 0x0202) + leave + sstore(0x03, 0x0303) + } + sstore(0x04, 0x0404) + } + + pop(f(0,1)) +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// f: [ RET[f] 0x01 0x00 ] => [ TMP[f, 0] ]\l\ +// pop: [ TMP[f, 0] ] => [ ]\l\ +// "]; +// Block0Exit [label="MainExit"]; +// Block0 -> Block0Exit; +// +// FunctionEntry_f_1 [label="function f(a, b) -> c"]; +// FunctionEntry_f_1 -> Block1; +// Block1 [label="\ +// sstore: [ 0x0101 0x01 ] => [ ]\l\ +// lt: [ b a ] => [ TMP[lt, 0] ]\l\ +// "]; +// Block1 -> Block1Exit; +// Block1Exit [label="{ TMP[lt, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1Exit:0 -> Block2; +// Block1Exit:1 -> Block3; +// +// Block2 [label="\ +// sstore: [ 0x0404 0x04 ] => [ ]\l\ +// "]; +// Block2Exit [label="FunctionReturn[f]"]; +// Block2 -> Block2Exit; +// +// Block3 [label="\ +// sstore: [ 0x0202 0x02 ] => [ ]\l\ +// "]; +// Block3Exit [label="FunctionReturn[f]"]; +// Block3 -> Block3Exit; +// +// } diff --git a/test/libyul/yulControlFlowGraph/nested_loop_complex.yul b/test/libyul/yulControlFlowGraph/nested_loop_complex.yul new file mode 100644 index 000000000..ea9aedf76 --- /dev/null +++ b/test/libyul/yulControlFlowGraph/nested_loop_complex.yul @@ -0,0 +1,193 @@ +{ + for { let x := 0 } lt(x, 0x0101) { + sstore(x, 0x0202) + for { let y := 0 } lt(y, 0x0303) { y := add(y, 0x0404) } { + sstore(y, 0x0505) + } + x := add(x, 0x0202) + } + { + sstore(0x0606, 0x0606) + if sload(0x0707) { continue } + sstore(0x0808, 0x0808) + if sload(0x0909) { break } + sstore(0x0A0A, 0x0B0B) + for { let z := 0 } lt(z, 0x0C0C) { z := add(z, 1) } { + sstore(0x0D0D, 0x0D0D) + if sload(0x0E0E) { + continue + } + sstore(0x0F0F, 0x0F0F) + if sload(0x1010) { + break + } + sstore(0x1111, 0x1111) + } + sstore(0x1212, 0x1212) + } +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// Assignment(x): [ 0x00 ] => [ x ]\l\ +// "]; +// Block0 -> Block0Exit [arrowhead=none]; +// Block0Exit [label="Jump" shape=oval]; +// Block0Exit -> Block1; +// +// Block1 [label="\ +// lt: [ 0x0101 x ] => [ TMP[lt, 0] ]\l\ +// "]; +// Block1 -> Block1Exit; +// Block1Exit [label="{ TMP[lt, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1Exit:0 -> Block2; +// Block1Exit:1 -> Block3; +// +// Block2 [label="\ +// "]; +// Block2Exit [label="MainExit"]; +// Block2 -> Block2Exit; +// +// Block3 [label="\ +// sstore: [ 0x0606 0x0606 ] => [ ]\l\ +// sload: [ 0x0707 ] => [ TMP[sload, 0] ]\l\ +// "]; +// Block3 -> Block3Exit; +// Block3Exit [label="{ TMP[sload, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block3Exit:0 -> Block4; +// Block3Exit:1 -> Block5; +// +// Block4 [label="\ +// sstore: [ 0x0808 0x0808 ] => [ ]\l\ +// sload: [ 0x0909 ] => [ TMP[sload, 0] ]\l\ +// "]; +// Block4 -> Block4Exit; +// Block4Exit [label="{ TMP[sload, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block4Exit:0 -> Block6; +// Block4Exit:1 -> Block7; +// +// Block5 [label="\ +// "]; +// Block5 -> Block5Exit [arrowhead=none]; +// Block5Exit [label="Jump" shape=oval]; +// Block5Exit -> Block8; +// +// Block6 [label="\ +// sstore: [ 0x0b0b 0x0a0a ] => [ ]\l\ +// Assignment(z): [ 0x00 ] => [ z ]\l\ +// "]; +// Block6 -> Block6Exit [arrowhead=none]; +// Block6Exit [label="Jump" shape=oval]; +// Block6Exit -> Block9; +// +// Block7 [label="\ +// "]; +// Block7 -> Block7Exit [arrowhead=none]; +// Block7Exit [label="Jump" shape=oval]; +// Block7Exit -> Block2; +// +// Block8 [label="\ +// sstore: [ 0x0202 x ] => [ ]\l\ +// Assignment(y): [ 0x00 ] => [ y ]\l\ +// "]; +// Block8 -> Block8Exit [arrowhead=none]; +// Block8Exit [label="Jump" shape=oval]; +// Block8Exit -> Block10; +// +// Block9 [label="\ +// lt: [ 0x0c0c z ] => [ TMP[lt, 0] ]\l\ +// "]; +// Block9 -> Block9Exit; +// Block9Exit [label="{ TMP[lt, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block9Exit:0 -> Block11; +// Block9Exit:1 -> Block12; +// +// Block10 [label="\ +// lt: [ 0x0303 y ] => [ TMP[lt, 0] ]\l\ +// "]; +// Block10 -> Block10Exit; +// Block10Exit [label="{ TMP[lt, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block10Exit:0 -> Block13; +// Block10Exit:1 -> Block14; +// +// Block11 [label="\ +// sstore: [ 0x1212 0x1212 ] => [ ]\l\ +// "]; +// Block11 -> Block11Exit [arrowhead=none]; +// Block11Exit [label="Jump" shape=oval]; +// Block11Exit -> Block8; +// +// Block12 [label="\ +// sstore: [ 0x0d0d 0x0d0d ] => [ ]\l\ +// sload: [ 0x0e0e ] => [ TMP[sload, 0] ]\l\ +// "]; +// Block12 -> Block12Exit; +// Block12Exit [label="{ TMP[sload, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block12Exit:0 -> Block15; +// Block12Exit:1 -> Block16; +// +// Block13 [label="\ +// add: [ 0x0202 x ] => [ TMP[add, 0] ]\l\ +// Assignment(x): [ TMP[add, 0] ] => [ x ]\l\ +// "]; +// Block13 -> Block13Exit [arrowhead=none]; +// Block13Exit [label="BackwardsJump" shape=oval]; +// Block13Exit -> Block1; +// +// Block14 [label="\ +// sstore: [ 0x0505 y ] => [ ]\l\ +// "]; +// Block14 -> Block14Exit [arrowhead=none]; +// Block14Exit [label="Jump" shape=oval]; +// Block14Exit -> Block17; +// +// Block15 [label="\ +// sstore: [ 0x0f0f 0x0f0f ] => [ ]\l\ +// sload: [ 0x1010 ] => [ TMP[sload, 0] ]\l\ +// "]; +// Block15 -> Block15Exit; +// Block15Exit [label="{ TMP[sload, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block15Exit:0 -> Block18; +// Block15Exit:1 -> Block19; +// +// Block16 [label="\ +// "]; +// Block16 -> Block16Exit [arrowhead=none]; +// Block16Exit [label="Jump" shape=oval]; +// Block16Exit -> Block20; +// +// Block17 [label="\ +// add: [ 0x0404 y ] => [ TMP[add, 0] ]\l\ +// Assignment(y): [ TMP[add, 0] ] => [ y ]\l\ +// "]; +// Block17 -> Block17Exit [arrowhead=none]; +// Block17Exit [label="BackwardsJump" shape=oval]; +// Block17Exit -> Block10; +// +// Block18 [label="\ +// sstore: [ 0x1111 0x1111 ] => [ ]\l\ +// "]; +// Block18 -> Block18Exit [arrowhead=none]; +// Block18Exit [label="Jump" shape=oval]; +// Block18Exit -> Block20; +// +// Block19 [label="\ +// "]; +// Block19 -> Block19Exit [arrowhead=none]; +// Block19Exit [label="Jump" shape=oval]; +// Block19Exit -> Block11; +// +// Block20 [label="\ +// add: [ 0x01 z ] => [ TMP[add, 0] ]\l\ +// Assignment(z): [ TMP[add, 0] ] => [ z ]\l\ +// "]; +// Block20 -> Block20Exit [arrowhead=none]; +// Block20Exit [label="BackwardsJump" shape=oval]; +// Block20Exit -> Block9; +// +// } diff --git a/test/libyul/yulControlFlowGraph/stub.yul b/test/libyul/yulControlFlowGraph/stub.yul new file mode 100644 index 000000000..4ec901619 --- /dev/null +++ b/test/libyul/yulControlFlowGraph/stub.yul @@ -0,0 +1,15 @@ +{ +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// "]; +// Block0Exit [label="MainExit"]; +// Block0 -> Block0Exit; +// +// } diff --git a/test/libyul/yulControlFlowGraph/switch.yul b/test/libyul/yulControlFlowGraph/switch.yul new file mode 100644 index 000000000..2d6751d8a --- /dev/null +++ b/test/libyul/yulControlFlowGraph/switch.yul @@ -0,0 +1,68 @@ +{ + sstore(0, 0) + switch sload(0) + case 0 { + sstore(0x01, 0x0101) + } + case 1 { + sstore(0x02, 0x0101) + } + default { + sstore(0x03, 0x0101) + } + sstore(0x04, 0x0101) +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// sstore: [ 0x00 0x00 ] => [ ]\l\ +// sload: [ 0x00 ] => [ TMP[sload, 0] ]\l\ +// Assignment(GHOST[0]): [ TMP[sload, 0] ] => [ GHOST[0] ]\l\ +// eq: [ GHOST[0] 0x00 ] => [ TMP[eq, 0] ]\l\ +// "]; +// Block0 -> Block0Exit; +// Block0Exit [label="{ TMP[eq, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block0Exit:0 -> Block1; +// Block0Exit:1 -> Block2; +// +// Block1 [label="\ +// eq: [ GHOST[0] 0x01 ] => [ TMP[eq, 0] ]\l\ +// "]; +// Block1 -> Block1Exit; +// Block1Exit [label="{ TMP[eq, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1Exit:0 -> Block3; +// Block1Exit:1 -> Block4; +// +// Block2 [label="\ +// sstore: [ 0x0101 0x01 ] => [ ]\l\ +// "]; +// Block2 -> Block2Exit [arrowhead=none]; +// Block2Exit [label="Jump" shape=oval]; +// Block2Exit -> Block5; +// +// Block3 [label="\ +// sstore: [ 0x0101 0x03 ] => [ ]\l\ +// "]; +// Block3 -> Block3Exit [arrowhead=none]; +// Block3Exit [label="Jump" shape=oval]; +// Block3Exit -> Block5; +// +// Block4 [label="\ +// sstore: [ 0x0101 0x02 ] => [ ]\l\ +// "]; +// Block4 -> Block4Exit [arrowhead=none]; +// Block4Exit [label="Jump" shape=oval]; +// Block4Exit -> Block5; +// +// Block5 [label="\ +// sstore: [ 0x0101 0x04 ] => [ ]\l\ +// "]; +// Block5Exit [label="MainExit"]; +// Block5 -> Block5Exit; +// +// } diff --git a/test/libyul/yulControlFlowGraph/variables.yul b/test/libyul/yulControlFlowGraph/variables.yul new file mode 100644 index 000000000..ecd274054 --- /dev/null +++ b/test/libyul/yulControlFlowGraph/variables.yul @@ -0,0 +1,31 @@ +{ + let x := calldataload(0) + let y := calldataload(2) + + x := calldataload(3) + y := calldataload(4) + + sstore(x,y) +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// calldataload: [ 0x00 ] => [ TMP[calldataload, 0] ]\l\ +// Assignment(x): [ TMP[calldataload, 0] ] => [ x ]\l\ +// calldataload: [ 0x02 ] => [ TMP[calldataload, 0] ]\l\ +// Assignment(y): [ TMP[calldataload, 0] ] => [ y ]\l\ +// calldataload: [ 0x03 ] => [ TMP[calldataload, 0] ]\l\ +// Assignment(x): [ TMP[calldataload, 0] ] => [ x ]\l\ +// calldataload: [ 0x04 ] => [ TMP[calldataload, 0] ]\l\ +// Assignment(y): [ TMP[calldataload, 0] ] => [ y ]\l\ +// sstore: [ y x ] => [ ]\l\ +// "]; +// Block0Exit [label="MainExit"]; +// Block0 -> Block0Exit; +// +// } diff --git a/test/libyul/yulControlFlowGraph/verbatim.yul b/test/libyul/yulControlFlowGraph/verbatim.yul new file mode 100644 index 000000000..05a4cc034 --- /dev/null +++ b/test/libyul/yulControlFlowGraph/verbatim.yul @@ -0,0 +1,26 @@ +{ + let a_1 := 42 + let a_2 := 23 + let a_3 := 1 + let b := verbatim_10i_1o("test", a_1, a_2, a_3, 2, 3, 4, 5, 6, 7, 8) + sstore(b,b) +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// Assignment(a_1): [ 0x2a ] => [ a_1 ]\l\ +// Assignment(a_2): [ 0x17 ] => [ a_2 ]\l\ +// Assignment(a_3): [ 0x01 ] => [ a_3 ]\l\ +// verbatim_10i_1o: [ 0x08 0x07 0x06 0x05 0x04 0x03 0x02 a_3 a_2 a_1 ] => [ TMP[verbatim_10i_1o, 0] ]\l\ +// Assignment(b): [ TMP[verbatim_10i_1o, 0] ] => [ b ]\l\ +// sstore: [ b b ] => [ ]\l\ +// "]; +// Block0Exit [label="MainExit"]; +// Block0 -> Block0Exit; +// +// } diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index 95eb3a636..1f62b57fc 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -32,6 +32,7 @@ add_executable(isoltest ../libsolidity/ASTJSONTest.cpp ../libsolidity/SMTCheckerTest.cpp ../libyul/Common.cpp + ../libyul/ControlFlowGraphTest.cpp ../libyul/EVMCodeTransformTest.cpp ../libyul/EwasmTranslationTest.cpp ../libyul/FunctionSideEffects.cpp @@ -41,4 +42,5 @@ add_executable(isoltest ../libyul/YulOptimizerTestCommon.cpp ../libyul/YulInterpreterTest.cpp ) -target_link_libraries(isoltest PRIVATE evmc libsolc solidity yulInterpreter evmasm Boost::boost Boost::program_options Boost::unit_test_framework) +target_compile_definitions(isoltest PRIVATE ISOLTEST) +target_link_libraries(isoltest PRIVATE evmc libsolc solidity yulInterpreter evmasm Boost::boost Boost::program_options Boost::unit_test_framework Threads::Threads) From 20e23171dad2379772305bfbcbe9d9f82a6b74a8 Mon Sep 17 00:00:00 2001 From: Leo Alt Date: Thu, 15 Jul 2021 12:13:04 +0200 Subject: [PATCH 53/98] Update tests to z3 4.8.12 --- .../smtCheckerTests/abi/abi_encode_string_literal.sol | 4 +--- .../abi/abi_encode_with_selector_vs_sig.sol | 3 ++- .../array_members/push_storage_ref_unsafe_aliasing.sol | 2 +- .../smtCheckerTests/crypto/crypto_functions_fail.sol | 8 ++------ .../external_calls/external_call_from_constructor_3.sol | 2 +- .../file_level/constants_at_file_level_referencing.sol | 2 +- .../smtCheckerTests/file_level/free_function_1.sol | 2 +- .../smtCheckerTests/file_level/libraries_from_free.sol | 2 +- test/libsolidity/smtCheckerTests/file_level/recursion.sol | 6 ++++-- .../smtCheckerTests/functions/getters/bytes.sol | 4 +--- .../smtCheckerTests/functions/getters/string.sol | 4 +--- .../smtCheckerTests/loops/while_nested_break_fail.sol | 2 +- .../abstract_function_nondet_pow_no_abstraction.sol | 2 -- .../natspec/safe_assert_false_positive_pure.sol | 2 +- .../smtCheckerTests/overflow/simple_overflow.sol | 2 +- .../smtCheckerTests/overflow/unsigned_mul_overflow.sol | 2 +- .../smtCheckerTests/special/abi_decode_simple.sol | 2 +- test/libsolidity/smtCheckerTests/special/blockhash.sol | 2 +- test/libsolidity/smtCheckerTests/special/event.sol | 2 +- test/libsolidity/smtCheckerTests/special/msg_data.sol | 2 +- .../try_catch/try_multiple_catch_clauses_2.sol | 2 +- .../smtCheckerTests/types/array_aliasing_storage_5.sol | 1 + .../struct/struct_array_struct_array_storage_unsafe_1.sol | 6 +++--- 23 files changed, 29 insertions(+), 37 deletions(-) diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_string_literal.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_string_literal.sol index 3b2006215..6eb3fff73 100644 --- a/test/libsolidity/smtCheckerTests/abi/abi_encode_string_literal.sol +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_string_literal.sol @@ -19,15 +19,13 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 1218: (208-238): CHC: Error trying to invoke SMT solver. -// Warning 6328: (208-238): CHC: Assertion violation might happen here. +// Warning 6328: (208-238): CHC: Assertion violation happens here. // Warning 1218: (286-316): CHC: Error trying to invoke SMT solver. // Warning 6328: (286-316): CHC: Assertion violation might happen here. // Warning 1218: (453-483): CHC: Error trying to invoke SMT solver. // Warning 6328: (453-483): CHC: Assertion violation might happen here. // Warning 1218: (532-562): CHC: Error trying to invoke SMT solver. // Warning 6328: (532-562): CHC: Assertion violation might happen here. -// Warning 4661: (208-238): BMC: Assertion violation happens here. // Warning 4661: (286-316): BMC: Assertion violation happens here. // Warning 4661: (453-483): BMC: Assertion violation happens here. // Warning 4661: (532-562): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_vs_sig.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_vs_sig.sol index b97f28955..b5346b049 100644 --- a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_vs_sig.sol +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_vs_sig.sol @@ -9,4 +9,5 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (294-324): CHC: Assertion violation happens here. +// Warning 6328: (294-324): CHC: Assertion violation might happen here. +// Warning 7812: (294-324): BMC: Assertion violation might happen here. diff --git a/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_unsafe_aliasing.sol b/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_unsafe_aliasing.sol index d51a5f9e2..402ea67b4 100644 --- a/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_unsafe_aliasing.sol +++ b/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_unsafe_aliasing.sol @@ -14,5 +14,5 @@ contract C { // SMTEngine: all // ---- // Warning 6368: (188-192): CHC: Out of bounds access happens here.\nCounterexample:\na = []\nb = [32]\n\nTransaction trace:\nC.constructor()\nState: a = []\nC.f() -// Warning 6368: (188-195): CHC: Out of bounds access happens here.\nCounterexample:\na = [[], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15]]\nb = [32]\n\nTransaction trace:\nC.constructor()\nState: a = []\nC.f() +// Warning 6368: (188-195): CHC: Out of bounds access happens here.\nCounterexample:\na = [[], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [25, 25, 25, 25, 25, 25, 25, 25], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15]]\nb = [32]\n\nTransaction trace:\nC.constructor()\nState: a = []\nC.f() // Warning 6328: (181-202): CHC: Assertion violation happens here.\nCounterexample:\n\nb = [32]\n\nTransaction trace:\nC.constructor()\nState: a = []\nC.f() diff --git a/test/libsolidity/smtCheckerTests/crypto/crypto_functions_fail.sol b/test/libsolidity/smtCheckerTests/crypto/crypto_functions_fail.sol index 76868fa2b..f7104caf5 100644 --- a/test/libsolidity/smtCheckerTests/crypto/crypto_functions_fail.sol +++ b/test/libsolidity/smtCheckerTests/crypto/crypto_functions_fail.sol @@ -27,10 +27,6 @@ contract C { // ---- // Warning 2072: (556-566): Unused local variable. // Warning 2072: (598-608): Unused local variable. -// Warning 1218: (135-151): CHC: Error trying to invoke SMT solver. -// Warning 6328: (135-151): CHC: Assertion violation might happen here. +// Warning 6328: (135-151): CHC: Assertion violation happens here. // Warning 6328: (272-288): CHC: Assertion violation happens here. -// Warning 1218: (415-431): CHC: Error trying to invoke SMT solver. -// Warning 6328: (415-431): CHC: Assertion violation might happen here. -// Warning 4661: (135-151): BMC: Assertion violation happens here. -// Warning 4661: (415-431): BMC: Assertion violation happens here. +// Warning 6328: (415-431): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_from_constructor_3.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_from_constructor_3.sol index eb5cd97dd..9d27d6d3b 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/external_call_from_constructor_3.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_from_constructor_3.sol @@ -20,4 +20,4 @@ contract C { // SMTEngine: all // ---- // Warning 6328: (69-85): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 100\n = 0\n\nTransaction trace:\nState.constructor()\nState.f(100) -// Warning 6328: (203-217): CHC: Assertion violation happens here.\nCounterexample:\ns = 0, z = 3\n\nTransaction trace:\nC.constructor()\nState: s = 0, z = 3\nC.f() +// Warning 6328: (203-217): CHC: Assertion violation happens here.\nCounterexample:\ns = 0, z = 0\n\nTransaction trace:\nC.constructor()\nState: s = 0, z = 0\nC.f() diff --git a/test/libsolidity/smtCheckerTests/file_level/constants_at_file_level_referencing.sol b/test/libsolidity/smtCheckerTests/file_level/constants_at_file_level_referencing.sol index 4082885b9..2af325964 100644 --- a/test/libsolidity/smtCheckerTests/file_level/constants_at_file_level_referencing.sol +++ b/test/libsolidity/smtCheckerTests/file_level/constants_at_file_level_referencing.sol @@ -58,7 +58,7 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (s2.sol:518-539): CHC: Assertion violation happens here.\nCounterexample:\n\nr1 = [3, 1, 2]\nr2 = []\nr3 = []\nz = 0\n\nTransaction trace:\nC.constructor()\nC.p()\n C.f() -- internal call +// Warning 6328: (s2.sol:518-539): CHC: Assertion violation happens here.\nCounterexample:\n\nr2 = []\nr3 = []\nz = 0\n\nTransaction trace:\nC.constructor()\nC.p()\n C.f() -- internal call // Warning 6328: (s2.sol:704-725): CHC: Assertion violation happens here.\nCounterexample:\n\nr1 = [3, 1, 2]\nr2 = [3, 1, 2]\nr3 = []\nz = 0\n\nTransaction trace:\nC.constructor()\nC.p()\n C.f() -- internal call\n C.g() -- internal call // Warning 6328: (s2.sol:890-911): CHC: Assertion violation happens here. // Warning 6328: (s2.sol:980-994): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/file_level/free_function_1.sol b/test/libsolidity/smtCheckerTests/file_level/free_function_1.sol index 1da78ef51..87d565327 100644 --- a/test/libsolidity/smtCheckerTests/file_level/free_function_1.sol +++ b/test/libsolidity/smtCheckerTests/file_level/free_function_1.sol @@ -12,4 +12,4 @@ function fun(uint[] calldata _x, uint[] storage _y) view returns (uint, uint[] // SMTEngine: all // ---- // Warning 4984: (168-180): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\ndata = []\nx = 1\n = 0\n = 0\na = 0\n\nTransaction trace:\nC.constructor()\nState: data = []\nC.f(1, input)\n fun(_x, []) -- internal call -// Warning 6368: (289-294): CHC: Out of bounds access happens here.\nCounterexample:\ndata = []\nx = 0\ninput = [5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 13, 5, 5, 5, 5, 5, 19, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]\n = 0\n = 0\n\nTransaction trace:\nC.constructor()\nState: data = []\nC.f(0, [5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 13, 5, 5, 5, 5, 5, 19, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5])\n fun([5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 13, 5, 5, 5, 5, 5, 19, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5], []) -- internal call +// Warning 6368: (289-294): CHC: Out of bounds access happens here.\nCounterexample:\ndata = []\nx = 0\ninput = [5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 16, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]\n = 0\n = 0\n\nTransaction trace:\nC.constructor()\nState: data = []\nC.f(0, [5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 16, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5])\n fun([5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 16, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5], []) -- internal call diff --git a/test/libsolidity/smtCheckerTests/file_level/libraries_from_free.sol b/test/libsolidity/smtCheckerTests/file_level/libraries_from_free.sol index 9f0104c21..728f9f69f 100644 --- a/test/libsolidity/smtCheckerTests/file_level/libraries_from_free.sol +++ b/test/libsolidity/smtCheckerTests/file_level/libraries_from_free.sol @@ -23,6 +23,6 @@ contract C { // ---- // Warning 4588: (190-197): Assertion checker does not yet implement this type of function call. // Warning 4588: (190-197): Assertion checker does not yet implement this type of function call. -// Warning 6328: (284-298): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 8\ny = 8\n\nTransaction trace:\nC.constructor()\nC.f()\n fu() -- internal call\n L.inter() -- internal call +// Warning 6328: (284-298): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\ny = 8\n\nTransaction trace:\nC.constructor()\nC.f()\n fu() -- internal call\n L.inter() -- internal call // Warning 6328: (363-377): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\ny = 8\n\nTransaction trace:\nC.constructor()\nC.f()\n fu() -- internal call\n L.inter() -- internal call // Warning 4588: (190-197): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/file_level/recursion.sol b/test/libsolidity/smtCheckerTests/file_level/recursion.sol index ae3e25e08..a10087ba5 100644 --- a/test/libsolidity/smtCheckerTests/file_level/recursion.sol +++ b/test/libsolidity/smtCheckerTests/file_level/recursion.sol @@ -26,14 +26,16 @@ contract C { // Warning 4281: (118-130): CHC: Division by zero might happen here. // Warning 4984: (134-148): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. // Warning 4984: (176-189): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. -// Warning 6328: (430-450): CHC: Assertion violation happens here. +// Warning 6328: (430-450): CHC: Assertion violation might happen here. +// Warning 6328: (454-474): CHC: Assertion violation might happen here. // Warning 6328: (478-498): CHC: Assertion violation might happen here. // Warning 6328: (502-527): CHC: Assertion violation might happen here. // Warning 2661: (134-148): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. // Warning 2661: (176-189): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. // Warning 2661: (134-148): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 4661: (454-474): BMC: Assertion violation happens here. // Warning 2661: (134-148): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. -// Warning 8065: (176-189): BMC: Overflow (resulting value larger than 2**256 - 1) might happen here. +// Warning 2661: (176-189): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. // Warning 4661: (478-498): BMC: Assertion violation happens here. // Warning 2661: (134-148): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. // Warning 4661: (502-527): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/getters/bytes.sol b/test/libsolidity/smtCheckerTests/functions/getters/bytes.sol index 01dec8bc1..875cd5455 100644 --- a/test/libsolidity/smtCheckerTests/functions/getters/bytes.sol +++ b/test/libsolidity/smtCheckerTests/functions/getters/bytes.sol @@ -10,6 +10,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 1218: (162-201): CHC: Error trying to invoke SMT solver. -// Warning 6328: (162-201): CHC: Assertion violation might happen here. -// Warning 4661: (162-201): BMC: Assertion violation happens here. +// Warning 6328: (162-201): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/getters/string.sol b/test/libsolidity/smtCheckerTests/functions/getters/string.sol index 2f2319a16..c856ad514 100644 --- a/test/libsolidity/smtCheckerTests/functions/getters/string.sol +++ b/test/libsolidity/smtCheckerTests/functions/getters/string.sol @@ -10,6 +10,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 1218: (178-224): CHC: Error trying to invoke SMT solver. -// Warning 6328: (178-224): CHC: Assertion violation might happen here. -// Warning 4661: (178-224): BMC: Assertion violation happens here. +// Warning 6328: (178-224): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/loops/while_nested_break_fail.sol b/test/libsolidity/smtCheckerTests/loops/while_nested_break_fail.sol index ecd34cf3a..5a1bc9395 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_nested_break_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_nested_break_fail.sol @@ -30,4 +30,4 @@ contract C // SMTEngine: all // ---- // Warning 6328: (296-311): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\ny = 10\nb = false\nc = true\n\nTransaction trace:\nC.constructor()\nC.f(0, 9, false, true) -// Warning 6328: (347-362): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 15\ny = 20\nb = false\nc = false\n\nTransaction trace:\nC.constructor()\nC.f(0, 0, false, false) +// Warning 6328: (347-362): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 15\ny = 0\nb = true\nc = false\n\nTransaction trace:\nC.constructor()\nC.f(9, 0, true, false) diff --git a/test/libsolidity/smtCheckerTests/natspec/abstract_function_nondet_pow_no_abstraction.sol b/test/libsolidity/smtCheckerTests/natspec/abstract_function_nondet_pow_no_abstraction.sol index f3a6581ca..7ce8b9c06 100644 --- a/test/libsolidity/smtCheckerTests/natspec/abstract_function_nondet_pow_no_abstraction.sol +++ b/test/libsolidity/smtCheckerTests/natspec/abstract_function_nondet_pow_no_abstraction.sol @@ -33,11 +33,9 @@ contract C { // ==== // SMTEngine: chc // ---- -// Warning 4984: (176-179): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. // Warning 4281: (435-447): CHC: Division by zero might happen here. // Warning 4984: (467-478): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. // Warning 4281: (495-507): CHC: Division by zero might happen here. // Warning 4984: (529-537): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. // Warning 4984: (550-561): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. -// Warning 3944: (579-591): CHC: Underflow (resulting value less than 0) might happen here. // Warning 4984: (616-624): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. diff --git a/test/libsolidity/smtCheckerTests/natspec/safe_assert_false_positive_pure.sol b/test/libsolidity/smtCheckerTests/natspec/safe_assert_false_positive_pure.sol index 03dd24475..464d9834a 100644 --- a/test/libsolidity/smtCheckerTests/natspec/safe_assert_false_positive_pure.sol +++ b/test/libsolidity/smtCheckerTests/natspec/safe_assert_false_positive_pure.sol @@ -26,4 +26,4 @@ contract C { // ---- // Warning 2018: (33-335): Function state mutability can be restricted to view // Warning 2018: (457-524): Function state mutability can be restricted to pure -// Warning 6328: (135-150): CHC: Assertion violation happens here.\nCounterexample:\nx = 0, y = 0\n_x = 0\nz = 1\nt = 0\n\nTransaction trace:\nC.constructor()\nState: x = 0, y = 0\nC.g(0)\n C.f1(0) -- internal call +// Warning 6328: (135-150): CHC: Assertion violation happens here.\nCounterexample:\nx = 0, y = 0\n_x = 1\nz = 0\nt = 0\n\nTransaction trace:\nC.constructor()\nState: x = 0, y = 0\nC.g(1)\n C.f1(1) -- internal call diff --git a/test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol index d872cb669..e8cf49a83 100644 --- a/test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol +++ b/test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol @@ -4,4 +4,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 4984: (80-85): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\n\na = 115792089237316195423570985008687907853269984665640564039457584007913129639935\nb = 1\n = 0\n\nTransaction trace:\nC.constructor()\nC.f(115792089237316195423570985008687907853269984665640564039457584007913129639935, 1) +// Warning 4984: (80-85): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\n\na = 1\nb = 115792089237316195423570985008687907853269984665640564039457584007913129639935\n = 0\n\nTransaction trace:\nC.constructor()\nC.f(1, 115792089237316195423570985008687907853269984665640564039457584007913129639935) diff --git a/test/libsolidity/smtCheckerTests/overflow/unsigned_mul_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/unsigned_mul_overflow.sol index 0a6d85a7e..045fcbff8 100644 --- a/test/libsolidity/smtCheckerTests/overflow/unsigned_mul_overflow.sol +++ b/test/libsolidity/smtCheckerTests/overflow/unsigned_mul_overflow.sol @@ -6,4 +6,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 4984: (80-85): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\n\nx = 57896044618658097711785492504343953926634992332820282019728792003956564819968\ny = 2\n = 0\n\nTransaction trace:\nC.constructor()\nC.f(57896044618658097711785492504343953926634992332820282019728792003956564819968, 2) +// Warning 4984: (80-85): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\n\nx = 2\ny = 57896044618658097711785492504343953926634992332820282019728792003956564819968\n = 0\n\nTransaction trace:\nC.constructor()\nC.f(2, 57896044618658097711785492504343953926634992332820282019728792003956564819968) diff --git a/test/libsolidity/smtCheckerTests/special/abi_decode_simple.sol b/test/libsolidity/smtCheckerTests/special/abi_decode_simple.sol index 5a6beef49..17e3b30d8 100644 --- a/test/libsolidity/smtCheckerTests/special/abi_decode_simple.sol +++ b/test/libsolidity/smtCheckerTests/special/abi_decode_simple.sol @@ -15,6 +15,6 @@ contract C { // Warning 2072: (152-156): Unused local variable. // Warning 8364: (123-124): Assertion checker does not yet implement type type(contract C) // Warning 8364: (193-194): Assertion checker does not yet implement type type(contract C) -// Warning 6328: (220-236): CHC: Assertion violation happens here.\nCounterexample:\n\ndata = [13, 13, 13, 13, 13, 13, 13, 13, 10, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13]\na1 = 7719\nb1 = 5\nc1 = 6\na2 = 7719\nb2 = 5\nc2 = 6\n\nTransaction trace:\nC.constructor()\nC.f([13, 13, 13, 13, 13, 13, 13, 13, 10, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13]) +// Warning 6328: (220-236): CHC: Assertion violation happens here.\nCounterexample:\n\ndata = [9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9]\na1 = 7719\nb1 = 5\nc1 = 6\na2 = 7719\nb2 = 5\nc2 = 6\n\nTransaction trace:\nC.constructor()\nC.f([9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9]) // Warning 8364: (123-124): Assertion checker does not yet implement type type(contract C) // Warning 8364: (193-194): Assertion checker does not yet implement type type(contract C) diff --git a/test/libsolidity/smtCheckerTests/special/blockhash.sol b/test/libsolidity/smtCheckerTests/special/blockhash.sol index 7c8b23052..c33aed968 100644 --- a/test/libsolidity/smtCheckerTests/special/blockhash.sol +++ b/test/libsolidity/smtCheckerTests/special/blockhash.sol @@ -11,4 +11,4 @@ contract C // SMTEngine: all // ---- // Warning 6328: (52-76): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 38\ny = 0\n\nTransaction trace:\nC.constructor()\nC.f(38){ value: 8365 } -// Warning 6328: (80-104): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 38\ny = 0\n\nTransaction trace:\nC.constructor()\nC.f(38){ value: 32285 } +// Warning 6328: (80-104): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 38\ny = 0\n\nTransaction trace:\nC.constructor()\nC.f(38){ value: 5853 } diff --git a/test/libsolidity/smtCheckerTests/special/event.sol b/test/libsolidity/smtCheckerTests/special/event.sol index 2788eeecd..a0a6d566b 100644 --- a/test/libsolidity/smtCheckerTests/special/event.sol +++ b/test/libsolidity/smtCheckerTests/special/event.sol @@ -27,4 +27,4 @@ contract C { // ---- // Warning 6321: (247-251): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. // Warning 6321: (397-401): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. -// Warning 6328: (407-416): CHC: Assertion violation happens here.\nCounterexample:\nx = false\n\nTransaction trace:\nC.constructor()\nState: x = true\nC.h()\n C.h_data() -- internal call\n C.h_data() -- internal call +// Warning 6328: (407-416): CHC: Assertion violation happens here.\nCounterexample:\nx = false\n\nTransaction trace:\nC.constructor()\nState: x = true\nC.h()\n C.h_data() -- internal call diff --git a/test/libsolidity/smtCheckerTests/special/msg_data.sol b/test/libsolidity/smtCheckerTests/special/msg_data.sol index 5bdc754c4..5c42613d3 100644 --- a/test/libsolidity/smtCheckerTests/special/msg_data.sol +++ b/test/libsolidity/smtCheckerTests/special/msg_data.sol @@ -23,4 +23,4 @@ contract C // SMTEngine: all // ---- // Warning 6328: (120-147): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.f(){ value: 30612 } -// Warning 6328: (467-494): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.g(){ value: 30612 } +// Warning 6328: (467-494): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.g(){ value: 11797 } diff --git a/test/libsolidity/smtCheckerTests/try_catch/try_multiple_catch_clauses_2.sol b/test/libsolidity/smtCheckerTests/try_catch/try_multiple_catch_clauses_2.sol index 609b31fcd..9180119a1 100644 --- a/test/libsolidity/smtCheckerTests/try_catch/try_multiple_catch_clauses_2.sol +++ b/test/libsolidity/smtCheckerTests/try_catch/try_multiple_catch_clauses_2.sol @@ -20,4 +20,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (306-320): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 3\nsuccess = false\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (306-320): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 2\nsuccess = false\n\nTransaction trace:\nC.constructor()\nC.f() diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_5.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_5.sol index 9f111dfb4..15500167d 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_5.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_5.sol @@ -33,6 +33,7 @@ contract C // SMTEngine: all // SMTIgnoreCex: yes // ---- +// Warning 6368: (186-196): CHC: Out of bounds access might happen here. // Warning 6368: (329-333): CHC: Out of bounds access happens here. // Warning 6368: (342-346): CHC: Out of bounds access happens here. // Warning 6368: (355-359): CHC: Out of bounds access happens here. diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_storage_unsafe_1.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_storage_unsafe_1.sol index d1686e8c0..3d6cf0df4 100644 --- a/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_storage_unsafe_1.sol +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_storage_unsafe_1.sol @@ -41,8 +41,8 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (148-165): CHC: Assertion violation happens here. -// Warning 6328: (183-202): CHC: Assertion violation happens here.\nCounterexample:\ns1 = {x: 2, t: {y: 3, a: []}, a: [], ts: []}\n\nTransaction trace:\nC.constructor()\nState: s1 = {x: 0, t: {y: 0, a: []}, a: [], ts: []}\nC.f() +// Warning 6328: (148-165): CHC: Assertion violation happens here.\nCounterexample:\ns1 = {x: 2, t: {y: 0, a: []}, a: [], ts: []}\n\nTransaction trace:\nC.constructor()\nState: s1 = {x: 0, t: {y: 0, a: []}, a: [], ts: []}\nC.f() +// Warning 6328: (183-202): CHC: Assertion violation happens here. // Warning 6328: (266-286): CHC: Assertion violation happens here. // Warning 6328: (404-427): CHC: Assertion violation happens here.\nCounterexample:\ns1 = {x: 2, t: {y: 3, a: []}, a: [0, 0, 4], ts: [{y: 0, a: []}, {y: 0, a: []}, {y: 0, a: []}, {y: 5, a: []}, {y: 0, a: []}, {y: 0, a: []}]}\n\nTransaction trace:\nC.constructor()\nState: s1 = {x: 0, t: {y: 0, a: []}, a: [], ts: []}\nC.f() -// Warning 6328: (578-604): CHC: Assertion violation happens here.\nCounterexample:\ns1 = {x: 2, t: {y: 3, a: []}, a: [0, 0, 4], ts: [{y: 0, a: []}, {y: 0, a: []}, {y: 0, a: []}, {y: 5, a: []}, {y: 0, a: [0, 0, 0, 0, 0, 6]}, {y: 0, a: []}]}\n\nTransaction trace:\nC.constructor()\nState: s1 = {x: 0, t: {y: 0, a: []}, a: [], ts: []}\nC.f() +// Warning 6328: (578-604): CHC: Assertion violation happens here. From 616933cd5f8d13a97b0b12cda069a0976dec4907 Mon Sep 17 00:00:00 2001 From: t11s Date: Fri, 16 Jul 2021 22:33:48 -0700 Subject: [PATCH 54/98] Remove incorrect statement that calldata must be used for external functions --- docs/types/reference-types.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/types/reference-types.rst b/docs/types/reference-types.rst index 6d5240ec8..a5a62ca4a 100644 --- a/docs/types/reference-types.rst +++ b/docs/types/reference-types.rst @@ -27,7 +27,6 @@ Every reference type has an additional annotation, the "data location", about where it is stored. There are three data locations: ``memory``, ``storage`` and ``calldata``. Calldata is a non-modifiable, non-persistent area where function arguments are stored, and behaves mostly like memory. -It is required for parameters of external functions but can also be used for other variables. .. note:: From e7324f660df2ba335df1f366fd865d5f703ed6f9 Mon Sep 17 00:00:00 2001 From: t11s Date: Sat, 17 Jul 2021 12:45:03 -0700 Subject: [PATCH 55/98] Update line about calldata use for external funcs --- docs/types/reference-types.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/types/reference-types.rst b/docs/types/reference-types.rst index a5a62ca4a..8472a5074 100644 --- a/docs/types/reference-types.rst +++ b/docs/types/reference-types.rst @@ -28,6 +28,7 @@ annotation, the "data location", about where it is stored. There are three data ``memory``, ``storage`` and ``calldata``. Calldata is a non-modifiable, non-persistent area where function arguments are stored, and behaves mostly like memory. +Calldata is usually preferred over memory for external functions but is not required after Solidity `0.6.9`. .. note:: Prior to version 0.5.0 the data location could be omitted, and would default to different locations From bb040e40ed68e1b91bedf900d0d1e4a1c7361a2f Mon Sep 17 00:00:00 2001 From: t11s Date: Sat, 17 Jul 2021 12:46:58 -0700 Subject: [PATCH 56/98] Fix rst syntax --- docs/types/reference-types.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/types/reference-types.rst b/docs/types/reference-types.rst index 8472a5074..9e2fa8020 100644 --- a/docs/types/reference-types.rst +++ b/docs/types/reference-types.rst @@ -28,7 +28,7 @@ annotation, the "data location", about where it is stored. There are three data ``memory``, ``storage`` and ``calldata``. Calldata is a non-modifiable, non-persistent area where function arguments are stored, and behaves mostly like memory. -Calldata is usually preferred over memory for external functions but is not required after Solidity `0.6.9`. +Calldata is usually preferred over memory for external functions but is not required after version 0.6.9. .. note:: Prior to version 0.5.0 the data location could be omitted, and would default to different locations From 61e4c29cf88693306a8742f62c8938ea0478b55a Mon Sep 17 00:00:00 2001 From: t11s Date: Sun, 18 Jul 2021 10:10:07 -0700 Subject: [PATCH 57/98] Update note about calldata --- docs/types/reference-types.rst | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/types/reference-types.rst b/docs/types/reference-types.rst index 9e2fa8020..be50daf19 100644 --- a/docs/types/reference-types.rst +++ b/docs/types/reference-types.rst @@ -28,19 +28,18 @@ annotation, the "data location", about where it is stored. There are three data ``memory``, ``storage`` and ``calldata``. Calldata is a non-modifiable, non-persistent area where function arguments are stored, and behaves mostly like memory. -Calldata is usually preferred over memory for external functions but is not required after version 0.6.9. +.. note:: + If you can, try to use ``calldata`` as data location because it will avoid copies and + also makes sure that the data cannot be modified. Calldata is typically preferred over + memory in external functions (though not required after version 0.6.9). Arrays and + structs with ``calldata`` data location can also be returned from functions, but it + is not possible to allocate such types. .. note:: Prior to version 0.5.0 the data location could be omitted, and would default to different locations depending on the kind of variable, function type, etc., but all complex types must now give an explicit data location. -.. note:: - If you can, try to use ``calldata`` as data location because it will avoid copies and - also makes sure that the data cannot be modified. Arrays and structs with ``calldata`` - data location can also be returned from functions, but it is not possible to - allocate such types. - .. _data-location-assignment: Data location and assignment behaviour From bc36dbf3b8937091c9abd0765874be05dec54606 Mon Sep 17 00:00:00 2001 From: t11s Date: Sun, 18 Jul 2021 10:11:18 -0700 Subject: [PATCH 58/98] Fix extra space --- docs/types/reference-types.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/types/reference-types.rst b/docs/types/reference-types.rst index be50daf19..bf5e736b1 100644 --- a/docs/types/reference-types.rst +++ b/docs/types/reference-types.rst @@ -29,10 +29,10 @@ annotation, the "data location", about where it is stored. There are three data non-persistent area where function arguments are stored, and behaves mostly like memory. .. note:: - If you can, try to use ``calldata`` as data location because it will avoid copies and + If you can, try to use ``calldata`` as a data location because it will avoid copies and also makes sure that the data cannot be modified. Calldata is typically preferred over memory in external functions (though not required after version 0.6.9). Arrays and - structs with ``calldata`` data location can also be returned from functions, but it + structs with ``calldata`` data location can also be returned from functions, but it is not possible to allocate such types. .. note:: From 61f33c53a05df2bf917c806ac309fe54377b4526 Mon Sep 17 00:00:00 2001 From: t11s Date: Sun, 18 Jul 2021 10:16:51 -0700 Subject: [PATCH 59/98] Remove trailing whitespace --- docs/types/reference-types.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/types/reference-types.rst b/docs/types/reference-types.rst index bf5e736b1..d2dda9925 100644 --- a/docs/types/reference-types.rst +++ b/docs/types/reference-types.rst @@ -30,9 +30,9 @@ non-persistent area where function arguments are stored, and behaves mostly like .. note:: If you can, try to use ``calldata`` as a data location because it will avoid copies and - also makes sure that the data cannot be modified. Calldata is typically preferred over - memory in external functions (though not required after version 0.6.9). Arrays and - structs with ``calldata`` data location can also be returned from functions, but it + also makes sure that the data cannot be modified. Calldata is typically preferred over + memory in external functions (though not required after version 0.6.9). Arrays and + structs with ``calldata`` data location can also be returned from functions, but it is not possible to allocate such types. .. note:: From c2dfbbf78c276c49a3935d90e24ecbc123eb0cc1 Mon Sep 17 00:00:00 2001 From: t11s Date: Sun, 18 Jul 2021 15:02:16 -0700 Subject: [PATCH 60/98] New note about which data locations are allowed in which function types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kamil Śliwak --- docs/types/reference-types.rst | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/types/reference-types.rst b/docs/types/reference-types.rst index d2dda9925..a40cab3c2 100644 --- a/docs/types/reference-types.rst +++ b/docs/types/reference-types.rst @@ -29,11 +29,16 @@ annotation, the "data location", about where it is stored. There are three data non-persistent area where function arguments are stored, and behaves mostly like memory. .. note:: - If you can, try to use ``calldata`` as a data location because it will avoid copies and - also makes sure that the data cannot be modified. Calldata is typically preferred over - memory in external functions (though not required after version 0.6.9). Arrays and - structs with ``calldata`` data location can also be returned from functions, but it - is not possible to allocate such types. + If you can, try to use ``calldata`` as data location because it will avoid copies and + also makes sure that the data cannot be modified. Arrays and structs with ``calldata`` + data location can also be returned from functions, but it is not possible to + allocate such types. + +.. note:: + Prior to version 0.6.9 data location for reference-type arguments was limited to + ``calldata`` in external functions, ``memory`` in public functions and either + ``memory`` or ``storage`` in internal and private ones. + Now ``memory`` and ``calldata`` are allowed in all functions regardless of their visibility. .. note:: Prior to version 0.5.0 the data location could be omitted, and would default to different locations From 5decccaf3a8f8fb9c2551355dc6a23120c2fb0c7 Mon Sep 17 00:00:00 2001 From: Leo Alt Date: Fri, 16 Jul 2021 13:01:17 +0200 Subject: [PATCH 61/98] update docker hashes and versions --- .circleci/config.yml | 16 ++++++++-------- .circleci/osx_install_dependencies.sh | 16 +++++++++------- scripts/build_emscripten.sh | 4 ++-- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 054a60a28..93e9f93bd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,20 +9,20 @@ version: 2.1 parameters: ubuntu-2004-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-6 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:da44d7f78e093f7f0415abf07f7c1fd1c2ed4fa65fefea428821a05186c42ec9" + # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-7 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:af5a0c6ea5e113e477f5387955a862f9aea5cc74d9ceeb2377fc64e64088d200" ubuntu-2004-clang-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-6 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:c78dd9c48d393b57afe053aeb2d0d358a9f31ac85039a181724c2f8408d0bcf8" + # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-7 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:cb504c6456d4a2dd80b354acfd7429836da5acce4e394500c02d5740617f9d01" ubuntu-1604-clang-ossfuzz-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-9 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:5078e1d74ab6f4329e9218c2d8c0ebe2d42817a3d4c3c62ce887100cbe9bc739" + # solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-10 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:ea15a35f6188360b425593c83e946660ab4ea4dac9b9c3bb3629e6ed57276b1d" emscripten-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:emscripten-5 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:d28afb9624c2352ea40f157d1a321ffac77f54a21e33a8e8744f9126b780ded4" + # solbuildpackpusher/solidity-buildpack-deps:emscripten-6 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:092da5817bc032c91a806b4f73db2a1a31e5cc4c066d94d43eedd9f365df7154" orbs: win: circleci/windows@2.2.0 diff --git a/.circleci/osx_install_dependencies.sh b/.circleci/osx_install_dependencies.sh index f1dd4f11b..55d406946 100755 --- a/.circleci/osx_install_dependencies.sh +++ b/.circleci/osx_install_dependencies.sh @@ -48,13 +48,15 @@ then ./scripts/install_obsolete_jsoncpp_1_7_4.sh # z3 - wget https://github.com/Z3Prover/z3/releases/download/z3-4.8.10/z3-4.8.10-x64-osx-10.15.7.zip - unzip z3-4.8.10-x64-osx-10.15.7.zip - rm -f z3-4.8.10-x64-osx-10.15.7.zip - cp z3-4.8.10-x64-osx-10.15.7/bin/libz3.a /usr/local/lib - cp z3-4.8.10-x64-osx-10.15.7/bin/z3 /usr/local/bin - cp z3-4.8.10-x64-osx-10.15.7/include/* /usr/local/include - rm -rf z3-4.8.10-x64-osx-10.15.7 + z3_version="z3-4.8.12" + osx_version="osx-10.15.7" + wget "https://github.com/Z3Prover/z3/releases/download/$z3_version/$z3_version-x64-$osx_version.zip" + unzip "$z3_version-x64-$osx_version.zip" + rm -f "$z3_version-x64-$osx_version.zip" + cp "$z3_version-x64-$osx_version/bin/libz3.a" /usr/local/lib + cp "$z3_version-x64-$osx_version/bin/z3" /usr/local/bin + cp "$z3_version-x64-$osx_version"/include/* /usr/local/include + rm -rf "$z3_version-x64-$osx_version" # evmone wget https://github.com/ethereum/evmone/releases/download/v0.7.0/evmone-0.7.0-darwin-x86_64.tar.gz diff --git a/scripts/build_emscripten.sh b/scripts/build_emscripten.sh index 1e0e35924..667d63708 100755 --- a/scripts/build_emscripten.sh +++ b/scripts/build_emscripten.sh @@ -34,7 +34,7 @@ else BUILD_DIR="$1" fi -# solbuildpackpusher/solidity-buildpack-deps:emscripten-4 +# solbuildpackpusher/solidity-buildpack-deps:emscripten-6 docker run -v "$(pwd):/root/project" -w /root/project \ - solbuildpackpusher/solidity-buildpack-deps@sha256:434719d8104cab47712dd1f56f255994d04eb65b802c0d382790071c1a0c074b \ + solbuildpackpusher/solidity-buildpack-deps@sha256:092da5817bc032c91a806b4f73db2a1a31e5cc4c066d94d43eedd9f365df7154 \ ./scripts/ci/build_emscripten.sh "$BUILD_DIR" From e46abd0ca149b6639703f0c5f600ed00dbe581ce Mon Sep 17 00:00:00 2001 From: Leo Alt Date: Fri, 16 Jul 2021 15:44:36 +0200 Subject: [PATCH 62/98] Update tests due to nondeterminism --- .../abi/abi_encode_packed_array_slice_2.sol | 1 + .../crypto/crypto_functions_fail.sol | 16 ++++++++++------ .../file_level/free_function_1.sol | 5 +++-- .../smtCheckerTests/file_level/recursion.sol | 19 +++---------------- .../functions/constructor_state_value.sol | 3 ++- .../inheritance/fallback_receive.sol | 7 ++++--- .../smtCheckerTests/inheritance/receive.sol | 7 ++++--- ...y_local_storage_access_inside_function.sol | 5 +++-- ...act_function_nondet_pow_no_abstraction.sol | 9 +++------ .../smtCheckerTests/special/blockhash.sol | 5 +++-- .../smtCheckerTests/special/msg_data.sol | 5 +++-- .../try_multiple_catch_clauses_2.sol | 3 ++- .../types/string_literal_comparison_2.sol | 5 +++-- ...uct_array_struct_array_memory_unsafe_2.sol | 1 + ...ct_array_struct_array_storage_unsafe_1.sol | 5 +++-- 15 files changed, 48 insertions(+), 48 deletions(-) diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_packed_array_slice_2.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_packed_array_slice_2.sol index 7f8b98806..fc34ff1c7 100644 --- a/test/libsolidity/smtCheckerTests/abi/abi_encode_packed_array_slice_2.sol +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_packed_array_slice_2.sol @@ -28,6 +28,7 @@ contract C { } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // ---- // Warning 2072: (643-658): Unused local variable. // Warning 6328: (298-328): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/crypto/crypto_functions_fail.sol b/test/libsolidity/smtCheckerTests/crypto/crypto_functions_fail.sol index f7104caf5..8485b89a6 100644 --- a/test/libsolidity/smtCheckerTests/crypto/crypto_functions_fail.sol +++ b/test/libsolidity/smtCheckerTests/crypto/crypto_functions_fail.sol @@ -7,12 +7,14 @@ contract C { function s(bytes memory b0, bytes memory b1) public pure { bytes32 s0 = sha256(b0); bytes32 s1 = sha256(b1); - assert(s0 == s1); + // Disabled because of Spacer nondeterminism. + //assert(s0 == s1); } function r(bytes memory b0, bytes memory b1) public pure { bytes32 r0 = ripemd160(b0); bytes32 r1 = ripemd160(b1); - assert(r0 == r1); + // Disabled because of Spacer nondeterminism. + //assert(r0 == r1); } function e(bytes32 h0, uint8 v0, bytes32 r0, bytes32 s0, bytes32 h1, uint8 v1, bytes32 r1, bytes32 s1) public pure { address a0 = ecrecover(h0, v0, r0, s0); @@ -25,8 +27,10 @@ contract C { // SMTEngine: all // SMTIgnoreCex: yes // ---- -// Warning 2072: (556-566): Unused local variable. -// Warning 2072: (598-608): Unused local variable. +// Warning 2072: (218-228): Unused local variable. +// Warning 2072: (245-255): Unused local variable. +// Warning 2072: (405-415): Unused local variable. +// Warning 2072: (435-445): Unused local variable. +// Warning 2072: (656-666): Unused local variable. +// Warning 2072: (698-708): Unused local variable. // Warning 6328: (135-151): CHC: Assertion violation happens here. -// Warning 6328: (272-288): CHC: Assertion violation happens here. -// Warning 6328: (415-431): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/file_level/free_function_1.sol b/test/libsolidity/smtCheckerTests/file_level/free_function_1.sol index 87d565327..5dd951521 100644 --- a/test/libsolidity/smtCheckerTests/file_level/free_function_1.sol +++ b/test/libsolidity/smtCheckerTests/file_level/free_function_1.sol @@ -10,6 +10,7 @@ function fun(uint[] calldata _x, uint[] storage _y) view returns (uint, uint[] } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // ---- -// Warning 4984: (168-180): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\ndata = []\nx = 1\n = 0\n = 0\na = 0\n\nTransaction trace:\nC.constructor()\nState: data = []\nC.f(1, input)\n fun(_x, []) -- internal call -// Warning 6368: (289-294): CHC: Out of bounds access happens here.\nCounterexample:\ndata = []\nx = 0\ninput = [5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 16, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]\n = 0\n = 0\n\nTransaction trace:\nC.constructor()\nState: data = []\nC.f(0, [5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 16, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5])\n fun([5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 16, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5], []) -- internal call +// Warning 4984: (168-180): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 6368: (289-294): CHC: Out of bounds access happens here. diff --git a/test/libsolidity/smtCheckerTests/file_level/recursion.sol b/test/libsolidity/smtCheckerTests/file_level/recursion.sol index a10087ba5..f461861e9 100644 --- a/test/libsolidity/smtCheckerTests/file_level/recursion.sol +++ b/test/libsolidity/smtCheckerTests/file_level/recursion.sol @@ -13,29 +13,16 @@ contract C { } function f() public pure { // All of these should hold but the SMTChecker can't prove them. + // Disabled because of Spacer nondet + /* assert(g(0, 0) == 1); assert(g(0, 1) == 0); assert(g(1, 0) == 1); assert(g(2, 3) == 8); assert(g(3, 10) == 59049); + */ } } // ==== // SMTEngine: all // ---- -// Warning 4281: (118-130): CHC: Division by zero might happen here. -// Warning 4984: (134-148): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. -// Warning 4984: (176-189): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. -// Warning 6328: (430-450): CHC: Assertion violation might happen here. -// Warning 6328: (454-474): CHC: Assertion violation might happen here. -// Warning 6328: (478-498): CHC: Assertion violation might happen here. -// Warning 6328: (502-527): CHC: Assertion violation might happen here. -// Warning 2661: (134-148): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. -// Warning 2661: (176-189): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. -// Warning 2661: (134-148): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. -// Warning 4661: (454-474): BMC: Assertion violation happens here. -// Warning 2661: (134-148): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. -// Warning 2661: (176-189): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. -// Warning 4661: (478-498): BMC: Assertion violation happens here. -// Warning 2661: (134-148): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. -// Warning 4661: (502-527): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_state_value.sol b/test/libsolidity/smtCheckerTests/functions/constructor_state_value.sol index 5add9bf68..48819f839 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_state_value.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_state_value.sol @@ -12,5 +12,6 @@ contract C { } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // ---- -// Warning 6328: (112-126): CHC: Assertion violation happens here.\nCounterexample:\nx = 10\ny = 11\n\nTransaction trace:\nC.constructor()\nState: x = 10\nC.f(11) +// Warning 6328: (112-126): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/inheritance/fallback_receive.sol b/test/libsolidity/smtCheckerTests/inheritance/fallback_receive.sol index 81b3d9a48..203484265 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/fallback_receive.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/fallback_receive.sol @@ -20,7 +20,8 @@ contract B is A { } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // ---- -// Warning 6328: (81-95): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\nTransaction trace:\nA.constructor()\nState: x = 0\nA.fallback() -// Warning 6328: (130-144): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\nTransaction trace:\nA.constructor()\nState: x = 0\nA.g() -// Warning 6328: (256-270): CHC: Assertion violation happens here.\nCounterexample:\ny = 0, x = 0\n\nTransaction trace:\nB.constructor()\nState: y = 0, x = 0\nB.receive(){ value: 10450 } +// Warning 6328: (81-95): CHC: Assertion violation happens here. +// Warning 6328: (130-144): CHC: Assertion violation happens here. +// Warning 6328: (256-270): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/inheritance/receive.sol b/test/libsolidity/smtCheckerTests/inheritance/receive.sol index a88ff75f8..f55b9efd6 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/receive.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/receive.sol @@ -20,7 +20,8 @@ contract B is A { } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // ---- -// Warning 6328: (95-109): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\nTransaction trace:\nA.constructor()\nState: x = 0\nA.receive() -// Warning 6328: (144-158): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\nTransaction trace:\nA.constructor()\nState: x = 0\nA.g() -// Warning 6328: (267-281): CHC: Assertion violation happens here.\nCounterexample:\ny = 0, x = 0\n\nTransaction trace:\nB.constructor()\nState: y = 0, x = 0\nB.receive() +// Warning 6328: (95-109): CHC: Assertion violation happens here. +// Warning 6328: (144-158): CHC: Assertion violation happens here. +// Warning 6328: (267-281): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/inline_assembly/assembly_local_storage_access_inside_function.sol b/test/libsolidity/smtCheckerTests/inline_assembly/assembly_local_storage_access_inside_function.sol index a95bb8731..797f6ae76 100644 --- a/test/libsolidity/smtCheckerTests/inline_assembly/assembly_local_storage_access_inside_function.sol +++ b/test/libsolidity/smtCheckerTests/inline_assembly/assembly_local_storage_access_inside_function.sol @@ -17,8 +17,9 @@ contract C { } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // ---- // Warning 7737: (83-149): Inline assembly may cause SMTChecker to produce spurious warnings (false positives). -// Warning 6328: (152-167): CHC: Assertion violation happens here.\nCounterexample:\nz = 0\ni = 32\n\nTransaction trace:\nC.constructor()\nState: z = 0\nC.f() -// Warning 6328: (186-200): CHC: Assertion violation happens here.\nCounterexample:\nz = 0\ni = 32\n\nTransaction trace:\nC.constructor()\nState: z = 0\nC.f() +// Warning 6328: (152-167): CHC: Assertion violation happens here. +// Warning 6328: (186-200): CHC: Assertion violation happens here. // Warning 7737: (83-149): Inline assembly may cause SMTChecker to produce spurious warnings (false positives). diff --git a/test/libsolidity/smtCheckerTests/natspec/abstract_function_nondet_pow_no_abstraction.sol b/test/libsolidity/smtCheckerTests/natspec/abstract_function_nondet_pow_no_abstraction.sol index 7ce8b9c06..dc4fc9ef1 100644 --- a/test/libsolidity/smtCheckerTests/natspec/abstract_function_nondet_pow_no_abstraction.sol +++ b/test/libsolidity/smtCheckerTests/natspec/abstract_function_nondet_pow_no_abstraction.sol @@ -7,6 +7,8 @@ contract C { } function pow(uint base, uint exponent) internal pure returns (uint) { + // Disabled because of Spacer nondet + /* if (base == 0) { return 0; } @@ -28,14 +30,9 @@ contract C { } } return base * y; + */ } } // ==== // SMTEngine: chc // ---- -// Warning 4281: (435-447): CHC: Division by zero might happen here. -// Warning 4984: (467-478): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. -// Warning 4281: (495-507): CHC: Division by zero might happen here. -// Warning 4984: (529-537): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. -// Warning 4984: (550-561): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. -// Warning 4984: (616-624): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. diff --git a/test/libsolidity/smtCheckerTests/special/blockhash.sol b/test/libsolidity/smtCheckerTests/special/blockhash.sol index c33aed968..2a7cc39fa 100644 --- a/test/libsolidity/smtCheckerTests/special/blockhash.sol +++ b/test/libsolidity/smtCheckerTests/special/blockhash.sol @@ -9,6 +9,7 @@ contract C } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // ---- -// Warning 6328: (52-76): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 38\ny = 0\n\nTransaction trace:\nC.constructor()\nC.f(38){ value: 8365 } -// Warning 6328: (80-104): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 38\ny = 0\n\nTransaction trace:\nC.constructor()\nC.f(38){ value: 5853 } +// Warning 6328: (52-76): CHC: Assertion violation happens here. +// Warning 6328: (80-104): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/special/msg_data.sol b/test/libsolidity/smtCheckerTests/special/msg_data.sol index 5c42613d3..90022508f 100644 --- a/test/libsolidity/smtCheckerTests/special/msg_data.sol +++ b/test/libsolidity/smtCheckerTests/special/msg_data.sol @@ -21,6 +21,7 @@ contract C } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // ---- -// Warning 6328: (120-147): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.f(){ value: 30612 } -// Warning 6328: (467-494): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.g(){ value: 11797 } +// Warning 6328: (120-147): CHC: Assertion violation happens here. +// Warning 6328: (467-494): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/try_catch/try_multiple_catch_clauses_2.sol b/test/libsolidity/smtCheckerTests/try_catch/try_multiple_catch_clauses_2.sol index 9180119a1..d11d3a8a6 100644 --- a/test/libsolidity/smtCheckerTests/try_catch/try_multiple_catch_clauses_2.sol +++ b/test/libsolidity/smtCheckerTests/try_catch/try_multiple_catch_clauses_2.sol @@ -19,5 +19,6 @@ contract C { } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // ---- -// Warning 6328: (306-320): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 2\nsuccess = false\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (306-320): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/types/string_literal_comparison_2.sol b/test/libsolidity/smtCheckerTests/types/string_literal_comparison_2.sol index 24a2652d8..81f513bd5 100644 --- a/test/libsolidity/smtCheckerTests/types/string_literal_comparison_2.sol +++ b/test/libsolidity/smtCheckerTests/types/string_literal_comparison_2.sol @@ -9,6 +9,7 @@ contract C { } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // ---- -// Warning 6328: (114-133): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 0\ny = 0\nz = 0\n\nTransaction trace:\nC.constructor()\nC.f(0) -// Warning 6328: (137-157): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 52647538830022687173130149211684818290356179572910782152375644828738034597888\ny = 52647538830022687173130149211684818290356179572910782152375644828738034597888\nz = 52647538830022687173130149211684818290356179572910782152375644828738034597888\n\nTransaction trace:\nC.constructor()\nC.f(52647538830022687173130149211684818290356179572910782152375644828738034597888) +// Warning 6328: (114-133): CHC: Assertion violation happens here. +// Warning 6328: (137-157): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_memory_unsafe_2.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_memory_unsafe_2.sol index d34eb47ed..a1a40d64b 100644 --- a/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_memory_unsafe_2.sol +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_memory_unsafe_2.sol @@ -36,5 +36,6 @@ contract C { } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // ---- // Warning 6328: (804-842): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_storage_unsafe_1.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_storage_unsafe_1.sol index 3d6cf0df4..662b4f1d2 100644 --- a/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_storage_unsafe_1.sol +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_storage_unsafe_1.sol @@ -40,9 +40,10 @@ contract C { } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // ---- -// Warning 6328: (148-165): CHC: Assertion violation happens here.\nCounterexample:\ns1 = {x: 2, t: {y: 0, a: []}, a: [], ts: []}\n\nTransaction trace:\nC.constructor()\nState: s1 = {x: 0, t: {y: 0, a: []}, a: [], ts: []}\nC.f() +// Warning 6328: (148-165): CHC: Assertion violation happens here. // Warning 6328: (183-202): CHC: Assertion violation happens here. // Warning 6328: (266-286): CHC: Assertion violation happens here. -// Warning 6328: (404-427): CHC: Assertion violation happens here.\nCounterexample:\ns1 = {x: 2, t: {y: 3, a: []}, a: [0, 0, 4], ts: [{y: 0, a: []}, {y: 0, a: []}, {y: 0, a: []}, {y: 5, a: []}, {y: 0, a: []}, {y: 0, a: []}]}\n\nTransaction trace:\nC.constructor()\nState: s1 = {x: 0, t: {y: 0, a: []}, a: [], ts: []}\nC.f() +// Warning 6328: (404-427): CHC: Assertion violation happens here. // Warning 6328: (578-604): CHC: Assertion violation happens here. From f745604474cd5de52aa69c2374b6185124bb6290 Mon Sep 17 00:00:00 2001 From: Leo Alt Date: Fri, 16 Jul 2021 11:03:04 +0200 Subject: [PATCH 63/98] Bump docker images to z3 4.8.12 --- scripts/docker/buildpack-deps/Dockerfile.emscripten | 4 ++-- .../docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz | 4 ++-- scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 | 4 ++-- scripts/docker/buildpack-deps/Dockerfile.ubuntu2004.clang | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/docker/buildpack-deps/Dockerfile.emscripten b/scripts/docker/buildpack-deps/Dockerfile.emscripten index cdd973fbf..790a29634 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.emscripten +++ b/scripts/docker/buildpack-deps/Dockerfile.emscripten @@ -29,12 +29,12 @@ # make version=2.0.12 build # FROM emscripten/emsdk:2.0.12 AS base -LABEL version="5" +LABEL version="6" ADD emscripten.jam /usr/src RUN set -ex; \ cd /usr/src; \ - git clone https://github.com/Z3Prover/z3.git -b z3-4.8.10 --depth 1 ; \ + git clone https://github.com/Z3Prover/z3.git -b z3-4.8.12 --depth 1 ; \ cd z3; \ mkdir build; \ cd build; \ diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz b/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz index fbcc17aae..80aa969c3 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz @@ -22,7 +22,7 @@ # (c) 2016-2019 solidity contributors. #------------------------------------------------------------------------------ FROM gcr.io/oss-fuzz-base/base-clang as base -LABEL version="9" +LABEL version="10" ARG DEBIAN_FRONTEND=noninteractive @@ -60,7 +60,7 @@ RUN set -ex; \ # Z3 RUN set -ex; \ - git clone --depth 1 -b z3-4.8.10 https://github.com/Z3Prover/z3.git \ + git clone --depth 1 -b z3-4.8.12 https://github.com/Z3Prover/z3.git \ /usr/src/z3; \ cd /usr/src/z3; \ mkdir build; \ diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 index 8013db7ff..c27f7246b 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 @@ -22,7 +22,7 @@ # (c) 2016-2019 solidity contributors. #------------------------------------------------------------------------------ FROM buildpack-deps:focal AS base -LABEL version="6" +LABEL version="7" ARG DEBIAN_FRONTEND=noninteractive @@ -37,7 +37,7 @@ RUN set -ex; \ cmake ninja-build \ libboost-filesystem-dev libboost-test-dev libboost-system-dev \ libboost-program-options-dev \ - libcvc4-dev libz3-static-dev \ + libcvc4-dev libz3-static-dev z3-static \ ; \ apt-get install -qy python3-pip python3-sphinx; \ pip3 install codecov; \ diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004.clang b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004.clang index 0d816d5d7..89a0cef7f 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004.clang +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004.clang @@ -22,7 +22,7 @@ # (c) 2016-2019 solidity contributors. #------------------------------------------------------------------------------ FROM buildpack-deps:focal AS base -LABEL version="6" +LABEL version="7" ARG DEBIAN_FRONTEND=noninteractive From 6e18c6e7cdc891b661975ee8d58d1b6c6a9f8d6b Mon Sep 17 00:00:00 2001 From: qedk <1994constant@gmail.com> Date: Sun, 25 Jul 2021 20:23:29 +0530 Subject: [PATCH 64/98] Remove keyword already in use from cheatsheet --- docs/cheatsheet.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cheatsheet.rst b/docs/cheatsheet.rst index 10a7ce105..4b18c63ac 100644 --- a/docs/cheatsheet.rst +++ b/docs/cheatsheet.rst @@ -193,7 +193,7 @@ Reserved Keywords These keywords are reserved in Solidity. They might become part of the syntax in the future: ``after``, ``alias``, ``apply``, ``auto``, ``case``, ``copyof``, ``default``, -``define``, ``final``, ``immutable``, ``implements``, ``in``, ``inline``, ``let``, ``macro``, ``match``, +``define``, ``final``, ``implements``, ``in``, ``inline``, ``let``, ``macro``, ``match``, ``mutable``, ``null``, ``of``, ``partial``, ``promise``, ``reference``, ``relocatable``, ``sealed``, ``sizeof``, ``static``, ``supports``, ``switch``, ``typedef``, ``typeof``, ``unchecked``. From 71d6e8da2dfcc1f24127f73076a01bd71701ada3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9?= Date: Fri, 23 Jul 2021 14:11:35 -0500 Subject: [PATCH 65/98] tx.origin pointing where exactly is the bug Clarification for preventing more bugs of this kind that have been shown recently. --- docs/security-considerations.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index c59f4c750..e348dd915 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -211,6 +211,7 @@ Never use tx.origin for authorization. Let's say you have a wallet contract like } function transferTo(address payable dest, uint amount) public { + // THE BUG IS RIGHT HERE, you must use msg.sender instead of tx.origin require(tx.origin == owner); dest.transfer(amount); } From 997c014971a02a77ae1431af42dbcc5a992ed1b0 Mon Sep 17 00:00:00 2001 From: GuLiPing-Hz <1058719078@qq.com> Date: Thu, 22 Jul 2021 18:37:08 +0800 Subject: [PATCH 66/98] Update libraries.rst add unchecked --- docs/contracts/libraries.rst | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/contracts/libraries.rst b/docs/contracts/libraries.rst index a6a062501..830eed448 100644 --- a/docs/contracts/libraries.rst +++ b/docs/contracts/libraries.rst @@ -132,7 +132,7 @@ custom types without the overhead of external function calls: .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.8 <0.9.0; + pragma solidity >=0.8.0 <0.9.0; struct bigint { uint[] limbs; @@ -150,11 +150,14 @@ custom types without the overhead of external function calls: for (uint i = 0; i < r.limbs.length; ++i) { uint a = limb(_a, i); uint b = limb(_b, i); - r.limbs[i] = a + b + carry; - if (a + b < a || (a + b == type(uint).max && carry > 0)) - carry = 1; - else - carry = 0; + unchecked { + r.limbs[i] = a + b + carry; + + if (a + b < a || (a + b == type(uint).max && carry > 0)) + carry = 1; + else + carry = 0; + } } if (carry > 0) { // too bad, we have to add a limb From 8ea31cf4e1cb9d082c452f5f8339a6a24a98618c Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 26 Jul 2021 14:17:22 +0200 Subject: [PATCH 67/98] Use caret --- docs/contracts/libraries.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contracts/libraries.rst b/docs/contracts/libraries.rst index 830eed448..72fdb61cb 100644 --- a/docs/contracts/libraries.rst +++ b/docs/contracts/libraries.rst @@ -132,7 +132,7 @@ custom types without the overhead of external function calls: .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.8.0 <0.9.0; + pragma solidity ^0.8.0; struct bigint { uint[] limbs; From 9bbeea58c83a25b4b155ef784cd838e2d5fde2fe Mon Sep 17 00:00:00 2001 From: Ladislav Sladecek Date: Wed, 7 Jul 2021 13:18:24 +0200 Subject: [PATCH 68/98] Make --pretty-json work with Standard JSON output --- Changelog.md | 1 + libsolidity/interface/StandardCompiler.cpp | 4 +-- libsolidity/interface/StandardCompiler.h | 9 +++++-- libsolutil/JSON.cpp | 27 +++++++++++++------ libsolutil/JSON.h | 21 +++++++++++++++ solc/CommandLineInterface.cpp | 6 ++--- solc/CommandLineParser.cpp | 21 ++++++++++++--- solc/CommandLineParser.h | 5 +++- test/cmdlineTests.sh | 11 ++++++-- test/cmdlineTests/pretty_json_combined/args | 1 + .../pretty_json_combined/input.sol | 3 +++ test/cmdlineTests/pretty_json_combined/output | 10 +++++++ .../cmdlineTests/pretty_json_indent_only/args | 1 + .../pretty_json_indent_only/input.json | 10 +++++++ .../pretty_json_indent_only/output | 1 + .../pretty_json_indent_only/output.json | 9 +++++++ test/cmdlineTests/pretty_json_standard/args | 1 + .../pretty_json_standard/input.json | 10 +++++++ .../pretty_json_standard/output.json | 9 +++++++ .../pretty_json_standard_indent/args | 1 + .../pretty_json_standard_indent/input.json | 10 +++++++ .../pretty_json_standard_indent/output | 9 +++++++ .../pretty_json_standard_indent/output.json | 9 +++++++ test/solc/CommandLineParser.cpp | 13 ++++++--- 24 files changed, 176 insertions(+), 26 deletions(-) create mode 100644 test/cmdlineTests/pretty_json_combined/args create mode 100644 test/cmdlineTests/pretty_json_combined/input.sol create mode 100644 test/cmdlineTests/pretty_json_combined/output create mode 100644 test/cmdlineTests/pretty_json_indent_only/args create mode 100644 test/cmdlineTests/pretty_json_indent_only/input.json create mode 100644 test/cmdlineTests/pretty_json_indent_only/output create mode 100644 test/cmdlineTests/pretty_json_indent_only/output.json create mode 100644 test/cmdlineTests/pretty_json_standard/args create mode 100644 test/cmdlineTests/pretty_json_standard/input.json create mode 100644 test/cmdlineTests/pretty_json_standard/output.json create mode 100644 test/cmdlineTests/pretty_json_standard_indent/args create mode 100644 test/cmdlineTests/pretty_json_standard_indent/input.json create mode 100644 test/cmdlineTests/pretty_json_standard_indent/output create mode 100644 test/cmdlineTests/pretty_json_standard_indent/output.json diff --git a/Changelog.md b/Changelog.md index 9638d597a..042248fe7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,6 +8,7 @@ Compiler Features: * Yul EVM Code Transform: Do not reuse stack slots that immediately become unreachable. * Yul EVM Code Transform: Also pop unused argument slots for functions without return variables (under the same restrictions as for functions with return variables). * Yul Optimizer: Move function arguments and return variables to memory with the experimental Stack Limit Evader (which is not enabled by default). + * Commandline Interface: option ``--pretty-json`` works also with ``--standard--json``. Bugfixes: diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index ab70d93b7..b27e7d08d 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -1420,7 +1420,7 @@ string StandardCompiler::compile(string const& _input) noexcept try { if (!util::jsonParseStrict(_input, input, &errors)) - return util::jsonCompactPrint(formatFatalError("JSONError", errors)); + return util::jsonPrint(formatFatalError("JSONError", errors), m_jsonPrintingFormat); } catch (...) { @@ -1433,7 +1433,7 @@ string StandardCompiler::compile(string const& _input) noexcept try { - return util::jsonCompactPrint(output); + return util::jsonPrint(output, m_jsonPrintingFormat); } catch (...) { diff --git a/libsolidity/interface/StandardCompiler.h b/libsolidity/interface/StandardCompiler.h index da193b3a1..4d43fdfec 100644 --- a/libsolidity/interface/StandardCompiler.h +++ b/libsolidity/interface/StandardCompiler.h @@ -24,6 +24,7 @@ #pragma once #include +#include #include #include @@ -46,8 +47,10 @@ public: /// Creates a new StandardCompiler. /// @param _readFile callback used to read files for import statements. Must return /// and must not emit exceptions. - explicit StandardCompiler(ReadCallback::Callback _readFile = ReadCallback::Callback()): - m_readFile(std::move(_readFile)) + explicit StandardCompiler(ReadCallback::Callback _readFile = ReadCallback::Callback(), + util::JsonFormat const& _format = {}): + m_readFile(std::move(_readFile)), + m_jsonPrintingFormat(std::move(_format)) { } @@ -91,6 +94,8 @@ private: Json::Value compileYul(InputsAndSettings _inputsAndSettings); ReadCallback::Callback m_readFile; + + util::JsonFormat m_jsonPrintingFormat; }; } diff --git a/libsolutil/JSON.cpp b/libsolutil/JSON.cpp index 81ef7f76b..d27982acb 100644 --- a/libsolutil/JSON.cpp +++ b/libsolutil/JSON.cpp @@ -115,18 +115,29 @@ Json::Value removeNullMembers(Json::Value _json) string jsonPrettyPrint(Json::Value const& _input) { - static map settings{{"indentation", " "}, {"enableYAMLCompatibility", true}}; - static StreamWriterBuilder writerBuilder(settings); - string result = print(_input, writerBuilder); - boost::replace_all(result, " \n", "\n"); - return result; + return jsonPrint(_input, JsonFormat{ JsonFormat::Pretty }); } string jsonCompactPrint(Json::Value const& _input) { - static map settings{{"indentation", ""}}; - static StreamWriterBuilder writerBuilder(settings); - return print(_input, writerBuilder); + return jsonPrint(_input, JsonFormat{ JsonFormat::Compact }); +} + +string jsonPrint(Json::Value const& _input, JsonFormat const& _format) +{ + map settings; + if (_format.format == JsonFormat::Pretty) + { + settings["indentation"] = string(_format.indent, ' '); + settings["enableYAMLCompatibility"] = true; + } + else + settings["indentation"] = ""; + StreamWriterBuilder writerBuilder(settings); + string result = print(_input, writerBuilder); + if (_format.format == JsonFormat::Pretty) + boost::replace_all(result, " \n", "\n"); + return result; } bool jsonParseStrict(string const& _input, Json::Value& _json, string* _errs /* = nullptr */) diff --git a/libsolutil/JSON.h b/libsolutil/JSON.h index c1d045e73..3a326a5e3 100644 --- a/libsolutil/JSON.h +++ b/libsolutil/JSON.h @@ -33,12 +33,33 @@ namespace solidity::util /// Removes members with null value recursively from (@a _json). Json::Value removeNullMembers(Json::Value _json); +/// JSON printing format. +struct JsonFormat +{ + enum Format + { + Compact, + Pretty + }; + + static constexpr uint32_t defaultIndent = 2; + + bool operator==(JsonFormat const& _other) const noexcept { return (format == _other.format) && (indent == _other.indent); } + bool operator!=(JsonFormat const& _other) const noexcept { return !(*this == _other); } + + Format format = Compact; + uint32_t indent = defaultIndent; +}; + /// Serialise the JSON object (@a _input) with indentation std::string jsonPrettyPrint(Json::Value const& _input); /// Serialise the JSON object (@a _input) without indentation std::string jsonCompactPrint(Json::Value const& _input); +/// Serialise the JSON object (@a _input) using specified format (@a _format) +std::string jsonPrint(Json::Value const& _input, JsonFormat const& _format); + /// Parse a JSON string (@a _input) with enabled strict-mode and writes resulting JSON object to (@a _json) /// \param _input JSON input string /// \param _json [out] resulting JSON object diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index aeb40f099..1b7f2d089 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -532,7 +532,7 @@ bool CommandLineInterface::processInput() return false; } } - StandardCompiler compiler(m_fileReader.reader()); + StandardCompiler compiler(m_fileReader.reader(), m_options.formatting.json); sout() << compiler.compile(std::move(input)) << endl; return true; } @@ -771,9 +771,7 @@ void CommandLineInterface::handleCombinedJSON() } } - string json = m_options.formatting.prettyJson ? jsonPrettyPrint(removeNullMembers(std::move(output))) : - jsonCompactPrint(removeNullMembers(std::move(output))); - + string json = jsonPrint(removeNullMembers(std::move(output)), m_options.formatting.json); if (!m_options.output.dir.empty()) createJson("combined", json); else diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index c09ff906b..adbb1ca38 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -125,6 +125,7 @@ static string const g_strStandardJSON = "standard-json"; static string const g_strStrictAssembly = "strict-assembly"; static string const g_strSwarm = "swarm"; static string const g_strPrettyJson = "pretty-json"; +static string const g_strJsonIndent = "json-indent"; static string const g_strVersion = "version"; static string const g_strIgnoreMissingFiles = "ignore-missing"; static string const g_strColor = "color"; @@ -281,7 +282,7 @@ bool CommandLineOptions::operator==(CommandLineOptions const& _other) const noex assembly.targetMachine == _other.assembly.targetMachine && assembly.inputLanguage == _other.assembly.inputLanguage && linker.libraries == _other.linker.libraries && - formatting.prettyJson == _other.formatting.prettyJson && + formatting.json == _other.formatting.json && formatting.coloredOutput == _other.formatting.coloredOutput && formatting.withErrorIds == _other.formatting.withErrorIds && compiler.outputs == _other.compiler.outputs && @@ -582,7 +583,12 @@ General Information)").c_str(), outputFormatting.add_options() ( g_strPrettyJson.c_str(), - "Output JSON in pretty format. Currently it only works with the combined JSON output." + "Output JSON in pretty format." + ) + ( + g_strJsonIndent.c_str(), + po::value()->value_name("N")->default_value(util::JsonFormat::defaultIndent), + "Indent pretty-printed JSON with N spaces. Enables '--pretty-json' automatically." ) ( g_strColor.c_str(), @@ -790,7 +796,16 @@ General Information)").c_str(), m_options.output.dir = m_args.at(g_strOutputDir).as(); m_options.output.overwriteFiles = (m_args.count(g_strOverwrite) > 0); - m_options.formatting.prettyJson = (m_args.count(g_strPrettyJson) > 0); + + if (m_args.count(g_strPrettyJson) > 0) + { + m_options.formatting.json.format = JsonFormat::Pretty; + } + if (!m_args[g_strJsonIndent].defaulted()) + { + m_options.formatting.json.format = JsonFormat::Pretty; + m_options.formatting.json.indent = m_args[g_strJsonIndent].as(); + } static_assert( sizeof(m_options.compiler.outputs) == 15 * sizeof(bool), diff --git a/solc/CommandLineParser.h b/solc/CommandLineParser.h index b05caa705..539379216 100644 --- a/solc/CommandLineParser.h +++ b/solc/CommandLineParser.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -101,6 +102,7 @@ struct CommandLineOptions bool operator==(CommandLineOptions const& _other) const noexcept; bool operator!=(CommandLineOptions const& _other) const noexcept { return !(*this == _other); } + struct { InputMode mode = InputMode::Compiler; @@ -137,7 +139,7 @@ struct CommandLineOptions struct { - bool prettyJson = false; + util::JsonFormat json; std::optional coloredOutput; bool withErrorIds = false; } formatting; @@ -168,6 +170,7 @@ struct CommandLineOptions bool initialize = false; ModelCheckerSettings settings; } modelChecker; + }; /// Parses the command-line arguments and produces a filled-out CommandLineOptions structure. diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index a864ce17a..7deaa0c32 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -141,9 +141,16 @@ function test_solc_behaviour() if [[ " ${solc_args[*]} " == *" --standard-json "* ]] then - sed -i.bak -e 's/{[^{]*Warning: This is a pre-release compiler version[^}]*},\{0,1\}//' "$stdout_path" + python3 - < }] +json = re.sub(r"\"errors\":\s*\[\s*\],?\s*","",json) # Remove "errors" array if it's not empty +json = re.sub("\n\\s+\n", "\n\n", json) # Remove any leftover trailing whitespace +open("$stdout_path", "w").write(json) +EOF sed -i.bak -E -e 's/ Consider adding \\"pragma solidity \^[0-9.]*;\\"//g' "$stdout_path" - sed -i.bak -e 's/"errors":\[\],\{0,1\}//' "$stdout_path" sed -i.bak -E -e 's/\"opcodes\":\"[^"]+\"/\"opcodes\":\"\"/g' "$stdout_path" sed -i.bak -E -e 's/\"sourceMap\":\"[0-9:;-]+\"/\"sourceMap\":\"\"/g' "$stdout_path" diff --git a/test/cmdlineTests/pretty_json_combined/args b/test/cmdlineTests/pretty_json_combined/args new file mode 100644 index 000000000..f2ed65c2e --- /dev/null +++ b/test/cmdlineTests/pretty_json_combined/args @@ -0,0 +1 @@ +--combined-json abi --pretty-json --json-indent 3 diff --git a/test/cmdlineTests/pretty_json_combined/input.sol b/test/cmdlineTests/pretty_json_combined/input.sol new file mode 100644 index 000000000..625af5682 --- /dev/null +++ b/test/cmdlineTests/pretty_json_combined/input.sol @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C {} diff --git a/test/cmdlineTests/pretty_json_combined/output b/test/cmdlineTests/pretty_json_combined/output new file mode 100644 index 000000000..6bed3b94d --- /dev/null +++ b/test/cmdlineTests/pretty_json_combined/output @@ -0,0 +1,10 @@ +{ + "contracts": + { + "pretty_json_combined/input.sol:C": + { + "abi": [] + } + }, + "version": "" +} diff --git a/test/cmdlineTests/pretty_json_indent_only/args b/test/cmdlineTests/pretty_json_indent_only/args new file mode 100644 index 000000000..10c94a50c --- /dev/null +++ b/test/cmdlineTests/pretty_json_indent_only/args @@ -0,0 +1 @@ +--json-indent 7 diff --git a/test/cmdlineTests/pretty_json_indent_only/input.json b/test/cmdlineTests/pretty_json_indent_only/input.json new file mode 100644 index 000000000..e9dc4cf38 --- /dev/null +++ b/test/cmdlineTests/pretty_json_indent_only/input.json @@ -0,0 +1,10 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; contract C {}" + } + } +} diff --git a/test/cmdlineTests/pretty_json_indent_only/output b/test/cmdlineTests/pretty_json_indent_only/output new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/test/cmdlineTests/pretty_json_indent_only/output @@ -0,0 +1 @@ + diff --git a/test/cmdlineTests/pretty_json_indent_only/output.json b/test/cmdlineTests/pretty_json_indent_only/output.json new file mode 100644 index 000000000..80d51c4f1 --- /dev/null +++ b/test/cmdlineTests/pretty_json_indent_only/output.json @@ -0,0 +1,9 @@ +{ + "sources": + { + "A": + { + "id": 0 + } + } +} diff --git a/test/cmdlineTests/pretty_json_standard/args b/test/cmdlineTests/pretty_json_standard/args new file mode 100644 index 000000000..d13a8ac44 --- /dev/null +++ b/test/cmdlineTests/pretty_json_standard/args @@ -0,0 +1 @@ +--pretty-json diff --git a/test/cmdlineTests/pretty_json_standard/input.json b/test/cmdlineTests/pretty_json_standard/input.json new file mode 100644 index 000000000..e9dc4cf38 --- /dev/null +++ b/test/cmdlineTests/pretty_json_standard/input.json @@ -0,0 +1,10 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; contract C {}" + } + } +} diff --git a/test/cmdlineTests/pretty_json_standard/output.json b/test/cmdlineTests/pretty_json_standard/output.json new file mode 100644 index 000000000..29185e758 --- /dev/null +++ b/test/cmdlineTests/pretty_json_standard/output.json @@ -0,0 +1,9 @@ +{ + "sources": + { + "A": + { + "id": 0 + } + } +} diff --git a/test/cmdlineTests/pretty_json_standard_indent/args b/test/cmdlineTests/pretty_json_standard_indent/args new file mode 100644 index 000000000..54a84919f --- /dev/null +++ b/test/cmdlineTests/pretty_json_standard_indent/args @@ -0,0 +1 @@ +--pretty-json --json-indent 7 diff --git a/test/cmdlineTests/pretty_json_standard_indent/input.json b/test/cmdlineTests/pretty_json_standard_indent/input.json new file mode 100644 index 000000000..df5bb3dfc --- /dev/null +++ b/test/cmdlineTests/pretty_json_standard_indent/input.json @@ -0,0 +1,10 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; contract C { }" + } + } +} diff --git a/test/cmdlineTests/pretty_json_standard_indent/output b/test/cmdlineTests/pretty_json_standard_indent/output new file mode 100644 index 000000000..80d51c4f1 --- /dev/null +++ b/test/cmdlineTests/pretty_json_standard_indent/output @@ -0,0 +1,9 @@ +{ + "sources": + { + "A": + { + "id": 0 + } + } +} diff --git a/test/cmdlineTests/pretty_json_standard_indent/output.json b/test/cmdlineTests/pretty_json_standard_indent/output.json new file mode 100644 index 000000000..80d51c4f1 --- /dev/null +++ b/test/cmdlineTests/pretty_json_standard_indent/output.json @@ -0,0 +1,9 @@ +{ + "sources": + { + "A": + { + "id": 0 + } + } +} diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp index 430399c5c..015c28b98 100644 --- a/test/solc/CommandLineParser.cpp +++ b/test/solc/CommandLineParser.cpp @@ -138,6 +138,7 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) "--experimental-via-ir", "--revert-strings=strip", "--pretty-json", + "--json-indent=7", "--no-color", "--error-codes", "--libraries=" @@ -173,6 +174,7 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) {"a", "b", "c/d"}, {"", "contract.sol", ""}, }; + expectedOptions.input.addStdin = true; expectedOptions.input.basePath = "/home/user/"; expectedOptions.input.allowedDirectories = {"/tmp", "/home", "project", "../contracts", "", "c", "/usr/lib"}; @@ -183,11 +185,11 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) expectedOptions.output.evmVersion = EVMVersion::spuriousDragon(); expectedOptions.output.experimentalViaIR = true; expectedOptions.output.revertStrings = RevertStrings::Strip; + expectedOptions.formatting.json = JsonFormat{JsonFormat::Pretty, 7}; expectedOptions.linker.libraries = { {"dir1/file1.sol:L", h160("1234567890123456789012345678901234567890")}, {"dir2/file2.sol:L", h160("1111122222333334444455555666667777788888")}, }; - expectedOptions.formatting.prettyJson = true; expectedOptions.formatting.coloredOutput = false; expectedOptions.formatting.withErrorIds = true; expectedOptions.compiler.outputs = { @@ -269,6 +271,7 @@ BOOST_AUTO_TEST_CASE(assembly_mode_options) "--experimental-via-ir", // Ignored in assembly mode "--revert-strings=strip", // Accepted but has no effect in assembly mode "--pretty-json", + "--json-indent=1", "--no-color", "--error-codes", "--libraries=" @@ -313,13 +316,13 @@ BOOST_AUTO_TEST_CASE(assembly_mode_options) expectedOptions.output.overwriteFiles = true; expectedOptions.output.evmVersion = EVMVersion::spuriousDragon(); expectedOptions.output.revertStrings = RevertStrings::Strip; + expectedOptions.formatting.json = JsonFormat {JsonFormat::Pretty, 1}; expectedOptions.assembly.targetMachine = expectedMachine; expectedOptions.assembly.inputLanguage = expectedLanguage; expectedOptions.linker.libraries = { {"dir1/file1.sol:L", h160("1234567890123456789012345678901234567890")}, {"dir2/file2.sol:L", h160("1111122222333334444455555666667777788888")}, }; - expectedOptions.formatting.prettyJson = true; expectedOptions.formatting.coloredOutput = false; expectedOptions.formatting.withErrorIds = true; expectedOptions.compiler.outputs = { @@ -359,7 +362,8 @@ BOOST_AUTO_TEST_CASE(standard_json_mode_options) "--evm-version=spuriousDragon", // Ignored in Standard JSON mode "--experimental-via-ir", // Ignored in Standard JSON mode "--revert-strings=strip", // Accepted but has no effect in Standard JSON mode - "--pretty-json", // Accepted but has no effect in Standard JSON mode + "--pretty-json", + "--json-indent=1", "--no-color", // Accepted but has no effect in Standard JSON mode "--error-codes", // Accepted but has no effect in Standard JSON mode "--libraries=" // Ignored in Standard JSON mode @@ -387,6 +391,7 @@ BOOST_AUTO_TEST_CASE(standard_json_mode_options) }; CommandLineOptions expectedOptions; + expectedOptions.input.mode = InputMode::StandardJson; expectedOptions.input.paths = {}; expectedOptions.input.standardJsonFile = "input.json"; @@ -395,7 +400,7 @@ BOOST_AUTO_TEST_CASE(standard_json_mode_options) expectedOptions.output.dir = "/tmp/out"; expectedOptions.output.overwriteFiles = true; expectedOptions.output.revertStrings = RevertStrings::Strip; - expectedOptions.formatting.prettyJson = true; + expectedOptions.formatting.json = JsonFormat {JsonFormat::Pretty, 1}; expectedOptions.formatting.coloredOutput = false; expectedOptions.formatting.withErrorIds = true; expectedOptions.compiler.outputs = { From 3ea2854e97fa0912d031f63421b42c7f3bd8bb06 Mon Sep 17 00:00:00 2001 From: qedk <1994constant@gmail.com> Date: Mon, 26 Jul 2021 23:48:33 +0530 Subject: [PATCH 69/98] Update list of reserved keywords in cheatsheet - `unchecked` (added) + `var` (removed) + `byte` (removed) --- docs/cheatsheet.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/cheatsheet.rst b/docs/cheatsheet.rst index 4b18c63ac..6f4805806 100644 --- a/docs/cheatsheet.rst +++ b/docs/cheatsheet.rst @@ -192,8 +192,8 @@ Reserved Keywords These keywords are reserved in Solidity. They might become part of the syntax in the future: -``after``, ``alias``, ``apply``, ``auto``, ``case``, ``copyof``, ``default``, +``after``, ``alias``, ``apply``, ``auto``, ``byte``, ``case``, ``copyof``, ``default``, ``define``, ``final``, ``implements``, ``in``, ``inline``, ``let``, ``macro``, ``match``, ``mutable``, ``null``, ``of``, ``partial``, ``promise``, ``reference``, ``relocatable``, ``sealed``, ``sizeof``, ``static``, ``supports``, ``switch``, ``typedef``, ``typeof``, -``unchecked``. +``var``. From 1b56fdea9dc882bc7547c3c0641eb07a7b0cbd02 Mon Sep 17 00:00:00 2001 From: hrkrshnn Date: Tue, 27 Jul 2021 15:20:49 +0200 Subject: [PATCH 70/98] Updated dockerfiles for evmone 0.8.0 --- .../buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz | 8 ++++---- scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 | 7 +++---- scripts/docker/buildpack-deps/Dockerfile.ubuntu2004.clang | 7 +++---- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz b/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz index 80aa969c3..5e32ef9bf 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz @@ -22,7 +22,7 @@ # (c) 2016-2019 solidity contributors. #------------------------------------------------------------------------------ FROM gcr.io/oss-fuzz-base/base-clang as base -LABEL version="10" +LABEL version="11" ARG DEBIAN_FRONTEND=noninteractive @@ -92,7 +92,7 @@ RUN set -ex; \ # EVMONE RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.7.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ + git clone --branch="v0.8.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ cd evmone; \ mkdir build; \ cd build; \ @@ -104,7 +104,7 @@ RUN set -ex; \ # HERA RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.3.2-evmc8" --depth 1 --recurse-submodules https://github.com/ewasm/hera.git; \ + git clone --branch="v0.5.0" --depth 1 --recurse-submodules https://github.com/ewasm/hera.git; \ cd hera; \ mkdir build; \ cd build; \ @@ -146,4 +146,4 @@ RUN set -ex; \ FROM base COPY --from=libraries /usr/lib /usr/lib COPY --from=libraries /usr/bin /usr/bin -COPY --from=libraries /usr/include /usr/include \ No newline at end of file +COPY --from=libraries /usr/include /usr/include diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 index c27f7246b..63f09e513 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 @@ -22,7 +22,7 @@ # (c) 2016-2019 solidity contributors. #------------------------------------------------------------------------------ FROM buildpack-deps:focal AS base -LABEL version="7" +LABEL version="8" ARG DEBIAN_FRONTEND=noninteractive @@ -48,7 +48,7 @@ FROM base AS libraries # EVMONE RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.7.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ + git clone --branch="v0.8.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ cd evmone; \ mkdir build; \ cd build; \ @@ -60,7 +60,7 @@ RUN set -ex; \ # HERA RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.3.2-evmc8" --depth 1 --recurse-submodules https://github.com/ewasm/hera.git; \ + git clone --branch="v0.5.0" --depth 1 --recurse-submodules https://github.com/ewasm/hera.git; \ cd hera; \ mkdir build; \ cd build; \ @@ -73,4 +73,3 @@ FROM base COPY --from=libraries /usr/lib /usr/lib COPY --from=libraries /usr/bin /usr/bin COPY --from=libraries /usr/include /usr/include - diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004.clang b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004.clang index 89a0cef7f..1f96e41d5 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004.clang +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004.clang @@ -22,7 +22,7 @@ # (c) 2016-2019 solidity contributors. #------------------------------------------------------------------------------ FROM buildpack-deps:focal AS base -LABEL version="7" +LABEL version="8" ARG DEBIAN_FRONTEND=noninteractive @@ -50,7 +50,7 @@ ENV CXX clang++ # EVMONE RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.7.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ + git clone --branch="v0.8.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ cd evmone; \ mkdir build; \ cd build; \ @@ -62,7 +62,7 @@ RUN set -ex; \ # HERA RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.3.2-evmc8" --depth 1 --recurse-submodules https://github.com/ewasm/hera.git; \ + git clone --branch="v0.5.0" --depth 1 --recurse-submodules https://github.com/ewasm/hera.git; \ cd hera; \ mkdir build; \ cd build; \ @@ -75,4 +75,3 @@ FROM base COPY --from=libraries /usr/lib /usr/lib COPY --from=libraries /usr/bin /usr/bin COPY --from=libraries /usr/include /usr/include - From 3b104a3f386dcae96439bfeed29f56911139f4ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sun, 13 Jun 2021 14:28:03 +0200 Subject: [PATCH 71/98] CommandLineParser: Refactor checkMutuallyExclusive() to handle multiple options and use it more --- solc/CommandLineParser.cpp | 21 ++++++++------------- solc/CommandLineParser.h | 6 +----- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index adbb1ca38..fa2622119 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -198,14 +198,14 @@ void CommandLineParser::printLicenseAndExit() } -bool CommandLineParser::checkMutuallyExclusive(boost::program_options::variables_map const& args, string const& _optionA, string const& _optionB) +bool CommandLineParser::checkMutuallyExclusive(vector const& _optionNames) { - if (args.count(_optionA) && args.count(_optionB)) + if (countEnabledOptions(_optionNames) > 1) { - serr() << "Option " << _optionA << " and " << _optionB << " are mutually exclusive." << endl; + serr() << "The following options are mutually exclusive: " << joinOptionNames(_optionNames) << ". "; + serr() << "Select at most one." << endl; return false; } - return true; } @@ -735,7 +735,7 @@ General Information)").c_str(), return false; } - if (!checkMutuallyExclusive(m_args, g_strColor, g_strNoColor)) + if (!checkMutuallyExclusive({g_strColor, g_strNoColor})) return false; array const conflictingWithStopAfter{ @@ -750,7 +750,7 @@ General Information)").c_str(), }; for (auto& option: conflictingWithStopAfter) - if (!checkMutuallyExclusive(m_args, g_strStopAfter, option)) + if (!checkMutuallyExclusive({g_strStopAfter, option})) return false; if (m_args.count(g_strColor) > 0) @@ -861,20 +861,15 @@ General Information)").c_str(), m_options.output.stopAfter = CompilerStack::State::Parsed; } - vector const exclusiveModes = { + if (!checkMutuallyExclusive({ g_strStandardJSON, g_strLink, g_strAssemble, g_strStrictAssembly, g_strYul, g_strImportAst, - }; - if (countEnabledOptions(exclusiveModes) > 1) - { - serr() << "The following options are mutually exclusive: " << joinOptionNames(exclusiveModes) << ". "; - serr() << "Select at most one." << endl; + })) return false; - } if (m_args.count(g_strStandardJSON)) { diff --git a/solc/CommandLineParser.h b/solc/CommandLineParser.h index 539379216..375c961dc 100644 --- a/solc/CommandLineParser.h +++ b/solc/CommandLineParser.h @@ -218,11 +218,7 @@ private: /// @return false if there are any validation errors, true otherwise. bool parseLibraryOption(std::string const& _input); - bool checkMutuallyExclusive( - boost::program_options::variables_map const& args, - std::string const& _optionA, - std::string const& _optionB - ); + bool checkMutuallyExclusive(std::vector const& _optionNames); [[noreturn]] void printVersionAndExit(); [[noreturn]] void printLicenseAndExit(); size_t countEnabledOptions(std::vector const& _optionNames) const; From c938f35b993b35ec21f8dacb6116bd3722279624 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sun, 13 Jun 2021 14:30:16 +0200 Subject: [PATCH 72/98] CommandLineParser: Initialize inputMode earlier to make it usable in more functions --- solc/CommandLineParser.cpp | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index fa2622119..864282c21 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -871,10 +871,19 @@ General Information)").c_str(), })) return false; - if (m_args.count(g_strStandardJSON)) - { + if (m_args.count(g_strStandardJSON) > 0) m_options.input.mode = InputMode::StandardJson; + else if (m_args.count(g_strAssemble) > 0 || m_args.count(g_strStrictAssembly) > 0 || m_args.count(g_strYul) > 0) + m_options.input.mode = InputMode::Assembler; + else if (m_args.count(g_strLink) > 0) + m_options.input.mode = InputMode::Linker; + else if (m_args.count(g_strImportAst) > 0) + m_options.input.mode = InputMode::CompilerWithASTImport; + else + m_options.input.mode = InputMode::Compiler; + if (m_options.input.mode == InputMode::StandardJson) + { vector inputFiles; if (m_args.count(g_strInputFile)) inputFiles = m_args[g_strInputFile].as>(); @@ -909,10 +918,8 @@ General Information)").c_str(), m_options.output.evmVersion = *versionOption; } - if (m_args.count(g_strAssemble) || m_args.count(g_strStrictAssembly) || m_args.count(g_strYul)) + if (m_options.input.mode == InputMode::Assembler) { - m_options.input.mode = InputMode::Assembler; - vector const nonAssemblyModeOptions = { // TODO: The list is not complete. Add more. g_strOutputDir, @@ -1026,11 +1033,8 @@ General Information)").c_str(), return false; } - if (m_args.count(g_strLink)) - { - m_options.input.mode = InputMode::Linker; + if (m_options.input.mode == InputMode::Linker) return true; - } if (m_args.count(g_strMetadataHash)) { @@ -1123,9 +1127,7 @@ General Information)").c_str(), m_options.optimizer.yulSteps = m_args[g_strYulOptimizations].as(); } - if (m_args.count(g_strImportAst) > 0) - m_options.input.mode = InputMode::CompilerWithASTImport; - else + if (m_options.input.mode == InputMode::Compiler) m_options.input.errorRecovery = (m_args.count(g_strErrorRecovery) > 0); solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); From 9a7c364c716c375cb137b8d52f8f562c7824773d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sun, 13 Jun 2021 15:53:16 +0200 Subject: [PATCH 73/98] Process input files in Standard JSON mode just like in other modes - This makes `-` for stdin work. - `--ignore-missing` now works with `--standard-json`, though it's not very useful because there can be at most one input file. - Separate errors for situations where there are no input files on the command line (this can be detected in the parser) and where they are not present on disk. --- Changelog.md | 2 + solc/CommandLineInterface.cpp | 95 ++--- solc/CommandLineInterface.h | 8 +- solc/CommandLineParser.cpp | 47 ++- solc/CommandLineParser.h | 1 - solc/main.cpp | 2 + test/CMakeLists.txt | 3 + test/FilesystemUtils.cpp | 15 + test/FilesystemUtils.h | 5 + test/cmdlineTests/standard_file_not_found/err | 2 +- .../analysis/FunctionCallGraph.cpp | 2 +- test/solc/CommandLineInterface.cpp | 346 ++++++++++++++++++ test/solc/CommandLineParser.cpp | 32 +- test/solc/Common.cpp | 58 +++ test/solc/Common.h | 53 +++ test/yulPhaser/TestHelpers.h | 2 +- 16 files changed, 583 insertions(+), 90 deletions(-) create mode 100644 test/solc/CommandLineInterface.cpp create mode 100644 test/solc/Common.cpp create mode 100644 test/solc/Common.h diff --git a/Changelog.md b/Changelog.md index 042248fe7..490d054dd 100644 --- a/Changelog.md +++ b/Changelog.md @@ -15,6 +15,8 @@ Bugfixes: * Code Generator: Fix crash when passing an empty string literal to ``bytes.concat()``. * Code Generator: Fix internal compiler error when calling functions bound to calldata structs and arrays. * Code Generator: Fix internal compiler error when passing a 32-byte hex literal or a zero literal to ``bytes.concat()`` by disallowing such literals. + * Commandline Interface: Fix crash when a directory path is passed to ``--standard-json``. + * Commandline Interface: Read JSON from standard input when ``--standard-json`` gets ``-`` as a file name. * Standard JSON: Include source location for errors in files with empty name. * Type Checker: Fix internal error and prevent static calls to unimplemented modifiers. * Yul Code Generator: Fix internal compiler error when using a long literal with bitwise negation. diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 1b7f2d089..91f2baf9a 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -377,8 +377,18 @@ void CommandLineInterface::handleGasEstimation(string const& _contract) } } -bool CommandLineInterface::readInputFilesAndConfigureFileReader() +bool CommandLineInterface::readInputFiles() { + solAssert(!m_standardJsonInput.has_value(), ""); + + m_fileReader.setBasePath(m_options.input.basePath); + + if (m_fileReader.basePath() != "" && !boost::filesystem::is_directory(m_fileReader.basePath())) + { + serr() << "Base path must be a directory: " << m_fileReader.basePath() << endl; + return false; + } + for (boost::filesystem::path const& allowedDirectory: m_options.input.allowedDirectories) m_fileReader.allowDirectory(allowedDirectory); @@ -411,16 +421,33 @@ bool CommandLineInterface::readInputFilesAndConfigureFileReader() } // NOTE: we ignore the FileNotFound exception as we manually check above - m_fileReader.setSource(infile, readFileAsString(infile.string())); - m_fileReader.allowDirectory(boost::filesystem::path(boost::filesystem::canonical(infile).string()).remove_filename()); + string fileContent = readFileAsString(infile.string()); + if (m_options.input.mode == InputMode::StandardJson) + { + solAssert(!m_standardJsonInput.has_value(), ""); + m_standardJsonInput = move(fileContent); + } + else + { + m_fileReader.setSource(infile, move(fileContent)); + m_fileReader.allowDirectory(boost::filesystem::canonical(infile).remove_filename()); + } } if (m_options.input.addStdin) - m_fileReader.setSource(g_stdinFileName, readUntilEnd(m_sin)); - - if (m_fileReader.sourceCodes().size() == 0) { - serr() << "No input files given. If you wish to use the standard input please specify \"-\" explicitly." << endl; + if (m_options.input.mode == InputMode::StandardJson) + { + solAssert(!m_standardJsonInput.has_value(), ""); + m_standardJsonInput = readUntilEnd(m_sin); + } + else + m_fileReader.setSource(g_stdinFileName, readUntilEnd(m_sin)); + } + + if (m_fileReader.sourceCodes().empty() && !m_standardJsonInput.has_value()) + { + serr() << "All specified input files either do not exist or are not regular files." << endl; return false; } @@ -502,57 +529,35 @@ bool CommandLineInterface::parseArguments(int _argc, char const* const* _argv) bool CommandLineInterface::processInput() { - m_fileReader.setBasePath(m_options.input.basePath); - - if (m_options.input.basePath != "" && !boost::filesystem::is_directory(m_options.input.basePath)) + switch (m_options.input.mode) { - serr() << "Base path must be a directory: \"" << m_options.input.basePath << "\"\n"; - return false; - } - - if (m_options.input.mode == InputMode::StandardJson) + case InputMode::StandardJson: { - string input; - if (m_options.input.standardJsonFile.empty()) - input = readUntilEnd(m_sin); - else - { - try - { - input = readFileAsString(m_options.input.standardJsonFile); - } - catch (FileNotFound const&) - { - serr() << "File not found: " << m_options.input.standardJsonFile << endl; - return false; - } - catch (NotAFile const&) - { - serr() << "Not a regular file: " << m_options.input.standardJsonFile << endl; - return false; - } - } + solAssert(m_standardJsonInput.has_value(), ""); + StandardCompiler compiler(m_fileReader.reader(), m_options.formatting.json); - sout() << compiler.compile(std::move(input)) << endl; + sout() << compiler.compile(move(m_standardJsonInput.value())) << endl; + m_standardJsonInput.reset(); return true; } - - if (!readInputFilesAndConfigureFileReader()) - return false; - - if (m_options.input.mode == InputMode::Assembler) + case InputMode::Assembler: + { return assemble( m_options.assembly.inputLanguage, m_options.assembly.targetMachine, m_options.optimizer.enabled, m_options.optimizer.yulSteps ); - - if (m_options.input.mode == InputMode::Linker) + } + case InputMode::Linker: return link(); + case InputMode::Compiler: + case InputMode::CompilerWithASTImport: + return compile(); + } - solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); - return compile(); + solAssert(false, ""); + return false; } bool CommandLineInterface::compile() diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 57674355a..8e17489a7 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -53,6 +53,8 @@ public: /// Parse command line arguments and return false if we should not continue bool parseArguments(int _argc, char const* const* _argv); + /// Read the content of all input files and initialize the file reader. + bool readInputFiles(); /// Parse the files and create source code objects bool processInput(); /// Perform actions on the input depending on provided compiler arguments @@ -61,6 +63,7 @@ public: CommandLineOptions const& options() const { return m_options; } FileReader const& fileReader() const { return m_fileReader; } + std::optional const& standardJsonInput() const { return m_standardJsonInput; } private: bool compile(); @@ -95,10 +98,6 @@ private: void handleGasEstimation(std::string const& _contract); void handleStorageLayout(std::string const& _contract); - /// Reads the content of input files specified on the command line and passes them to FileReader. - /// @return false if there are no input files or input files cannot be read. - bool readInputFilesAndConfigureFileReader(); - /// Tries to read @ m_sourceCodes as a JSONs holding ASTs /// such that they can be imported into the compiler (importASTs()) /// (produced by --combined-json ast,compact-format @@ -129,6 +128,7 @@ private: bool m_hasOutput = false; bool m_error = false; ///< If true, some error occurred. FileReader m_fileReader; + std::optional m_standardJsonInput; std::unique_ptr m_compiler; CommandLineOptions m_options; }; diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 864282c21..ed2f2c9b3 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -265,7 +265,6 @@ bool CommandLineOptions::operator==(CommandLineOptions const& _other) const noex { return input.paths == _other.input.paths && - input.standardJsonFile == _other.input.standardJsonFile && input.remappings == _other.input.remappings && input.addStdin == _other.input.addStdin && input.basePath == _other.input.basePath && @@ -301,12 +300,20 @@ bool CommandLineOptions::operator==(CommandLineOptions const& _other) const noex bool CommandLineParser::parseInputPathsAndRemappings() { m_options.input.ignoreMissingFiles = (m_args.count(g_strIgnoreMissingFiles) > 0); + if (m_args.count(g_strInputFile)) for (string path: m_args[g_strInputFile].as>()) { auto eq = find(path.begin(), path.end(), '='); if (eq != path.end()) { + if (m_options.input.mode == InputMode::StandardJson) + { + serr() << "Import remappings are not accepted on the command line in Standard JSON mode." << endl; + serr() << "Please put them under 'settings.remappings' in the JSON input." << endl; + return false; + } + if (auto r = ImportRemapper::parseRemapping(path)) m_options.input.remappings.emplace_back(std::move(*r)); else @@ -324,6 +331,25 @@ bool CommandLineParser::parseInputPathsAndRemappings() m_options.input.paths.insert(path); } + if (m_options.input.mode == InputMode::StandardJson) + { + if (m_options.input.paths.size() > 1 || (m_options.input.paths.size() == 1 && m_options.input.addStdin)) + { + serr() << "Too many input files for --" << g_strStandardJSON << "." << endl; + serr() << "Please either specify a single file name or provide its content on standard input." << endl; + return false; + } + else if (m_options.input.paths.size() == 0) + // Standard JSON mode input used to be handled separately and zero files meant "read from stdin". + // Keep it working that way for backwards-compatibility. + m_options.input.addStdin = true; + } + else if (m_options.input.paths.size() == 0 && !m_options.input.addStdin) + { + serr() << "No input files given. If you wish to use the standard input please specify \"-\" explicitly." << endl; + return false; + } + return true; } @@ -882,25 +908,12 @@ General Information)").c_str(), else m_options.input.mode = InputMode::Compiler; - if (m_options.input.mode == InputMode::StandardJson) - { - vector inputFiles; - if (m_args.count(g_strInputFile)) - inputFiles = m_args[g_strInputFile].as>(); - if (inputFiles.size() == 1) - m_options.input.standardJsonFile = inputFiles[0]; - else if (inputFiles.size() > 1) - { - serr() << "If --" << g_strStandardJSON << " is used, only zero or one input files are supported." << endl; - return false; - } - - return true; - } - if (!parseInputPathsAndRemappings()) return false; + if (m_options.input.mode == InputMode::StandardJson) + return true; + if (m_args.count(g_strLibraries)) for (string const& library: m_args[g_strLibraries].as>()) if (!parseLibraryOption(library)) diff --git a/solc/CommandLineParser.h b/solc/CommandLineParser.h index 375c961dc..176966601 100644 --- a/solc/CommandLineParser.h +++ b/solc/CommandLineParser.h @@ -107,7 +107,6 @@ struct CommandLineOptions { InputMode mode = InputMode::Compiler; std::set paths; - std::string standardJsonFile; std::vector remappings; bool addStdin = false; boost::filesystem::path basePath = ""; diff --git a/solc/main.cpp b/solc/main.cpp index 4c40f47f1..9eefc1afd 100644 --- a/solc/main.cpp +++ b/solc/main.cpp @@ -57,6 +57,8 @@ int main(int argc, char** argv) solidity::frontend::CommandLineInterface cli(cin, cout, cerr); if (!cli.parseArguments(argc, argv)) return 1; + if (!cli.readInputFiles()) + return 1; if (!cli.processInput()) return 1; bool success = false; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 17300c660..a70a92639 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -152,6 +152,9 @@ set(libyul_sources detect_stray_source_files("${libyul_sources}" "libyul/") set(solcli_sources + solc/Common.cpp + solc/Common.h + solc/CommandLineInterface.cpp solc/CommandLineParser.cpp ) detect_stray_source_files("${solcli_sources}" "solc/") diff --git a/test/FilesystemUtils.cpp b/test/FilesystemUtils.cpp index 08d484504..cdad58e62 100644 --- a/test/FilesystemUtils.cpp +++ b/test/FilesystemUtils.cpp @@ -24,6 +24,21 @@ using namespace std; using namespace solidity; using namespace solidity::test; +void solidity::test::createFilesWithParentDirs(set const& _paths, string const& _content) +{ + for (boost::filesystem::path const& path: _paths) + { + if (!path.parent_path().empty()) + boost::filesystem::create_directories(path.parent_path()); + + ofstream newFile(path.string()); + newFile << _content; + + if (newFile.fail() || !boost::filesystem::exists(path)) + BOOST_THROW_EXCEPTION(runtime_error("Failed to create an empty file: \"" + path.string() + "\".")); + } +} + void solidity::test::createFileWithContent(boost::filesystem::path const& _path, string const& content) { if (boost::filesystem::is_regular_file(_path)) diff --git a/test/FilesystemUtils.h b/test/FilesystemUtils.h index f8ad32044..291188114 100644 --- a/test/FilesystemUtils.h +++ b/test/FilesystemUtils.h @@ -23,11 +23,16 @@ #include +#include #include namespace solidity::test { +/// Creates all the specified files and fills them with the specifiedcontent. Creates their parent +/// directories if they do not exist. Throws an exception if any part of the operation does not succeed. +void createFilesWithParentDirs(std::set const& _paths, std::string const& _content = ""); + /// Creates a file with the exact content specified in the second argument. /// Throws an exception if the file already exists or if the parent directory of the file does not. void createFileWithContent(boost::filesystem::path const& _path, std::string const& content); diff --git a/test/cmdlineTests/standard_file_not_found/err b/test/cmdlineTests/standard_file_not_found/err index a97cc5b7a..db5590ed9 100644 --- a/test/cmdlineTests/standard_file_not_found/err +++ b/test/cmdlineTests/standard_file_not_found/err @@ -1 +1 @@ -File not found: input.sol +"input.sol" is not found. diff --git a/test/libsolidity/analysis/FunctionCallGraph.cpp b/test/libsolidity/analysis/FunctionCallGraph.cpp index e4cd3d066..de63be563 100644 --- a/test/libsolidity/analysis/FunctionCallGraph.cpp +++ b/test/libsolidity/analysis/FunctionCallGraph.cpp @@ -215,7 +215,7 @@ ostream& operator<<(ostream& _out, map> const& _map) namespace boost::test_tools::tt_detail { -// Boost won't find find the << operator unless we put it in the std namespace which is illegal. +// Boost won't find the << operator unless we put it in the std namespace which is illegal. // The recommended solution is to overload print_log_value<> struct and make it use our operator. template<> diff --git a/test/solc/CommandLineInterface.cpp b/test/solc/CommandLineInterface.cpp new file mode 100644 index 000000000..9c73c66ea --- /dev/null +++ b/test/solc/CommandLineInterface.cpp @@ -0,0 +1,346 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +/// Unit tests for solc/CommandLineInterface.h + +#include + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +using namespace std; +using namespace solidity::frontend; +using namespace solidity::test; + +using PathSet = set; + +#define TEST_CASE_NAME (boost::unit_test::framework::current_test_case().p_name) + +namespace +{ + +ostream& operator<<(ostream& _out, vector const& _remappings) +{ + static auto remappingToString = [](auto const& _remapping) + { + return _remapping.context + ":" + _remapping.prefix + "=" + _remapping.target; + }; + + _out << "[" << joinHumanReadable(_remappings | ranges::views::transform(remappingToString)) << "]"; + return _out; +} + +ostream& operator<<(ostream& _out, map const& _map) +{ + _out << "{" << endl; + for (auto const& [key, value]: _map) + _out << "" << key << ": " << value << "," << endl; + _out << "}"; + + return _out; +} + +ostream& operator<<(ostream& _out, PathSet const& _paths) +{ + static auto pathString = [](auto const& _path) { return _path.string(); }; + + _out << "{" << joinHumanReadable(_paths | ranges::views::transform(pathString)) << "}"; + return _out; +} + +} // namespace + +namespace boost::test_tools::tt_detail +{ + +// Boost won't find the << operator unless we put it in the std namespace which is illegal. +// The recommended solution is to overload print_log_value<> struct and make it use our operator. + +template<> +struct print_log_value> +{ + void operator()(std::ostream& _out, vector const& _value) { ::operator<<(_out, _value); } +}; + +template<> +struct print_log_value> +{ + void operator()(std::ostream& _out, map const& _value) { ::operator<<(_out, _value); } +}; + +template<> +struct print_log_value +{ + void operator()(std::ostream& _out, PathSet const& _value) { ::operator<<(_out, _value); } +}; + +} // namespace boost::test_tools::tt_detail + +namespace solidity::frontend::test +{ + +BOOST_AUTO_TEST_SUITE(CommandLineInterfaceTest) + +BOOST_AUTO_TEST_CASE(multiple_input_modes) +{ + array inputModeOptions = { + "--standard-json", + "--link", + "--assemble", + "--strict-assembly", + "--yul", + "--import-ast", + }; + string expectedMessage = + "The following options are mutually exclusive: " + "--standard-json, --link, --assemble, --strict-assembly, --yul, --import-ast. " + "Select at most one.\n"; + + for (string const& mode1: inputModeOptions) + for (string const& mode2: inputModeOptions) + if (mode1 != mode2) + { + vector commandLine = {"solc", mode1, mode2}; + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles(commandLine); + BOOST_TEST(!result.success); + BOOST_TEST(result.stderrContent == expectedMessage); + } +} + +BOOST_AUTO_TEST_CASE(cli_input) +{ + TemporaryDirectory tempDir1(TEST_CASE_NAME); + TemporaryDirectory tempDir2(TEST_CASE_NAME); + createFilesWithParentDirs({tempDir1.path() / "input1.sol"}); + createFilesWithParentDirs({tempDir2.path() / "input2.sol"}); + + vector expectedRemappings = { + {"", "a", "b/c/d"}, + {"a", "b", "c/d/e/"}, + }; + map expectedSources = { + {"", "\n"}, + {(tempDir1.path() / "input1.sol").generic_string(), ""}, + {(tempDir2.path() / "input2.sol").generic_string(), ""}, + }; + PathSet expectedAllowedPaths = { + boost::filesystem::canonical(tempDir1.path()), + boost::filesystem::canonical(tempDir2.path()), + "b/c", + "c/d/e", + }; + + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles({ + "solc", + "a=b/c/d", + (tempDir1.path() / "input1.sol").string(), + (tempDir2.path() / "input2.sol").string(), + "a:b=c/d/e/", + "-", + }); + + BOOST_TEST(result.success); + BOOST_TEST(result.stderrContent == ""); + BOOST_TEST(result.options.input.mode == InputMode::Compiler); + BOOST_TEST(result.options.input.addStdin); + BOOST_CHECK_EQUAL(result.options.input.remappings, expectedRemappings); + BOOST_CHECK_EQUAL(result.reader.sourceCodes(), expectedSources); + BOOST_CHECK_EQUAL(result.reader.allowedDirectories(), expectedAllowedPaths); +} + +BOOST_AUTO_TEST_CASE(cli_ignore_missing_some_files_exist) +{ + TemporaryDirectory tempDir1(TEST_CASE_NAME); + TemporaryDirectory tempDir2(TEST_CASE_NAME); + createFilesWithParentDirs({tempDir1.path() / "input1.sol"}); + + // NOTE: Allowed paths should not be added for skipped files. + map expectedSources = {{(tempDir1.path() / "input1.sol").generic_string(), ""}}; + PathSet expectedAllowedPaths = {boost::filesystem::canonical(tempDir1.path())}; + + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles({ + "solc", + (tempDir1.path() / "input1.sol").string(), + (tempDir2.path() / "input2.sol").string(), + "--ignore-missing", + }); + BOOST_TEST(result.success); + BOOST_TEST(result.stderrContent == "\"" + (tempDir2.path() / "input2.sol").string() + "\" is not found. Skipping.\n"); + BOOST_TEST(result.options.input.mode == InputMode::Compiler); + BOOST_TEST(!result.options.input.addStdin); + BOOST_CHECK_EQUAL(result.reader.sourceCodes(), expectedSources); + BOOST_CHECK_EQUAL(result.reader.allowedDirectories(), expectedAllowedPaths); +} + +BOOST_AUTO_TEST_CASE(cli_ignore_missing_no_files_exist) +{ + TemporaryDirectory tempDir(TEST_CASE_NAME); + + string expectedMessage = + "\"" + (tempDir.path() / "input1.sol").string() + "\" is not found. Skipping.\n" + "\"" + (tempDir.path() / "input2.sol").string() + "\" is not found. Skipping.\n" + "All specified input files either do not exist or are not regular files.\n"; + + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles({ + "solc", + (tempDir.path() / "input1.sol").string(), + (tempDir.path() / "input2.sol").string(), + "--ignore-missing", + }); + BOOST_TEST(!result.success); + BOOST_TEST(result.stderrContent == expectedMessage); +} + +BOOST_AUTO_TEST_CASE(cli_not_a_file) +{ + TemporaryDirectory tempDir(TEST_CASE_NAME); + + string expectedMessage = "\"" + tempDir.path().string() + "\" is not a valid file.\n"; + + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles({"solc", tempDir.path().string()}); + BOOST_TEST(!result.success); + BOOST_TEST(result.stderrContent == expectedMessage); +} + +BOOST_AUTO_TEST_CASE(standard_json_base_path) +{ + TemporaryDirectory tempDir(TEST_CASE_NAME); + + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles({ + "solc", + "--standard-json", + "--base-path=" + tempDir.path().string(), + }); + BOOST_TEST(result.success); + BOOST_TEST(result.stderrContent == ""); + BOOST_TEST(result.options.input.mode == InputMode::StandardJson); + BOOST_TEST(result.options.input.addStdin); + BOOST_TEST(result.options.input.paths.empty()); + BOOST_TEST(result.reader.sourceCodes().empty()); + BOOST_TEST(result.reader.allowedDirectories().empty()); + BOOST_TEST(result.reader.basePath() == tempDir.path()); +} + +BOOST_AUTO_TEST_CASE(standard_json_no_input_file) +{ + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles({"solc", "--standard-json"}); + BOOST_TEST(result.success); + BOOST_TEST(result.stderrContent == ""); + BOOST_TEST(result.options.input.mode == InputMode::StandardJson); + BOOST_TEST(result.options.input.addStdin); + BOOST_TEST(result.options.input.paths.empty()); + BOOST_TEST(result.reader.sourceCodes().empty()); + BOOST_TEST(result.reader.allowedDirectories().empty()); +} + +BOOST_AUTO_TEST_CASE(standard_json_dash) +{ + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles({"solc", "--standard-json", "-"}); + BOOST_TEST(result.success); + BOOST_TEST(result.stderrContent == ""); + BOOST_TEST(result.options.input.mode == InputMode::StandardJson); + BOOST_TEST(result.options.input.addStdin); + BOOST_TEST(result.reader.sourceCodes().empty()); + BOOST_TEST(result.reader.allowedDirectories().empty()); +} + +BOOST_AUTO_TEST_CASE(standard_json_one_input_file) +{ + TemporaryDirectory tempDir(TEST_CASE_NAME); + createFilesWithParentDirs({tempDir.path() / "input.json"}); + + vector commandLine = {"solc", "--standard-json", (tempDir.path() / "input.json").string()}; + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles(commandLine); + BOOST_TEST(result.success); + BOOST_TEST(result.stderrContent == ""); + BOOST_TEST(result.options.input.mode == InputMode::StandardJson); + BOOST_TEST(!result.options.input.addStdin); + BOOST_TEST(result.options.input.paths == PathSet{tempDir.path() / "input.json"}); + BOOST_TEST(result.reader.allowedDirectories().empty()); +} + +BOOST_AUTO_TEST_CASE(standard_json_two_input_files) +{ + string expectedMessage = + "Too many input files for --standard-json.\n" + "Please either specify a single file name or provide its content on standard input.\n"; + + vector commandLine = {"solc", "--standard-json", "input1.json", "input2.json"}; + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles(commandLine); + BOOST_TEST(!result.success); + BOOST_TEST(result.stderrContent == expectedMessage); +} + +BOOST_AUTO_TEST_CASE(standard_json_one_input_file_and_stdin) +{ + string expectedMessage = + "Too many input files for --standard-json.\n" + "Please either specify a single file name or provide its content on standard input.\n"; + + vector commandLine = {"solc", "--standard-json", "input1.json", "-"}; + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles(commandLine); + BOOST_TEST(!result.success); + BOOST_TEST(result.stderrContent == expectedMessage); +} + +BOOST_AUTO_TEST_CASE(standard_json_ignore_missing) +{ + TemporaryDirectory tempDir(TEST_CASE_NAME); + + // This option is pretty much useless Standard JSON mode. + string expectedMessage = + "\"" + (tempDir.path() / "input.json").string() + "\" is not found. Skipping.\n" + "All specified input files either do not exist or are not regular files.\n"; + + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles({ + "solc", + "--standard-json", + (tempDir.path() / "input.json").string(), + "--ignore-missing", + }); + BOOST_TEST(!result.success); + BOOST_TEST(result.stderrContent == expectedMessage); +} + +BOOST_AUTO_TEST_CASE(standard_json_remapping) +{ + string expectedMessage = + "Import remappings are not accepted on the command line in Standard JSON mode.\n" + "Please put them under 'settings.remappings' in the JSON input.\n"; + + vector commandLine = {"solc", "--standard-json", "a=b"}; + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles(commandLine); + BOOST_TEST(!result.success); + BOOST_TEST(result.stderrContent == expectedMessage); +} + +BOOST_AUTO_TEST_SUITE_END() + +} // namespace solidity::frontend::test diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp index 015c28b98..3f12b85fd 100644 --- a/test/solc/CommandLineParser.cpp +++ b/test/solc/CommandLineParser.cpp @@ -20,6 +20,8 @@ #include +#include + #include #include @@ -28,7 +30,6 @@ #include #include -#include #include #include @@ -46,20 +47,13 @@ using namespace solidity::yul; namespace { -optional parseCommandLine(vector const& commandLine, ostream& _stdout, ostream& _stderr) +optional parseCommandLine(vector const& _commandLine, ostream& _stdout, ostream& _stderr) { - size_t argc = commandLine.size(); - vector argv(argc + 1); - - // argv[argc] typically contains NULL - argv[argc] = nullptr; - - for (size_t i = 0; i < argc; ++i) - argv[i] = commandLine[i].c_str(); + vector argv = test::makeArgv(_commandLine); CommandLineParser cliParser(_stdout, _stderr); bool success = cliParser.parse( - static_cast(argc), + static_cast(_commandLine.size()), argv.data(), false // interactiveTerminal ); @@ -70,7 +64,6 @@ optional parseCommandLine(vector const& commandLine, return cliParser.options(); } - } // namespace namespace solidity::frontend::test @@ -99,7 +92,7 @@ BOOST_AUTO_TEST_CASE(no_options) BOOST_TEST(sout.str() == ""); BOOST_TEST(serr.str() == ""); BOOST_REQUIRE(parsedOptions.has_value()); - BOOST_TEST((parsedOptions.value() == expectedOptions)); + BOOST_TEST(parsedOptions.value() == expectedOptions); } BOOST_AUTO_TEST_CASE(help) @@ -224,7 +217,7 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) BOOST_TEST(sout.str() == ""); BOOST_TEST(serr.str() == ""); BOOST_REQUIRE(parsedOptions.has_value()); - BOOST_TEST((parsedOptions.value() == expectedOptions)); + BOOST_TEST(parsedOptions.value() == expectedOptions); } } @@ -342,8 +335,7 @@ BOOST_AUTO_TEST_CASE(assembly_mode_options) BOOST_TEST(sout.str() == ""); BOOST_TEST(serr.str() == "Warning: Yul is still experimental. Please use the output with care.\n"); BOOST_REQUIRE(parsedOptions.has_value()); - - BOOST_TEST((parsedOptions.value() == expectedOptions)); + BOOST_TEST(parsedOptions.value() == expectedOptions); } } @@ -355,7 +347,7 @@ BOOST_AUTO_TEST_CASE(standard_json_mode_options) "--standard-json", "--base-path=/home/user/", "--allow-paths=/tmp,/home,project,../contracts", - "--ignore-missing", // Ignored in Standard JSON mode + "--ignore-missing", "--error-recovery", // Ignored in Standard JSON mode "--output-dir=/tmp/out", // Accepted but has no effect in Standard JSON mode "--overwrite", // Accepted but has no effect in Standard JSON mode @@ -393,10 +385,10 @@ BOOST_AUTO_TEST_CASE(standard_json_mode_options) CommandLineOptions expectedOptions; expectedOptions.input.mode = InputMode::StandardJson; - expectedOptions.input.paths = {}; - expectedOptions.input.standardJsonFile = "input.json"; + expectedOptions.input.paths = {"input.json"}; expectedOptions.input.basePath = "/home/user/"; expectedOptions.input.allowedDirectories = {"/tmp", "/home", "project", "../contracts"}; + expectedOptions.input.ignoreMissingFiles = true; expectedOptions.output.dir = "/tmp/out"; expectedOptions.output.overwriteFiles = true; expectedOptions.output.revertStrings = RevertStrings::Strip; @@ -419,7 +411,7 @@ BOOST_AUTO_TEST_CASE(standard_json_mode_options) BOOST_TEST(sout.str() == ""); BOOST_TEST(serr.str() == ""); BOOST_REQUIRE(parsedOptions.has_value()); - BOOST_TEST((parsedOptions.value() == expectedOptions)); + BOOST_TEST(parsedOptions.value() == expectedOptions); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/solc/Common.cpp b/test/solc/Common.cpp new file mode 100644 index 000000000..1bd209067 --- /dev/null +++ b/test/solc/Common.cpp @@ -0,0 +1,58 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include + +#include + +using namespace std; +using namespace solidity::frontend; + +vector test::makeArgv(vector const& _commandLine) +{ + size_t argc = _commandLine.size(); + vector argv(_commandLine.size() + 1); + + // C++ standard mandates argv[argc] to be NULL + argv[argc] = nullptr; + + for (size_t i = 0; i < argc; ++i) + argv[i] = _commandLine[i].c_str(); + + return argv; +} + +test::OptionsReaderAndMessages test::parseCommandLineAndReadInputFiles( + vector const& _commandLine, + string const& _standardInputContent, + bool _processInput +) +{ + vector argv = makeArgv(_commandLine); + stringstream sin(_standardInputContent), sout, serr; + CommandLineInterface cli(sin, sout, serr); + bool success = cli.parseArguments(static_cast(_commandLine.size()), argv.data()); + if (success) + success = cli.readInputFiles(); + if (success && _processInput) + success = cli.processInput(); + + return {success, cli.options(), cli.fileReader(), cli.standardJsonInput(), sout.str(), serr.str()}; +} diff --git a/test/solc/Common.h b/test/solc/Common.h new file mode 100644 index 000000000..6d5e3875d --- /dev/null +++ b/test/solc/Common.h @@ -0,0 +1,53 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +/// Utilities shared by multiple tests for code in solc/. + +#include + +#include + +#include +#include +#include + +BOOST_TEST_DONT_PRINT_LOG_VALUE(solidity::frontend::CommandLineOptions) +BOOST_TEST_DONT_PRINT_LOG_VALUE(solidity::frontend::InputMode) + +namespace solidity::frontend::test +{ + +struct OptionsReaderAndMessages +{ + bool success; + CommandLineOptions options; + FileReader reader; + std::optional standardJsonInput; + std::string stdoutContent; + std::string stderrContent; +}; + +std::vector makeArgv(std::vector const& _commandLine); + +OptionsReaderAndMessages parseCommandLineAndReadInputFiles( + std::vector const& _commandLine, + std::string const& _standardInputContent = "", + bool _processInput = false +); + +} // namespace solidity::frontend::test diff --git a/test/yulPhaser/TestHelpers.h b/test/yulPhaser/TestHelpers.h index 31a5907f3..f9d0afa4c 100644 --- a/test/yulPhaser/TestHelpers.h +++ b/test/yulPhaser/TestHelpers.h @@ -57,7 +57,7 @@ std::ostream& operator<<(std::ostream& _output, std::tuple const& _tuple namespace boost::test_tools::tt_detail { -// Boost won't find find the << operator unless we put it in the std namespace which is illegal. +// Boost won't find the << operator unless we put it in the std namespace which is illegal. // The recommended solution is to overload print_log_value<> struct and make it use our global operator. template struct print_log_value> From 6b8c32b8599d9a32d8e31ed0347a155b8e0a4e9c Mon Sep 17 00:00:00 2001 From: hrkrshnn Date: Tue, 27 Jul 2021 16:38:48 +0200 Subject: [PATCH 74/98] Updated circleci scripts with evmone 0.8.0 docker images --- .circleci/config.yml | 12 ++++++------ .circleci/osx_install_dependencies.sh | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 93e9f93bd..1da228443 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,16 +9,16 @@ version: 2.1 parameters: ubuntu-2004-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-7 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:af5a0c6ea5e113e477f5387955a862f9aea5cc74d9ceeb2377fc64e64088d200" + # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-8 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:9c3cdfc1d573d1ca3edacd892590a9a83487a1f746a6ca2093d7e009818c5179" ubuntu-2004-clang-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-7 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:cb504c6456d4a2dd80b354acfd7429836da5acce4e394500c02d5740617f9d01" + # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-8 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:61232feea23c8c57e82cf5fae890f8b86bbec353cdc04f2fcba383ca589e1d8b" ubuntu-1604-clang-ossfuzz-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-10 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:ea15a35f6188360b425593c83e946660ab4ea4dac9b9c3bb3629e6ed57276b1d" + # solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-11 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:4acb2674eab3e7939d6dc6caa0b8320f4dd79484325242b58473ca2875792d90" emscripten-docker-image: type: string # solbuildpackpusher/solidity-buildpack-deps:emscripten-6 diff --git a/.circleci/osx_install_dependencies.sh b/.circleci/osx_install_dependencies.sh index 55d406946..0505aa6db 100755 --- a/.circleci/osx_install_dependencies.sh +++ b/.circleci/osx_install_dependencies.sh @@ -59,12 +59,12 @@ then rm -rf "$z3_version-x64-$osx_version" # evmone - wget https://github.com/ethereum/evmone/releases/download/v0.7.0/evmone-0.7.0-darwin-x86_64.tar.gz - tar xzpf evmone-0.7.0-darwin-x86_64.tar.gz -C /usr/local - rm -f evmone-0.7.0-darwin-x86_64.tar.gz + wget https://github.com/ethereum/evmone/releases/download/v0.8.0/evmone-0.8.0-darwin-x86_64.tar.gz + tar xzpf evmone-0.8.0-darwin-x86_64.tar.gz -C /usr/local + rm -f evmone-0.8.0-darwin-x86_64.tar.gz # hera - wget https://github.com/ewasm/hera/releases/download/v0.3.2-evmc8/hera-0.3.2+commit.dc886eb7-darwin-x86_64.tar.gz - tar xzpf hera-0.3.2+commit.dc886eb7-darwin-x86_64.tar.gz -C /usr/local - rm -f hera-0.3.2+commit.dc886eb7-darwin-x86_64.tar.gz + wget https://github.com/ewasm/hera/releases/download/v0.5.0/hera-0.5.0-darwin-x86_64.tar.gz + tar xzpf hera-0.5.0-darwin-x86_64.tar.gz -C /usr/local + rm -f hera-0.5.0-darwin-x86_64.tar.gz fi From 8bec0c8a6b0b1222136009b8ade65a03a56286c0 Mon Sep 17 00:00:00 2001 From: hrkrshnn Date: Tue, 27 Jul 2021 16:39:41 +0200 Subject: [PATCH 75/98] Updated docs and evmc.h header file for evmone 0.8.0 release --- docs/contributing.rst | 2 +- scripts/install_evmone.ps1 | 2 +- test/Common.h | 10 +++++----- test/evmc/README.md | 2 +- test/evmc/evmc.h | 30 ++++++++++++++++++++++++++---- 5 files changed, 34 insertions(+), 12 deletions(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index 62d5bad1f..1d621d4a9 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -112,7 +112,7 @@ starting from the current directory. The required file is called ``libevmone.so` ``evmone.dll`` on Windows systems and ``libevmone.dylib`` on macOS. If it is not found, tests that use it are skipped. These tests are ``libsolididty/semanticTests``, ``libsolidity/GasCosts``, ``libsolidity/SolidityEndToEndTest``, part of the soltest suite. To run all tests, download the library from -`GitHub `_ +`GitHub `_ and place it in the project root path or inside the ``deps`` folder. If the ``libz3`` library is not installed on your system, you should disable the diff --git a/scripts/install_evmone.ps1 b/scripts/install_evmone.ps1 index ea911d3da..ace3e5751 100644 --- a/scripts/install_evmone.ps1 +++ b/scripts/install_evmone.ps1 @@ -3,7 +3,7 @@ $ErrorActionPreference = "Stop" # Needed for Invoke-WebRequest to work via CI. $progressPreference = "silentlyContinue" -Invoke-WebRequest -URI "https://github.com/ethereum/evmone/releases/download/v0.7.0/evmone-0.7.0-windows-amd64.zip" -OutFile "evmone.zip" +Invoke-WebRequest -URI "https://github.com/ethereum/evmone/releases/download/v0.8.0/evmone-0.8.0-windows-amd64.zip" -OutFile "evmone.zip" tar -xf evmone.zip "bin/evmone.dll" mkdir deps mv bin/evmone.dll deps diff --git a/test/Common.h b/test/Common.h index 4dfaeaed5..c8a863d8c 100644 --- a/test/Common.h +++ b/test/Common.h @@ -31,19 +31,19 @@ namespace solidity::test #ifdef _WIN32 static constexpr auto evmoneFilename = "evmone.dll"; -static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.7.0/evmone-0.7.0-windows-amd64.zip"; +static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.8.0/evmone-0.8.0-windows-amd64.zip"; static constexpr auto heraFilename = "hera.dll"; static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/archive/v0.3.2-evmc8.tar.gz"; #elif defined(__APPLE__) static constexpr auto evmoneFilename = "libevmone.dylib"; -static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.7.0/evmone-0.7.0-darwin-x86_64.tar.gz"; +static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.8.0/evmone-0.8.0-darwin-x86_64.tar.gz"; static constexpr auto heraFilename = "libhera.dylib"; -static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/releases/download/v0.3.2-evmc8/hera-0.3.2+commit.dc886eb7-darwin-x86_64.tar.gz"; +static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/releases/download/v0.5.0/hera-0.5.0-darwin-x86_64.tar.gz"; #else static constexpr auto evmoneFilename = "libevmone.so"; -static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.7.0/evmone-0.7.0-linux-x86_64.tar.gz"; +static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.8.0/evmone-0.8.0-linux-x86_64.tar.gz"; static constexpr auto heraFilename = "libhera.so"; -static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/releases/download/v0.3.2-evmc8/hera-0.3.2+commit.dc886eb7-linux-x86_64.tar.gz"; +static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/releases/download/v0.5.0/hera-0.5.0-linux-x86_64.tar.gz"; #endif struct ConfigException : public util::Exception {}; diff --git a/test/evmc/README.md b/test/evmc/README.md index 68bcdd92b..cae41f170 100644 --- a/test/evmc/README.md +++ b/test/evmc/README.md @@ -1,5 +1,5 @@ # EVMC -This is an import of [EVMC](https://github.com/ethereum/evmc) version [8.0.0](https://github.com/ethereum/evmc/releases/tag/v8.0.0). +This is an import of [EVMC](https://github.com/ethereum/evmc) version [9.0.0](https://github.com/ethereum/evmc/releases/tag/v9.0.0). Important: The `MockedAccount.storage` is changed to a map from unordered_map as ordering is important for fuzzing. diff --git a/test/evmc/evmc.h b/test/evmc/evmc.h index 5de5f5dae..c0d79d4e7 100644 --- a/test/evmc/evmc.h +++ b/test/evmc/evmc.h @@ -44,7 +44,7 @@ enum * * @see @ref versioning */ - EVMC_ABI_VERSION = 8 + EVMC_ABI_VERSION = 9 }; @@ -154,6 +154,7 @@ struct evmc_tx_context int64_t block_gas_limit; /**< The block gas limit. */ evmc_uint256be block_difficulty; /**< The block difficulty. */ evmc_uint256be chain_id; /**< The blockchain's ChainID. */ + evmc_uint256be block_base_fee; /**< The block base fee per gas (EIP-1559, EIP-3198). */ }; /** @@ -813,19 +814,40 @@ enum evmc_revision /** * The Istanbul revision. * - * The spec draft: https://eips.ethereum.org/EIPS/eip-1679. + * https://eips.ethereum.org/EIPS/eip-1679 */ EVMC_ISTANBUL = 7, /** * The Berlin revision. * - * The spec draft: https://eips.ethereum.org/EIPS/eip-2070. + * https://github.com/ethereum/eth1.0-specs/blob/master/network-upgrades/mainnet-upgrades/berlin.md */ EVMC_BERLIN = 8, + /** + * The London revision. + * + * https://github.com/ethereum/eth1.0-specs/blob/master/network-upgrades/mainnet-upgrades/london.md + */ + EVMC_LONDON = 9, + + /** + * The Shanghai revision. + * + * https://github.com/ethereum/eth1.0-specs/blob/master/network-upgrades/mainnet-upgrades/shanghai.md + */ + EVMC_SHANGHAI = 10, + /** The maximum EVM revision supported. */ - EVMC_MAX_REVISION = EVMC_BERLIN + EVMC_MAX_REVISION = EVMC_SHANGHAI, + + /** + * The latest known EVM revision with finalized specification. + * + * This is handy for EVM tools to always use the latest revision available. + */ + EVMC_LATEST_STABLE_REVISION = EVMC_LONDON }; From 3755210b7b3f60337876fe0a8d71755d54846380 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Mon, 12 Jul 2021 12:05:26 +0200 Subject: [PATCH 76/98] [libyul] ObjectParser: Enables the use of custom source mapping via @use-src. --- libsolutil/StringUtils.h | 18 +++ libyul/AsmParser.h | 8 +- libyul/ObjectParser.cpp | 62 ++++++++- libyul/ObjectParser.h | 6 + scripts/error_codes.py | 4 +- test/libyul/ObjectParser.cpp | 121 +++++++++++++++++- .../libyul/objectCompiler/sourceLocations.yul | 53 ++++++++ 7 files changed, 263 insertions(+), 9 deletions(-) create mode 100644 test/libyul/objectCompiler/sourceLocations.yul diff --git a/libsolutil/StringUtils.h b/libsolutil/StringUtils.h index e7a68cedc..646cda505 100644 --- a/libsolutil/StringUtils.h +++ b/libsolutil/StringUtils.h @@ -177,4 +177,22 @@ inline std::string formatNumberReadable( return str; } +/// Safely converts an usigned integer as string into an unsigned int type. +/// +/// @return the converted number or nullopt in case of an failure (including if it would not fit). +inline std::optional toUnsignedInt(std::string const& _value) +{ + try + { + auto const ulong = stoul(_value); + if (ulong > std::numeric_limits::max()) + return std::nullopt; + return static_cast(ulong); + } + catch (...) + { + return std::nullopt; + } +} + } diff --git a/libyul/AsmParser.h b/libyul/AsmParser.h index 4b01ce697..23ee935e5 100644 --- a/libyul/AsmParser.h +++ b/libyul/AsmParser.h @@ -72,13 +72,17 @@ public: explicit Parser( langutil::ErrorReporter& _errorReporter, Dialect const& _dialect, - std::map> _sourceNames + std::optional>> _sourceNames ): ParserBase(_errorReporter), m_dialect(_dialect), m_sourceNames{std::move(_sourceNames)}, m_debugDataOverride{DebugData::create()}, - m_useSourceLocationFrom{UseSourceLocationFrom::Comments} + m_useSourceLocationFrom{ + m_sourceNames.has_value() ? + UseSourceLocationFrom::Comments : + UseSourceLocationFrom::Scanner + } {} /// Parses an inline assembly block starting with `{` and ending with `}`. diff --git a/libyul/ObjectParser.cpp b/libyul/ObjectParser.cpp index d2eb1feca..ab03e81a1 100644 --- a/libyul/ObjectParser.cpp +++ b/libyul/ObjectParser.cpp @@ -26,6 +26,11 @@ #include #include +#include + +#include + +#include using namespace std; using namespace solidity; @@ -40,6 +45,8 @@ shared_ptr ObjectParser::parse(shared_ptr const& _scanner, bool { shared_ptr object; m_scanner = _scanner; + m_sourceNameMapping = tryParseSourceNameMapping(); + if (currentToken() == Token::LBrace) { // Special case: Code-only form. @@ -104,9 +111,62 @@ shared_ptr ObjectParser::parseCode() return parseBlock(); } +optional ObjectParser::tryParseSourceNameMapping() const +{ + // @use-src 0:"abc.sol", 1:"foo.sol", 2:"bar.sol" + // + // UseSrcList := UseSrc (',' UseSrc)* + // UseSrc := [0-9]+ ':' FileName + // FileName := "(([^\"]|\.)*)" + + // Matches some "@use-src TEXT". + static std::regex const lineRE = std::regex( + "(^|\\s)@use-src\\b", + std::regex_constants::ECMAScript | std::regex_constants::optimize + ); + std::smatch sm; + if (!std::regex_search(m_scanner->currentCommentLiteral(), sm, lineRE)) + return nullopt; + + solAssert(sm.size() == 2, ""); + auto text = m_scanner->currentCommentLiteral().substr(static_cast(sm.position() + sm.length())); + Scanner scanner(make_shared(text, "")); + if (scanner.currentToken() == Token::EOS) + return SourceNameMap{}; + SourceNameMap sourceNames; + + while (scanner.currentToken() != Token::EOS) + { + if (scanner.currentToken() != Token::Number) + break; + auto sourceIndex = toUnsignedInt(scanner.currentLiteral()); + if (!sourceIndex) + break; + if (scanner.next() != Token::Colon) + break; + if (scanner.next() != Token::StringLiteral) + break; + sourceNames[*sourceIndex] = make_shared(scanner.currentLiteral()); + + Token const next = scanner.next(); + if (next == Token::EOS) + return {move(sourceNames)}; + if (next != Token::Comma) + break; + scanner.next(); + } + + m_errorReporter.syntaxError( + 9804_error, + m_scanner->currentCommentLocation(), + "Error parsing arguments to @use-src. Expected: \":\" \"\", ..." + ); + return nullopt; +} + shared_ptr ObjectParser::parseBlock() { - Parser parser(m_errorReporter, m_dialect); + Parser parser(m_errorReporter, m_dialect, m_sourceNameMapping); shared_ptr block = parser.parse(m_scanner, true); yulAssert(block || m_errorReporter.hasErrors(), "Invalid block but no error!"); return block; diff --git a/libyul/ObjectParser.h b/libyul/ObjectParser.h index 6c6f527cd..58b3a851c 100644 --- a/libyul/ObjectParser.h +++ b/libyul/ObjectParser.h @@ -55,7 +55,11 @@ public: /// @returns an empty shared pointer on error. std::shared_ptr parse(std::shared_ptr const& _scanner, bool _reuseScanner); + using SourceNameMap = std::map>; + std::optional const& sourceNameMapping() const noexcept { return m_sourceNameMapping; } + private: + std::optional tryParseSourceNameMapping() const; std::shared_ptr parseObject(Object* _containingObject = nullptr); std::shared_ptr parseCode(); std::shared_ptr parseBlock(); @@ -66,6 +70,8 @@ private: void addNamedSubObject(Object& _container, YulString _name, std::shared_ptr _subObject); Dialect const& m_dialect; + + std::optional m_sourceNameMapping; }; } diff --git a/scripts/error_codes.py b/scripts/error_codes.py index ffb95b889..eaaee10b3 100755 --- a/scripts/error_codes.py +++ b/scripts/error_codes.py @@ -191,9 +191,9 @@ def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False): # white list of ids which are not covered by tests white_ids = { - "6367", # these three are temporarily whitelisted until both PRs are merged. - "5798", + "9804", # Tested in test/libyul/ObjectParser.cpp. "2674", + "6367", "3805", # "This is a pre-release compiler version, please do not use it in production." # The warning may or may not exist in a compiler build. "4591", # "There are more than 256 warnings. Ignoring the rest." diff --git a/test/libyul/ObjectParser.cpp b/test/libyul/ObjectParser.cpp index de5b00f3e..946538078 100644 --- a/test/libyul/ObjectParser.cpp +++ b/test/libyul/ObjectParser.cpp @@ -24,16 +24,23 @@ #include #include +#include #include +#include #include +#include #include +#include + #include #include #include +#include +using namespace ranges; using namespace std; using namespace solidity::frontend; using namespace solidity::langutil; @@ -44,7 +51,7 @@ namespace solidity::yul::test namespace { -std::pair parse(string const& _source) +pair parse(string const& _source) { try { @@ -63,7 +70,7 @@ std::pair parse(string const& _source) return {false, {}}; } -std::optional parseAndReturnFirstError(string const& _source, bool _allowWarnings = true) +optional parseAndReturnFirstError(string const& _source, bool _allowWarnings = true) { bool success; ErrorList errors; @@ -88,12 +95,12 @@ std::optional parseAndReturnFirstError(string const& _source, bool _allow return {}; } -bool successParse(std::string const& _source, bool _allowWarnings = true) +bool successParse(string const& _source, bool _allowWarnings = true) { return !parseAndReturnFirstError(_source, _allowWarnings); } -Error expectError(std::string const& _source, bool _allowWarnings = false) +Error expectError(string const& _source, bool _allowWarnings = false) { auto error = parseAndReturnFirstError(_source, _allowWarnings); @@ -101,6 +108,20 @@ Error expectError(std::string const& _source, bool _allowWarnings = false) return *error; } +tuple, ErrorList> tryGetSourceLocationMapping(string _source) +{ + vector lines; + boost::split(lines, _source, boost::is_any_of("\n")); + string source = util::joinHumanReadablePrefixed(lines, "\n///") + "\n{}\n"; + + ErrorList errors; + ErrorReporter reporter(errors); + Dialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(EVMVersion::berlin()); + ObjectParser objectParser{reporter, dialect}; + objectParser.parse(make_shared(CharStream(move(source), "")), false); + return {objectParser.sourceNameMapping(), std::move(errors)}; +} + } #define CHECK_ERROR(text, typ, substring) \ @@ -162,6 +183,98 @@ BOOST_AUTO_TEST_CASE(to_string) BOOST_CHECK_EQUAL(asmStack.print(), expectation); } +BOOST_AUTO_TEST_CASE(use_src_empty) +{ + auto const [mapping, _] = tryGetSourceLocationMapping(""); + BOOST_REQUIRE(!mapping); +} + +BOOST_AUTO_TEST_CASE(use_src_simple) +{ + auto const [mapping, _] = tryGetSourceLocationMapping(R"(@use-src 0:"contract.sol")"); + BOOST_REQUIRE(mapping.has_value()); + BOOST_REQUIRE_EQUAL(mapping->size(), 1); + BOOST_REQUIRE_EQUAL(*mapping->at(0), "contract.sol"); +} + +BOOST_AUTO_TEST_CASE(use_src_multiple) +{ + auto const [mapping, _] = tryGetSourceLocationMapping(R"(@use-src 0:"contract.sol", 1:"misc.yul")"); + BOOST_REQUIRE(mapping); + BOOST_REQUIRE_EQUAL(mapping->size(), 2); + BOOST_REQUIRE_EQUAL(*mapping->at(0), "contract.sol"); + BOOST_REQUIRE_EQUAL(*mapping->at(1), "misc.yul"); +} + +BOOST_AUTO_TEST_CASE(use_src_escaped_filenames) +{ + auto const [mapping, _] = tryGetSourceLocationMapping( + R"(@use-src 42:"con\"tract@\".sol")" + ); + BOOST_REQUIRE(mapping); + BOOST_REQUIRE_EQUAL(mapping->size(), 1); + BOOST_REQUIRE(mapping->count(42)); + BOOST_REQUIRE_EQUAL(*mapping->at(42), "con\"tract@\".sol"); +} + +BOOST_AUTO_TEST_CASE(use_src_invalid_syntax_malformed_param_1) +{ + // open quote arg, missing closing quote + auto const [mapping, errors] = tryGetSourceLocationMapping(R"(@use-src 42_"con")"); + + BOOST_REQUIRE_EQUAL(errors.size(), 1); + BOOST_CHECK_EQUAL(errors.front()->errorId().error, 9804); +} + +BOOST_AUTO_TEST_CASE(use_src_invalid_syntax_malformed_param_2) +{ + // open quote arg, missing closing quote + auto const [mapping, errors] = tryGetSourceLocationMapping(R"(@use-src 42:"con)"); + + BOOST_REQUIRE_EQUAL(errors.size(), 1); + BOOST_CHECK_EQUAL(errors.front()->errorId().error, 9804); +} + +BOOST_AUTO_TEST_CASE(use_src_error_unexpected_trailing_tokens) +{ + auto const [mapping, errors] = tryGetSourceLocationMapping( + R"(@use-src 1:"file.sol" @use-src 2:"foo.sol")" + ); + + BOOST_REQUIRE_EQUAL(errors.size(), 1); + BOOST_CHECK_EQUAL(errors.front()->errorId().error, 9804); +} + +BOOST_AUTO_TEST_CASE(use_src_multiline) +{ + auto const [mapping, _] = tryGetSourceLocationMapping( + " @use-src \n 0:\"contract.sol\" \n , \n 1:\"misc.yul\"" + ); + BOOST_REQUIRE(mapping); + BOOST_REQUIRE_EQUAL(mapping->size(), 2); + BOOST_REQUIRE_EQUAL(*mapping->at(0), "contract.sol"); + BOOST_REQUIRE_EQUAL(*mapping->at(1), "misc.yul"); +} + +BOOST_AUTO_TEST_CASE(use_src_empty_body) +{ + auto const [mapping, _] = tryGetSourceLocationMapping("@use-src"); + BOOST_REQUIRE(mapping); + BOOST_REQUIRE_EQUAL(mapping->size(), 0); +} + +BOOST_AUTO_TEST_CASE(use_src_leading_text) +{ + auto const [mapping, _] = tryGetSourceLocationMapping( + "@something else @use-src 0:\"contract.sol\", 1:\"misc.sol\""s + ); + BOOST_REQUIRE(mapping); + BOOST_REQUIRE_EQUAL(mapping->size(), 2); + BOOST_REQUIRE(mapping->find(0) != mapping->end()); + BOOST_REQUIRE_EQUAL(*mapping->at(0), "contract.sol"); + BOOST_REQUIRE_EQUAL(*mapping->at(1), "misc.sol"); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libyul/objectCompiler/sourceLocations.yul b/test/libyul/objectCompiler/sourceLocations.yul new file mode 100644 index 000000000..f98f74f77 --- /dev/null +++ b/test/libyul/objectCompiler/sourceLocations.yul @@ -0,0 +1,53 @@ +// something else +/*-- another unrelated comment --*/ +/// @use-src 3: "abc.sol" , 2: "def.sol" +object "a" { + code { + /// @src 3:0:2 + datacopy(0, dataoffset("sub"), datasize("sub")) + return(0, + /** @src 2:5:6 */ + datasize("sub") + ) + } + object "sub" { + code { + /// @src 2:70:72 + sstore(0, dataoffset("sub")) + /** + * @something else + * @src 3:2:5 + */ + mstore( + 0, + datasize("data1") + /// @src 3:90:2 + ) + } + data "data1" "Hello, World!" + } +} +// ---- +// Assembly: +// /* "abc.sol":0:2 */ +// codecopy(0x00, dataOffset(sub_0), dataSize(sub_0)) +// /* "def.sol":5:6 */ +// dataSize(sub_0) +// /* "abc.sol":0:2 */ +// 0x00 +// return +// stop +// +// sub_0: assembly { +// /* "def.sol":70:72 */ +// 0x00 +// dup1 +// sstore +// /* "abc.sol":2:5 */ +// mstore(0x00, 0x0d) +// stop +// data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421 +// } +// Bytecode: 600a600d600039600a6000f3fe60008055600d600052fe +// Opcodes: PUSH1 0xA PUSH1 0xD PUSH1 0x0 CODECOPY PUSH1 0xA PUSH1 0x0 RETURN INVALID PUSH1 0x0 DUP1 SSTORE PUSH1 0xD PUSH1 0x0 MSTORE INVALID +// SourceMappings: 0:2::-:0;;;;5:1;0:2; From 6c8ecfa82c1cd6dcac0f1f951525451874205dad Mon Sep 17 00:00:00 2001 From: Leo Alt Date: Wed, 19 May 2021 17:35:19 +0200 Subject: [PATCH 77/98] Add option to choose solver --- .circleci/config.yml | 4 +- docs/smtchecker.rst | 33 +++++ libsmtutil/SMTPortfolio.cpp | 12 +- libsmtutil/SolverInterface.h | 70 ++++++++- libsolidity/formal/BMC.cpp | 53 +++++-- libsolidity/formal/BMC.h | 1 - libsolidity/formal/CHC.cpp | 54 ++++--- libsolidity/formal/CHC.h | 4 - libsolidity/formal/ModelChecker.cpp | 11 +- libsolidity/formal/ModelChecker.h | 3 +- libsolidity/formal/ModelCheckerSettings.h | 2 + libsolidity/interface/CompilerStack.cpp | 11 +- libsolidity/interface/CompilerStack.h | 3 - libsolidity/interface/StandardCompiler.cpp | 20 ++- scripts/error_codes.py | 3 +- solc/CommandLineParser.cpp | 19 +++ .../model_checker_solvers_all/args | 1 + .../model_checker_solvers_all/err | 12 ++ .../model_checker_solvers_all/input.sol | 7 + .../model_checker_solvers_all_implicit/args | 1 + .../model_checker_solvers_all_implicit/err | 12 ++ .../input.sol | 7 + .../model_checker_solvers_smtlib2/args | 1 + .../model_checker_solvers_smtlib2/err | 3 + .../model_checker_solvers_smtlib2/input.sol | 7 + .../model_checker_solvers_wrong/args | 1 + .../model_checker_solvers_wrong/err | 1 + .../model_checker_solvers_wrong/exit | 1 + .../model_checker_solvers_wrong/input.sol | 7 + .../model_checker_solvers_wrong2/args | 1 + .../model_checker_solvers_wrong2/err | 1 + .../model_checker_solvers_wrong2/exit | 1 + .../model_checker_solvers_wrong2/input.sol | 7 + .../model_checker_solvers_z3/args | 1 + .../cmdlineTests/model_checker_solvers_z3/err | 12 ++ .../model_checker_solvers_z3/input.sol | 7 + .../model_checker_solvers_z3_smtlib2/args | 1 + .../model_checker_solvers_z3_smtlib2/err | 12 ++ .../input.sol | 7 + .../input.json | 17 +++ .../output.json | 21 +++ .../input.json | 18 +++ .../output.json | 5 + .../input.json | 18 +++ .../output.json | 138 ++++++++++++++++++ .../input.json | 18 +++ .../output.json | 21 +++ .../input.json | 18 +++ .../output.json | 21 +++ test/externalTests/solc-js/solc-js.sh | 6 +- test/libsolidity/SMTCheckerTest.cpp | 19 +-- test/solc/CommandLineParser.cpp | 6 + test/tools/fuzzer_common.cpp | 2 +- 53 files changed, 650 insertions(+), 92 deletions(-) create mode 100644 test/cmdlineTests/model_checker_solvers_all/args create mode 100644 test/cmdlineTests/model_checker_solvers_all/err create mode 100644 test/cmdlineTests/model_checker_solvers_all/input.sol create mode 100644 test/cmdlineTests/model_checker_solvers_all_implicit/args create mode 100644 test/cmdlineTests/model_checker_solvers_all_implicit/err create mode 100644 test/cmdlineTests/model_checker_solvers_all_implicit/input.sol create mode 100644 test/cmdlineTests/model_checker_solvers_smtlib2/args create mode 100644 test/cmdlineTests/model_checker_solvers_smtlib2/err create mode 100644 test/cmdlineTests/model_checker_solvers_smtlib2/input.sol create mode 100644 test/cmdlineTests/model_checker_solvers_wrong/args create mode 100644 test/cmdlineTests/model_checker_solvers_wrong/err create mode 100644 test/cmdlineTests/model_checker_solvers_wrong/exit create mode 100644 test/cmdlineTests/model_checker_solvers_wrong/input.sol create mode 100644 test/cmdlineTests/model_checker_solvers_wrong2/args create mode 100644 test/cmdlineTests/model_checker_solvers_wrong2/err create mode 100644 test/cmdlineTests/model_checker_solvers_wrong2/exit create mode 100644 test/cmdlineTests/model_checker_solvers_wrong2/input.sol create mode 100644 test/cmdlineTests/model_checker_solvers_z3/args create mode 100644 test/cmdlineTests/model_checker_solvers_z3/err create mode 100644 test/cmdlineTests/model_checker_solvers_z3/input.sol create mode 100644 test/cmdlineTests/model_checker_solvers_z3_smtlib2/args create mode 100644 test/cmdlineTests/model_checker_solvers_z3_smtlib2/err create mode 100644 test/cmdlineTests/model_checker_solvers_z3_smtlib2/input.sol create mode 100644 test/cmdlineTests/standard_model_checker_solvers_all/input.json create mode 100644 test/cmdlineTests/standard_model_checker_solvers_all/output.json create mode 100644 test/cmdlineTests/standard_model_checker_solvers_none/input.json create mode 100644 test/cmdlineTests/standard_model_checker_solvers_none/output.json create mode 100644 test/cmdlineTests/standard_model_checker_solvers_smtlib2/input.json create mode 100644 test/cmdlineTests/standard_model_checker_solvers_smtlib2/output.json create mode 100644 test/cmdlineTests/standard_model_checker_solvers_z3/input.json create mode 100644 test/cmdlineTests/standard_model_checker_solvers_z3/output.json create mode 100644 test/cmdlineTests/standard_model_checker_solvers_z3_smtlib2/input.json create mode 100644 test/cmdlineTests/standard_model_checker_solvers_z3_smtlib2/output.json diff --git a/.circleci/config.yml b/.circleci/config.yml index 93e9f93bd..11198a089 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -811,7 +811,7 @@ jobs: t_ems_solcjs: docker: - - image: buildpack-deps:latest + - image: << pipeline.parameters.ubuntu-2004-docker-image >> environment: TERM: xterm steps: @@ -822,7 +822,7 @@ jobs: name: Install test dependencies command: | apt-get update - apt-get install -qqy --no-install-recommends nodejs npm cvc4 + apt-get install -qqy --no-install-recommends nodejs npm - run: name: Test solcjs no_output_timeout: 30m diff --git a/docs/smtchecker.rst b/docs/smtchecker.rst index a4580ce4d..f3a9e2e81 100644 --- a/docs/smtchecker.rst +++ b/docs/smtchecker.rst @@ -558,6 +558,39 @@ calls assume the called code is unknown and can do anything. The CHC engine is much more powerful than BMC in terms of what it can prove, and might require more computing resources. +SMT and Horn solvers +==================== + +The two engines detailed above use automated theorem provers as their logical +backends. BMC uses an SMT solver, whereas CHC uses a Horn solver. Often the +same tool can act as both, as seen in `z3 `_, +which is primarily an SMT solver and makes `Spacer +`_ available as a Horn solver, and `Eldarica +`_ which does both. + +The user can choose which solvers should be used, if available, via the CLI +option ``--model-checker-solvers {all,cvc4,smtlib2,z3}`` or the JSON option +``settings.modelChecker.solvers=[smtlib2,z3]``, where: + +- ``cvc4`` is only available if the ``solc`` binary is compiled with it. Only BMC uses ``cvc4``. +- ``smtlib2`` outputs SMT/Horn queries in the `smtlib2 `_ format. + These can be used together with the compiler's `callback mechanism `_ so that + any solver binary from the system can be employed to synchronously return the results of the queries to the compiler. + This is currently the only way to use Eldarica, for example, since it does not have a C++ API. + This can be used by both BMC and CHC depending on which solvers are called. +- ``z3`` is available + + - if ``solc`` is compiled with it; + - if a dynamic ``z3`` library of version 4.8.x is installed in a Linux system (from Solidity 0.7.6); + - statically in ``soljson.js`` (from Solidity 0.6.9), that is, the Javascript binary of the compiler. + +Since both BMC and CHC use ``z3``, and ``z3`` is available in a greater variety +of environments, including in the browser, most users will almost never need to be +concerned about this option. More advanced users might apply this option to try +alternative solvers on more complex problems. + +Please note that certain combinations of chosen engine and solver will lead to +the SMTChecker doing nothing, for example choosing CHC and ``cvc4``. ******************************* Abstraction and False Positives diff --git a/libsmtutil/SMTPortfolio.cpp b/libsmtutil/SMTPortfolio.cpp index de00c1f79..aec9b0a1c 100644 --- a/libsmtutil/SMTPortfolio.cpp +++ b/libsmtutil/SMTPortfolio.cpp @@ -40,7 +40,8 @@ SMTPortfolio::SMTPortfolio( ): SolverInterface(_queryTimeout) { - m_solvers.emplace_back(make_unique(move(_smtlib2Responses), move(_smtCallback), m_queryTimeout)); + if (_enabledSolvers.smtlib2) + m_solvers.emplace_back(make_unique(move(_smtlib2Responses), move(_smtCallback), m_queryTimeout)); #ifdef HAVE_Z3 if (_enabledSolvers.z3 && Z3Interface::available()) m_solvers.emplace_back(make_unique(m_queryTimeout)); @@ -143,10 +144,11 @@ pair> SMTPortfolio::check(vector const& vector SMTPortfolio::unhandledQueries() { // This code assumes that the constructor guarantees that - // SmtLib2Interface is in position 0. - smtAssert(!m_solvers.empty(), ""); - smtAssert(dynamic_cast(m_solvers.front().get()), ""); - return m_solvers.front()->unhandledQueries(); + // SmtLib2Interface is in position 0, if enabled. + if (!m_solvers.empty()) + if (auto smtlib2 = dynamic_cast(m_solvers.front().get())) + return smtlib2->unhandledQueries(); + return {}; } bool SMTPortfolio::solverAnswered(CheckResult result) diff --git a/libsmtutil/SolverInterface.h b/libsmtutil/SolverInterface.h index 6eb6982fb..21648c245 100644 --- a/libsmtutil/SolverInterface.h +++ b/libsmtutil/SolverInterface.h @@ -23,10 +23,13 @@ #include +#include + #include #include #include #include +#include #include #include @@ -36,16 +39,71 @@ namespace solidity::smtutil struct SMTSolverChoice { bool cvc4 = false; + bool smtlib2 = false; bool z3 = false; - static constexpr SMTSolverChoice All() { return {true, true}; } - static constexpr SMTSolverChoice CVC4() { return {true, false}; } - static constexpr SMTSolverChoice Z3() { return {false, true}; } - static constexpr SMTSolverChoice None() { return {false, false}; } + static constexpr SMTSolverChoice All() { return {true, true, true}; } + static constexpr SMTSolverChoice CVC4() { return {true, false, false}; } + static constexpr SMTSolverChoice SMTLIB2() { return {false, true, false}; } + static constexpr SMTSolverChoice Z3() { return {false, false, true}; } + static constexpr SMTSolverChoice None() { return {false, false, false}; } + + static std::optional fromString(std::string const& _solvers) + { + SMTSolverChoice solvers; + if (_solvers == "all") + { + smtAssert(solvers.setSolver("cvc4"), ""); + smtAssert(solvers.setSolver("smtlib2"), ""); + smtAssert(solvers.setSolver("z3"), ""); + } + else + for (auto&& s: _solvers | ranges::views::split(',') | ranges::to>()) + if (!solvers.setSolver(s)) + return {}; + + return solvers; + } + + SMTSolverChoice& operator&(SMTSolverChoice const& _other) + { + cvc4 &= _other.cvc4; + smtlib2 &= _other.smtlib2; + z3 &= _other.z3; + return *this; + } + + SMTSolverChoice& operator&=(SMTSolverChoice const& _other) + { + return *this & _other; + } + + bool operator!=(SMTSolverChoice const& _other) const noexcept { return !(*this == _other); } + + bool operator==(SMTSolverChoice const& _other) const noexcept + { + return cvc4 == _other.cvc4 && + smtlib2 == _other.smtlib2 && + z3 == _other.z3; + } + + bool setSolver(std::string const& _solver) + { + static std::set const solvers{"cvc4", "smtlib2", "z3"}; + if (!solvers.count(_solver)) + return false; + if (_solver == "cvc4") + cvc4 = true; + else if (_solver == "smtlib2") + smtlib2 = true; + else if (_solver == "z3") + z3 = true; + return true; + } bool none() { return !some(); } - bool some() { return cvc4 || z3; } - bool all() { return cvc4 && z3; } + bool some() { return cvc4 || smtlib2 || z3; } + bool all() { return cvc4 && smtlib2 && z3; } }; enum class CheckResult diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index e86906ff9..eee9988ed 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -40,16 +40,15 @@ BMC::BMC( ErrorReporter& _errorReporter, map const& _smtlib2Responses, ReadCallback::Callback const& _smtCallback, - smtutil::SMTSolverChoice _enabledSolvers, ModelCheckerSettings const& _settings, CharStreamProvider const& _charStreamProvider ): SMTEncoder(_context, _settings, _charStreamProvider), - m_interface(make_unique(_smtlib2Responses, _smtCallback, _enabledSolvers, _settings.timeout)), + m_interface(make_unique(_smtlib2Responses, _smtCallback, _settings.solvers, _settings.timeout)), m_outerErrorReporter(_errorReporter) { #if defined (HAVE_Z3) || defined (HAVE_CVC4) - if (_enabledSolvers.some()) + if (m_settings.solvers.cvc4 || m_settings.solvers.z3) if (!_smtlib2Responses.empty()) m_errorReporter.warning( 5622_error, @@ -63,6 +62,20 @@ BMC::BMC( void BMC::analyze(SourceUnit const& _source, map> _solvedTargets) { + if (m_interface->solvers() == 0) + { + if (!m_noSolverWarning) + { + m_noSolverWarning = true; + m_outerErrorReporter.warning( + 7710_error, + SourceLocation(), + "BMC analysis was not possible since no SMT solver was found and enabled." + ); + } + return; + } + if (SMTEncoder::analyze(_source)) { m_solvedTargets = move(_solvedTargets); @@ -75,11 +88,14 @@ void BMC::analyze(SourceUnit const& _source, mapsolvers() > 0, ""); // If this check is true, Z3 and CVC4 are not available // and the query answers were not provided, since SMTPortfolio - // guarantees that SmtLib2Interface is the first solver. - if (!m_interface->unhandledQueries().empty() && m_interface->solvers() == 1) + // guarantees that SmtLib2Interface is the first solver, if enabled. + if ( + !m_interface->unhandledQueries().empty() && + m_interface->solvers() == 1 && + m_settings.solvers.smtlib2 + ) { if (!m_noSolverWarning) { @@ -87,7 +103,8 @@ void BMC::analyze(SourceUnit const& _source, 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"; + std::ostringstream modelMessage; + // Sometimes models have complex smtlib2 expressions that SMTLib2Interface fails to parse. + if (values.size() == expressionNames.size()) + { + modelMessage << "Counterexample:\n"; + 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"; + } m_errorReporter.warning( _errorHappens, diff --git a/libsolidity/formal/BMC.h b/libsolidity/formal/BMC.h index fae2ecf45..c1b89347c 100644 --- a/libsolidity/formal/BMC.h +++ b/libsolidity/formal/BMC.h @@ -62,7 +62,6 @@ public: langutil::ErrorReporter& _errorReporter, std::map const& _smtlib2Responses, ReadCallback::Callback const& _smtCallback, - smtutil::SMTSolverChoice _enabledSolvers, ModelCheckerSettings const& _settings, langutil::CharStreamProvider const& _charStreamProvider ); diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index 10e066f60..00657922d 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -56,26 +56,38 @@ CHC::CHC( ErrorReporter& _errorReporter, [[maybe_unused]] map const& _smtlib2Responses, [[maybe_unused]] ReadCallback::Callback const& _smtCallback, - SMTSolverChoice _enabledSolvers, ModelCheckerSettings const& _settings, CharStreamProvider const& _charStreamProvider ): SMTEncoder(_context, _settings, _charStreamProvider), - m_outerErrorReporter(_errorReporter), - m_enabledSolvers(_enabledSolvers) + m_outerErrorReporter(_errorReporter) { - bool usesZ3 = _enabledSolvers.z3; + bool usesZ3 = m_settings.solvers.z3; #ifdef HAVE_Z3 usesZ3 = usesZ3 && Z3Interface::available(); #else usesZ3 = false; #endif - if (!usesZ3) + if (!usesZ3 && m_settings.solvers.smtlib2) m_interface = make_unique(_smtlib2Responses, _smtCallback, m_settings.timeout); } void CHC::analyze(SourceUnit const& _source) { + if (!m_settings.solvers.z3 && !m_settings.solvers.smtlib2) + { + if (!m_noSolverWarning) + { + m_noSolverWarning = true; + m_outerErrorReporter.warning( + 7649_error, + SourceLocation(), + "CHC analysis was not possible since no Horn solver was enabled." + ); + } + return; + } + if (SMTEncoder::analyze(_source)) { resetSourceAnalysis(); @@ -92,6 +104,8 @@ void CHC::analyze(SourceUnit const& _source) } bool ranSolver = true; + // If ranSolver is true here it's because an SMT solver callback was + // actually given and the queries were solved. if (auto const* smtLibInterface = dynamic_cast(m_interface.get())) ranSolver = smtLibInterface->unhandledQueries().empty(); if (!ranSolver && !m_noSolverWarning) @@ -103,7 +117,8 @@ void CHC::analyze(SourceUnit const& _source) #ifdef HAVE_Z3_DLOPEN "CHC analysis was not possible since libz3.so." + to_string(Z3_MAJOR_VERSION) + "." + to_string(Z3_MINOR_VERSION) + " was not found." #else - "CHC analysis was not possible since no integrated z3 SMT solver was found." + "CHC analysis was not possible. No Horn solver was available." + " None of the installed solvers was enabled." #endif ); } @@ -933,7 +948,7 @@ void CHC::resetSourceAnalysis() bool usesZ3 = false; #ifdef HAVE_Z3 - usesZ3 = m_enabledSolvers.z3 && Z3Interface::available(); + usesZ3 = m_settings.solvers.z3 && Z3Interface::available(); if (usesZ3) { /// z3::fixedpoint does not have a reset mechanism, so we need to create another. @@ -1427,20 +1442,23 @@ pair CHC::query(smtutil::Expression c case CheckResult::SATISFIABLE: { #ifdef HAVE_Z3 - // Even though the problem is SAT, Spacer's pre processing makes counterexamples incomplete. - // We now disable those optimizations and check whether we can still solve the problem. - auto* spacer = dynamic_cast(m_interface.get()); - solAssert(spacer, ""); - spacer->setSpacerOptions(false); + if (m_settings.solvers.z3) + { + // Even though the problem is SAT, Spacer's pre processing makes counterexamples incomplete. + // We now disable those optimizations and check whether we can still solve the problem. + auto* spacer = dynamic_cast(m_interface.get()); + solAssert(spacer, ""); + spacer->setSpacerOptions(false); - CheckResult resultNoOpt; - CHCSolverInterface::CexGraph cexNoOpt; - tie(resultNoOpt, cexNoOpt) = m_interface->query(_query); + CheckResult resultNoOpt; + CHCSolverInterface::CexGraph cexNoOpt; + tie(resultNoOpt, cexNoOpt) = m_interface->query(_query); - if (resultNoOpt == CheckResult::SATISFIABLE) - cex = move(cexNoOpt); + if (resultNoOpt == CheckResult::SATISFIABLE) + cex = move(cexNoOpt); - spacer->setSpacerOptions(true); + spacer->setSpacerOptions(true); + } #endif break; } diff --git a/libsolidity/formal/CHC.h b/libsolidity/formal/CHC.h index 6886c2c7f..3aab9268d 100644 --- a/libsolidity/formal/CHC.h +++ b/libsolidity/formal/CHC.h @@ -56,7 +56,6 @@ public: langutil::ErrorReporter& _errorReporter, std::map const& _smtlib2Responses, ReadCallback::Callback const& _smtCallback, - smtutil::SMTSolverChoice _enabledSolvers, ModelCheckerSettings const& _settings, langutil::CharStreamProvider const& _charStreamProvider ); @@ -393,9 +392,6 @@ private: /// ErrorReporter that comes from CompilerStack. langutil::ErrorReporter& m_outerErrorReporter; - - /// SMT solvers that are chosen at runtime. - smtutil::SMTSolverChoice m_enabledSolvers; }; } diff --git a/libsolidity/formal/ModelChecker.cpp b/libsolidity/formal/ModelChecker.cpp index e17c7f4df..1aac80248 100644 --- a/libsolidity/formal/ModelChecker.cpp +++ b/libsolidity/formal/ModelChecker.cpp @@ -35,14 +35,13 @@ ModelChecker::ModelChecker( langutil::CharStreamProvider const& _charStreamProvider, map const& _smtlib2Responses, ModelCheckerSettings _settings, - ReadCallback::Callback const& _smtCallback, - smtutil::SMTSolverChoice _enabledSolvers + ReadCallback::Callback const& _smtCallback ): m_errorReporter(_errorReporter), - m_settings(_settings), + m_settings(move(_settings)), m_context(), - m_bmc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, _enabledSolvers, m_settings, _charStreamProvider), - m_chc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, _enabledSolvers, m_settings, _charStreamProvider) + m_bmc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, m_settings, _charStreamProvider), + m_chc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, m_settings, _charStreamProvider) { } @@ -135,7 +134,7 @@ vector ModelChecker::unhandledQueries() solidity::smtutil::SMTSolverChoice ModelChecker::availableSolvers() { - smtutil::SMTSolverChoice available = smtutil::SMTSolverChoice::None(); + smtutil::SMTSolverChoice available = smtutil::SMTSolverChoice::SMTLIB2(); #ifdef HAVE_Z3 available.z3 = solidity::smtutil::Z3Interface::available(); #endif diff --git a/libsolidity/formal/ModelChecker.h b/libsolidity/formal/ModelChecker.h index 0bf2b4ffc..8156e39e7 100644 --- a/libsolidity/formal/ModelChecker.h +++ b/libsolidity/formal/ModelChecker.h @@ -52,8 +52,7 @@ public: langutil::CharStreamProvider const& _charStreamProvider, std::map const& _smtlib2Responses, ModelCheckerSettings _settings = ModelCheckerSettings{}, - ReadCallback::Callback const& _smtCallback = ReadCallback::Callback(), - smtutil::SMTSolverChoice _enabledSolvers = smtutil::SMTSolverChoice::All() + ReadCallback::Callback const& _smtCallback = ReadCallback::Callback() ); // TODO This should be removed for 0.9.0. diff --git a/libsolidity/formal/ModelCheckerSettings.h b/libsolidity/formal/ModelCheckerSettings.h index cfcd31d2c..edd8f0800 100644 --- a/libsolidity/formal/ModelCheckerSettings.h +++ b/libsolidity/formal/ModelCheckerSettings.h @@ -113,6 +113,7 @@ struct ModelCheckerSettings { ModelCheckerContracts contracts = ModelCheckerContracts::Default(); ModelCheckerEngine engine = ModelCheckerEngine::None(); + smtutil::SMTSolverChoice solvers = smtutil::SMTSolverChoice::All(); ModelCheckerTargets targets = ModelCheckerTargets::Default(); std::optional timeout; @@ -122,6 +123,7 @@ struct ModelCheckerSettings return contracts == _other.contracts && engine == _other.engine && + solvers == _other.solvers && targets == _other.targets && timeout == _other.timeout; } diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 93ddd4122..f32b47d2a 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -95,7 +95,6 @@ static int g_compilerStackCounts = 0; CompilerStack::CompilerStack(ReadCallback::Callback _readFile): m_readFile{std::move(_readFile)}, - m_enabledSMTSolvers{smtutil::SMTSolverChoice::All()}, m_errorReporter{m_errorList} { // Because TypeProvider is currently a singleton API, we must ensure that @@ -228,13 +227,6 @@ void CompilerStack::setModelCheckerSettings(ModelCheckerSettings _settings) m_modelCheckerSettings = _settings; } -void CompilerStack::setSMTSolverChoice(smtutil::SMTSolverChoice _enabledSMTSolvers) -{ - if (m_stackState >= ParsedAndImported) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set enabled SMT solvers before parsing.")); - m_enabledSMTSolvers = _enabledSMTSolvers; -} - void CompilerStack::setLibraries(std::map const& _libraries) { if (m_stackState >= ParsedAndImported) @@ -299,7 +291,6 @@ void CompilerStack::reset(bool _keepSettings) m_viaIR = false; m_evmVersion = langutil::EVMVersion(); m_modelCheckerSettings = ModelCheckerSettings{}; - m_enabledSMTSolvers = smtutil::SMTSolverChoice::All(); m_generateIR = false; m_generateEwasm = false; m_revertStrings = RevertStrings::Default; @@ -555,7 +546,7 @@ bool CompilerStack::analyze() if (noErrors) { - ModelChecker modelChecker(m_errorReporter, *this, m_smtlib2Responses, m_modelCheckerSettings, m_readFile, m_enabledSMTSolvers); + ModelChecker modelChecker(m_errorReporter, *this, m_smtlib2Responses, m_modelCheckerSettings, m_readFile); auto allSources = applyMap(m_sourceOrder, [](Source const* _source) { return _source->ast; }); modelChecker.enableAllEnginesIfPragmaPresent(allSources); modelChecker.checkRequestedSourcesAndContracts(allSources); diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 77b2fe4de..efdae0c36 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -176,8 +176,6 @@ public: /// Set model checker settings. void setModelCheckerSettings(ModelCheckerSettings _settings); - /// Set which SMT solvers should be enabled. - void setSMTSolverChoice(smtutil::SMTSolverChoice _enabledSolvers); /// Sets the requested contract names by source. /// If empty, no filtering is performed and every contract @@ -480,7 +478,6 @@ private: bool m_viaIR = false; langutil::EVMVersion m_evmVersion; ModelCheckerSettings m_modelCheckerSettings; - smtutil::SMTSolverChoice m_enabledSMTSolvers; std::map> m_requestedContractNames; bool m_generateEvmBytecode = true; bool m_generateIR = false; diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index b27e7d08d..70ed10d27 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -442,7 +442,7 @@ std::optional checkSettingsKeys(Json::Value const& _input) std::optional checkModelCheckerSettingsKeys(Json::Value const& _input) { - static set keys{"contracts", "engine", "targets", "timeout"}; + static set keys{"contracts", "engine", "solvers", "targets", "timeout"}; return checkKeys(_input, keys, "modelChecker"); } @@ -951,6 +951,24 @@ std::variant StandardCompiler: ret.modelCheckerSettings.engine = *engine; } + if (modelCheckerSettings.isMember("solvers")) + { + auto const& solversArray = modelCheckerSettings["solvers"]; + if (!solversArray.isArray()) + return formatFatalError("JSONError", "settings.modelChecker.solvers must be an array."); + + smtutil::SMTSolverChoice solvers; + for (auto const& s: solversArray) + { + if (!s.isString()) + return formatFatalError("JSONError", "Every target in settings.modelChecker.solvers must be a string."); + if (!solvers.setSolver(s.asString())) + return formatFatalError("JSONError", "Invalid model checker solvers requested."); + } + + ret.modelCheckerSettings.solvers = solvers; + } + if (modelCheckerSettings.isMember("targets")) { auto const& targetsArray = modelCheckerSettings["targets"]; diff --git a/scripts/error_codes.py b/scripts/error_codes.py index ffb95b889..b5c7887a1 100755 --- a/scripts/error_codes.py +++ b/scripts/error_codes.py @@ -227,7 +227,8 @@ def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False): "1988", "2066", "3356", "3893", "3996", "4010", "4802", "5272", "5622", "7128", "7400", - "7589", "7593", "8065", "8084", "8140", + "7589", "7593", "7649", "7710", + "8065", "8084", "8140", "8312", "8592", "9134", "9609", } diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index ed2f2c9b3..5a1daed1e 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -87,6 +87,7 @@ static string const g_strMetadataHash = "metadata-hash"; static string const g_strMetadataLiteral = "metadata-literal"; static string const g_strModelCheckerContracts = "model-checker-contracts"; static string const g_strModelCheckerEngine = "model-checker-engine"; +static string const g_strModelCheckerSolvers = "model-checker-solvers"; static string const g_strModelCheckerTargets = "model-checker-targets"; static string const g_strModelCheckerTimeout = "model-checker-timeout"; static string const g_strNatspecDev = "devdoc"; @@ -723,6 +724,11 @@ General Information)").c_str(), po::value()->value_name("all,bmc,chc,none")->default_value("none"), "Select model checker engine." ) + ( + g_strModelCheckerSolvers.c_str(), + po::value()->value_name("all,cvc4,z3,smtlib2")->default_value("all"), + "Select model checker solvers." + ) ( g_strModelCheckerTargets.c_str(), po::value()->value_name("default,constantCondition,underflow,overflow,divByZero,balance,assert,popEmptyArray,outOfBounds")->default_value("default"), @@ -1089,6 +1095,18 @@ General Information)").c_str(), m_options.modelChecker.settings.engine = *engine; } + if (m_args.count(g_strModelCheckerSolvers)) + { + string solversStr = m_args[g_strModelCheckerSolvers].as(); + optional solvers = smtutil::SMTSolverChoice::fromString(solversStr); + if (!solvers) + { + serr() << "Invalid option for --" << g_strModelCheckerSolvers << ": " << solversStr << endl; + return false; + } + m_options.modelChecker.settings.solvers = *solvers; + } + if (m_args.count(g_strModelCheckerTargets)) { string targetsStr = m_args[g_strModelCheckerTargets].as(); @@ -1108,6 +1126,7 @@ General Information)").c_str(), m_options.modelChecker.initialize = m_args.count(g_strModelCheckerContracts) || m_args.count(g_strModelCheckerEngine) || + m_args.count(g_strModelCheckerSolvers) || m_args.count(g_strModelCheckerTargets) || m_args.count(g_strModelCheckerTimeout); m_options.output.experimentalViaIR = (m_args.count(g_strExperimentalViaIR) > 0); diff --git a/test/cmdlineTests/model_checker_solvers_all/args b/test/cmdlineTests/model_checker_solvers_all/args new file mode 100644 index 000000000..4f09ccbe9 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_all/args @@ -0,0 +1 @@ +--model-checker-engine all --model-checker-solvers all diff --git a/test/cmdlineTests/model_checker_solvers_all/err b/test/cmdlineTests/model_checker_solvers_all/err new file mode 100644 index 000000000..3db918e25 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_all/err @@ -0,0 +1,12 @@ +Warning: CHC: Assertion violation happens here. +Counterexample: + +x = 0 + +Transaction trace: +test.constructor() +test.f(0) + --> model_checker_solvers_all/input.sol:5:3: + | +5 | assert(x > 0); + | ^^^^^^^^^^^^^ diff --git a/test/cmdlineTests/model_checker_solvers_all/input.sol b/test/cmdlineTests/model_checker_solvers_all/input.sol new file mode 100644 index 000000000..7d05ef6e5 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_all/input.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract test { + function f(uint x) public pure { + assert(x > 0); + } +} \ No newline at end of file diff --git a/test/cmdlineTests/model_checker_solvers_all_implicit/args b/test/cmdlineTests/model_checker_solvers_all_implicit/args new file mode 100644 index 000000000..5aeb1490e --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_all_implicit/args @@ -0,0 +1 @@ +--model-checker-engine all diff --git a/test/cmdlineTests/model_checker_solvers_all_implicit/err b/test/cmdlineTests/model_checker_solvers_all_implicit/err new file mode 100644 index 000000000..7fffab4ef --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_all_implicit/err @@ -0,0 +1,12 @@ +Warning: CHC: Assertion violation happens here. +Counterexample: + +x = 0 + +Transaction trace: +test.constructor() +test.f(0) + --> model_checker_solvers_all_implicit/input.sol:5:3: + | +5 | assert(x > 0); + | ^^^^^^^^^^^^^ diff --git a/test/cmdlineTests/model_checker_solvers_all_implicit/input.sol b/test/cmdlineTests/model_checker_solvers_all_implicit/input.sol new file mode 100644 index 000000000..7d05ef6e5 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_all_implicit/input.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract test { + function f(uint x) public pure { + assert(x > 0); + } +} \ No newline at end of file diff --git a/test/cmdlineTests/model_checker_solvers_smtlib2/args b/test/cmdlineTests/model_checker_solvers_smtlib2/args new file mode 100644 index 000000000..3d90e9161 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_smtlib2/args @@ -0,0 +1 @@ +--model-checker-engine all --model-checker-solvers smtlib2 diff --git a/test/cmdlineTests/model_checker_solvers_smtlib2/err b/test/cmdlineTests/model_checker_solvers_smtlib2/err new file mode 100644 index 000000000..9a156ff90 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_smtlib2/err @@ -0,0 +1,3 @@ +Warning: CHC analysis was not possible. No Horn solver was available. None of the installed solvers was enabled. + +Warning: BMC analysis was not possible. No SMT solver (Z3 or CVC4) was available. None of the installed solvers was enabled. diff --git a/test/cmdlineTests/model_checker_solvers_smtlib2/input.sol b/test/cmdlineTests/model_checker_solvers_smtlib2/input.sol new file mode 100644 index 000000000..7d05ef6e5 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_smtlib2/input.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract test { + function f(uint x) public pure { + assert(x > 0); + } +} \ No newline at end of file diff --git a/test/cmdlineTests/model_checker_solvers_wrong/args b/test/cmdlineTests/model_checker_solvers_wrong/args new file mode 100644 index 000000000..542502ee3 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_wrong/args @@ -0,0 +1 @@ +--model-checker-engine all --model-checker-solvers ultraSolver diff --git a/test/cmdlineTests/model_checker_solvers_wrong/err b/test/cmdlineTests/model_checker_solvers_wrong/err new file mode 100644 index 000000000..bf2df0a70 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_wrong/err @@ -0,0 +1 @@ +Invalid option for --model-checker-solvers: ultraSolver diff --git a/test/cmdlineTests/model_checker_solvers_wrong/exit b/test/cmdlineTests/model_checker_solvers_wrong/exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_wrong/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/model_checker_solvers_wrong/input.sol b/test/cmdlineTests/model_checker_solvers_wrong/input.sol new file mode 100644 index 000000000..7d05ef6e5 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_wrong/input.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract test { + function f(uint x) public pure { + assert(x > 0); + } +} \ No newline at end of file diff --git a/test/cmdlineTests/model_checker_solvers_wrong2/args b/test/cmdlineTests/model_checker_solvers_wrong2/args new file mode 100644 index 000000000..a20c99800 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_wrong2/args @@ -0,0 +1 @@ +--model-checker-engine all --model-checker-solvers z3, smtlib2 diff --git a/test/cmdlineTests/model_checker_solvers_wrong2/err b/test/cmdlineTests/model_checker_solvers_wrong2/err new file mode 100644 index 000000000..1bdd53068 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_wrong2/err @@ -0,0 +1 @@ +"smtlib2" is not found. diff --git a/test/cmdlineTests/model_checker_solvers_wrong2/exit b/test/cmdlineTests/model_checker_solvers_wrong2/exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_wrong2/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/model_checker_solvers_wrong2/input.sol b/test/cmdlineTests/model_checker_solvers_wrong2/input.sol new file mode 100644 index 000000000..7d05ef6e5 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_wrong2/input.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract test { + function f(uint x) public pure { + assert(x > 0); + } +} \ No newline at end of file diff --git a/test/cmdlineTests/model_checker_solvers_z3/args b/test/cmdlineTests/model_checker_solvers_z3/args new file mode 100644 index 000000000..ff27293ea --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_z3/args @@ -0,0 +1 @@ +--model-checker-engine all --model-checker-solvers z3 diff --git a/test/cmdlineTests/model_checker_solvers_z3/err b/test/cmdlineTests/model_checker_solvers_z3/err new file mode 100644 index 000000000..67349d8e1 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_z3/err @@ -0,0 +1,12 @@ +Warning: CHC: Assertion violation happens here. +Counterexample: + +x = 0 + +Transaction trace: +test.constructor() +test.f(0) + --> model_checker_solvers_z3/input.sol:5:3: + | +5 | assert(x > 0); + | ^^^^^^^^^^^^^ diff --git a/test/cmdlineTests/model_checker_solvers_z3/input.sol b/test/cmdlineTests/model_checker_solvers_z3/input.sol new file mode 100644 index 000000000..7d05ef6e5 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_z3/input.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract test { + function f(uint x) public pure { + assert(x > 0); + } +} \ No newline at end of file diff --git a/test/cmdlineTests/model_checker_solvers_z3_smtlib2/args b/test/cmdlineTests/model_checker_solvers_z3_smtlib2/args new file mode 100644 index 000000000..889888874 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_z3_smtlib2/args @@ -0,0 +1 @@ +--model-checker-engine all --model-checker-solvers z3,smtlib2 diff --git a/test/cmdlineTests/model_checker_solvers_z3_smtlib2/err b/test/cmdlineTests/model_checker_solvers_z3_smtlib2/err new file mode 100644 index 000000000..5699afcde --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_z3_smtlib2/err @@ -0,0 +1,12 @@ +Warning: CHC: Assertion violation happens here. +Counterexample: + +x = 0 + +Transaction trace: +test.constructor() +test.f(0) + --> model_checker_solvers_z3_smtlib2/input.sol:5:3: + | +5 | assert(x > 0); + | ^^^^^^^^^^^^^ diff --git a/test/cmdlineTests/model_checker_solvers_z3_smtlib2/input.sol b/test/cmdlineTests/model_checker_solvers_z3_smtlib2/input.sol new file mode 100644 index 000000000..7d05ef6e5 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_z3_smtlib2/input.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract test { + function f(uint x) public pure { + assert(x > 0); + } +} \ No newline at end of file diff --git a/test/cmdlineTests/standard_model_checker_solvers_all/input.json b/test/cmdlineTests/standard_model_checker_solvers_all/input.json new file mode 100644 index 000000000..db9f2232d --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_solvers_all/input.json @@ -0,0 +1,17 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\n\ncontract C { function f(uint x) public pure { assert(x > 0); } }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "all" + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_solvers_all/output.json b/test/cmdlineTests/standard_model_checker_solvers_all/output.json new file mode 100644 index 000000000..12dfe5871 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_solvers_all/output.json @@ -0,0 +1,21 @@ +{"errors":[{"component":"general","errorCode":"6328","formattedMessage":"Warning: CHC: Assertion violation happens here. +Counterexample: + +x = 0 + +Transaction trace: +C.constructor() +C.f(0) + --> A:4:47: + | +4 | contract C { function f(uint x) public pure { assert(x > 0); } } + | ^^^^^^^^^^^^^ + +","message":"CHC: Assertion violation happens here. +Counterexample: + +x = 0 + +Transaction trace: +C.constructor() +C.f(0)","severity":"warning","sourceLocation":{"end":119,"file":"A","start":106},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_solvers_none/input.json b/test/cmdlineTests/standard_model_checker_solvers_none/input.json new file mode 100644 index 000000000..07cbb88ac --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_solvers_none/input.json @@ -0,0 +1,18 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\n\ncontract C { function f(uint x) public pure { assert(x > 0); } }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "all", + "solvers": [] + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_solvers_none/output.json b/test/cmdlineTests/standard_model_checker_solvers_none/output.json new file mode 100644 index 000000000..b237fadce --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_solvers_none/output.json @@ -0,0 +1,5 @@ +{"errors":[{"component":"general","errorCode":"7649","formattedMessage":"Warning: CHC analysis was not possible since no Horn solver was enabled. + +","message":"CHC analysis was not possible since no Horn solver was enabled.","severity":"warning","type":"Warning"},{"component":"general","errorCode":"7710","formattedMessage":"Warning: BMC analysis was not possible since no SMT solver was found and enabled. + +","message":"BMC analysis was not possible since no SMT solver was found and enabled.","severity":"warning","type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_solvers_smtlib2/input.json b/test/cmdlineTests/standard_model_checker_solvers_smtlib2/input.json new file mode 100644 index 000000000..8903ec016 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_solvers_smtlib2/input.json @@ -0,0 +1,18 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\n\ncontract C { function f(uint x) public pure { assert(x > 0); } }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "all", + "solvers": ["smtlib2"] + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_solvers_smtlib2/output.json b/test/cmdlineTests/standard_model_checker_solvers_smtlib2/output.json new file mode 100644 index 000000000..2ab2d6b5e --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_solvers_smtlib2/output.json @@ -0,0 +1,138 @@ +{"auxiliaryInputRequested":{"smtlib2queries":{"0x4ee925df3426fec84707cea517cc52ed216229bfde71458a69e7edeece2c071e":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-fun |x_3_0| () Int) +(declare-fun |expr_7_0| () Int) +(declare-fun |expr_8_0| () Int) +(declare-fun |expr_9_1| () Bool) + +(assert (and (and (and true true) (and (= expr_9_1 (> expr_7_0 expr_8_0)) (and (=> (and true true) true) (and (= expr_8_0 0) (and (=> (and true true) (and (>= expr_7_0 0) (<= expr_7_0 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_7_0 x_3_0) (and (and (>= x_3_0 0) (<= x_3_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 3017696395)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 179)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 222)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 100)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 139)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true)))))))) (not expr_9_1))) +(declare-const |EVALEXPR_0| Int) +(assert (= |EVALEXPR_0| x_3_0)) +(check-sat) +(get-value (|EVALEXPR_0| )) +","0x540d6953fff999f72ca33d8af8b900986dc012a365fc1b8572aa9d9978f1392b":"(set-logic HORN) + +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |interface_0_C_14_0| (Int |abi_type| |crypto_type| |state_type| ) Bool) +(declare-fun |nondet_interface_1_C_14_0| (Int Int |abi_type| |crypto_type| |state_type| |state_type| ) Bool) +(declare-fun |summary_constructor_2_C_14_0| (Int Int |abi_type| |crypto_type| |tx_type| |state_type| |state_type| ) Bool) +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (state_0 |state_type|) (this_0 Int) (tx_0 |tx_type|)) +(=> (= error_0 0) (nondet_interface_1_C_14_0 error_0 this_0 abi_0 crypto_0 state_0 state_0)))) + + +(declare-fun |summary_3_function_f__13_14_0| (Int Int |abi_type| |crypto_type| |tx_type| |state_type| Int |state_type| Int ) Bool) +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (and (and (nondet_interface_1_C_14_0 error_0 this_0 abi_0 crypto_0 state_0 state_1) true) (and (= error_0 0) (summary_3_function_f__13_14_0 error_1 this_0 abi_0 crypto_0 tx_0 state_1 x_3_0 state_2 x_3_1))) (nondet_interface_1_C_14_0 error_1 this_0 abi_0 crypto_0 state_0 state_2)))) + + +(declare-fun |block_4_function_f__13_14_0| (Int Int |abi_type| |crypto_type| |tx_type| |state_type| Int |state_type| Int ) Bool) +(declare-fun |block_5_f_12_14_0| (Int Int |abi_type| |crypto_type| |tx_type| |state_type| Int |state_type| Int ) Bool) +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(block_4_function_f__13_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 x_3_0 state_1 x_3_1))) + + +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (and (and (block_4_function_f__13_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 x_3_0 state_1 x_3_1) (and (and (and (and (= state_1 state_0) (= error_0 0)) true) (and true (= x_3_1 x_3_0))) true)) true) (block_5_f_12_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 x_3_0 state_1 x_3_1)))) + + +(declare-fun |block_6_return_function_f__13_14_0| (Int Int |abi_type| |crypto_type| |tx_type| |state_type| Int |state_type| Int ) Bool) +(declare-fun |block_7_function_f__13_14_0| (Int Int |abi_type| |crypto_type| |tx_type| |state_type| Int |state_type| Int ) Bool) +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (and (and (block_5_f_12_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 x_3_0 state_1 x_3_1) (and (= expr_9_1 (> expr_7_0 expr_8_0)) (and (=> true true) (and (= expr_8_0 0) (and (=> true (and (>= expr_7_0 0) (<= expr_7_0 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_7_0 x_3_1) (and (and (>= x_3_1 0) (<= x_3_1 115792089237316195423570985008687907853269984665640564039457584007913129639935)) true))))))) (and (not expr_9_1) (= error_1 1))) (block_7_function_f__13_14_0 error_1 this_0 abi_0 crypto_0 tx_0 state_0 x_3_0 state_1 x_3_1)))) + + +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (block_7_function_f__13_14_0 error_1 this_0 abi_0 crypto_0 tx_0 state_0 x_3_0 state_1 x_3_1) (summary_3_function_f__13_14_0 error_1 this_0 abi_0 crypto_0 tx_0 state_0 x_3_0 state_1 x_3_1)))) + + +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (and (and (block_5_f_12_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 x_3_0 state_1 x_3_1) (and (= error_1 error_0) (and (= expr_9_1 (> expr_7_0 expr_8_0)) (and (=> true true) (and (= expr_8_0 0) (and (=> true (and (>= expr_7_0 0) (<= expr_7_0 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_7_0 x_3_1) (and (and (>= x_3_1 0) (<= x_3_1 115792089237316195423570985008687907853269984665640564039457584007913129639935)) true)))))))) true) (block_6_return_function_f__13_14_0 error_1 this_0 abi_0 crypto_0 tx_0 state_0 x_3_0 state_1 x_3_1)))) + + +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (and (and (block_6_return_function_f__13_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 x_3_0 state_1 x_3_1) true) true) (summary_3_function_f__13_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 x_3_0 state_1 x_3_1)))) + + +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (and (and (interface_0_C_14_0 this_0 abi_0 crypto_0 state_0) true) (and (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 3017696395)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 179)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 222)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 100)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 139)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) (summary_3_function_f__13_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 x_3_0 state_1 x_3_1)) (= error_0 0))) (interface_0_C_14_0 this_0 abi_0 crypto_0 state_1)))) + + +(declare-fun |contract_initializer_8_C_14_0| (Int Int |abi_type| |crypto_type| |tx_type| |state_type| |state_type| ) Bool) +(declare-fun |contract_initializer_entry_9_C_14_0| (Int Int |abi_type| |crypto_type| |tx_type| |state_type| |state_type| ) Bool) +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (and (and (= state_1 state_0) (= error_0 0)) true) (contract_initializer_entry_9_C_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 state_1)))) + + +(declare-fun |contract_initializer_after_init_10_C_14_0| (Int Int |abi_type| |crypto_type| |tx_type| |state_type| |state_type| ) Bool) +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (and (and (contract_initializer_entry_9_C_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 state_1) true) true) (contract_initializer_after_init_10_C_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 state_1)))) + + +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (and (and (contract_initializer_after_init_10_C_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 state_1) true) true) (contract_initializer_8_C_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 state_1)))) + + +(declare-fun |implicit_constructor_entry_11_C_14_0| (Int Int |abi_type| |crypto_type| |tx_type| |state_type| |state_type| ) Bool) +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (and (and (and (= state_1 state_0) (= error_0 0)) true) true) (implicit_constructor_entry_11_C_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 state_1)))) + + +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (and (and (implicit_constructor_entry_11_C_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 state_1) (and (contract_initializer_8_C_14_0 error_1 this_0 abi_0 crypto_0 tx_0 state_1 state_2) true)) (> error_1 0)) (summary_constructor_2_C_14_0 error_1 this_0 abi_0 crypto_0 tx_0 state_0 state_2)))) + + +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (and (and (implicit_constructor_entry_11_C_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 state_1) (and (= error_1 0) (and (contract_initializer_8_C_14_0 error_1 this_0 abi_0 crypto_0 tx_0 state_1 state_2) true))) true) (summary_constructor_2_C_14_0 error_1 this_0 abi_0 crypto_0 tx_0 state_0 state_2)))) + + +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (and (and (summary_constructor_2_C_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 state_1) true) (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (= (|msg.value| tx_0) 0)) (= error_0 0))) (interface_0_C_14_0 this_0 abi_0 crypto_0 state_1)))) + + +(declare-fun |error_target_2_0| () Bool) +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int) (x_3_2 Int)) +(=> (and (and (interface_0_C_14_0 this_0 abi_0 crypto_0 state_0) true) (and (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 3017696395)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 179)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 222)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 100)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 139)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) (summary_3_function_f__13_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 x_3_0 state_1 x_3_1)) (= error_0 1))) error_target_2_0))) + + +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int) (x_3_2 Int)) +(=> error_target_2_0 false))) +(check-sat)"}},"errors":[{"component":"general","errorCode":"3996","formattedMessage":"Warning: CHC analysis was not possible. No Horn solver was available. None of the installed solvers was enabled. + +","message":"CHC analysis was not possible. No Horn solver was available. None of the installed solvers was enabled.","severity":"warning","type":"Warning"},{"component":"general","errorCode":"8084","formattedMessage":"Warning: BMC analysis was not possible. No SMT solver (Z3 or CVC4) was available. None of the installed solvers was enabled. + +","message":"BMC analysis was not possible. No SMT solver (Z3 or CVC4) was available. None of the installed solvers was enabled.","severity":"warning","type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_solvers_z3/input.json b/test/cmdlineTests/standard_model_checker_solvers_z3/input.json new file mode 100644 index 000000000..91db9ad7f --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_solvers_z3/input.json @@ -0,0 +1,18 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\n\ncontract C { function f(uint x) public pure { assert(x > 0); } }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "all", + "solvers": ["z3"] + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_solvers_z3/output.json b/test/cmdlineTests/standard_model_checker_solvers_z3/output.json new file mode 100644 index 000000000..12dfe5871 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_solvers_z3/output.json @@ -0,0 +1,21 @@ +{"errors":[{"component":"general","errorCode":"6328","formattedMessage":"Warning: CHC: Assertion violation happens here. +Counterexample: + +x = 0 + +Transaction trace: +C.constructor() +C.f(0) + --> A:4:47: + | +4 | contract C { function f(uint x) public pure { assert(x > 0); } } + | ^^^^^^^^^^^^^ + +","message":"CHC: Assertion violation happens here. +Counterexample: + +x = 0 + +Transaction trace: +C.constructor() +C.f(0)","severity":"warning","sourceLocation":{"end":119,"file":"A","start":106},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_solvers_z3_smtlib2/input.json b/test/cmdlineTests/standard_model_checker_solvers_z3_smtlib2/input.json new file mode 100644 index 000000000..19913a562 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_solvers_z3_smtlib2/input.json @@ -0,0 +1,18 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\n\ncontract C { function f(uint x) public pure { assert(x > 0); } }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "all", + "solvers": ["z3", "smtlib2"] + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_solvers_z3_smtlib2/output.json b/test/cmdlineTests/standard_model_checker_solvers_z3_smtlib2/output.json new file mode 100644 index 000000000..12dfe5871 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_solvers_z3_smtlib2/output.json @@ -0,0 +1,21 @@ +{"errors":[{"component":"general","errorCode":"6328","formattedMessage":"Warning: CHC: Assertion violation happens here. +Counterexample: + +x = 0 + +Transaction trace: +C.constructor() +C.f(0) + --> A:4:47: + | +4 | contract C { function f(uint x) public pure { assert(x > 0); } } + | ^^^^^^^^^^^^^ + +","message":"CHC: Assertion violation happens here. +Counterexample: + +x = 0 + +Transaction trace: +C.constructor() +C.f(0)","severity":"warning","sourceLocation":{"end":119,"file":"A","start":106},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/externalTests/solc-js/solc-js.sh b/test/externalTests/solc-js/solc-js.sh index 9cc753959..3762ba7a2 100755 --- a/test/externalTests/solc-js/solc-js.sh +++ b/test/externalTests/solc-js/solc-js.sh @@ -47,8 +47,10 @@ function solcjs_test cp -Rf "$SOLCJS_INPUT_DIR/DAO" test/ printLog "Copying SMTChecker tests..." - cp -Rf "$TEST_DIR"/test/libsolidity/smtCheckerTests test/ - rm -rf test/smtCheckerTests/imports + # We do not copy all tests because that takes too long. + cp -Rf "$TEST_DIR"/test/libsolidity/smtCheckerTests/external_calls test/smtCheckerTests/ + cp -Rf "$TEST_DIR"/test/libsolidity/smtCheckerTests/loops test/smtCheckerTests/ + cp -Rf "$TEST_DIR"/test/libsolidity/smtCheckerTests/invariants test/smtCheckerTests/ # Update version (needed for some tests) echo "Updating package.json to version $VERSION" diff --git a/test/libsolidity/SMTCheckerTest.cpp b/test/libsolidity/SMTCheckerTest.cpp index bdc408b9f..961baab70 100644 --- a/test/libsolidity/SMTCheckerTest.cpp +++ b/test/libsolidity/SMTCheckerTest.cpp @@ -29,21 +29,13 @@ SMTCheckerTest::SMTCheckerTest(string const& _filename): SyntaxTest(_filename, E { auto const& choice = m_reader.stringSetting("SMTSolvers", "any"); if (choice == "any") - m_enabledSolvers = smtutil::SMTSolverChoice::All(); - else if (choice == "z3") - m_enabledSolvers = smtutil::SMTSolverChoice::Z3(); - else if (choice == "cvc4") - m_enabledSolvers = smtutil::SMTSolverChoice::CVC4(); + m_modelCheckerSettings.solvers = smtutil::SMTSolverChoice::All(); else if (choice == "none") - m_enabledSolvers = smtutil::SMTSolverChoice::None(); - else + m_modelCheckerSettings.solvers = smtutil::SMTSolverChoice::None(); + else if (!m_modelCheckerSettings.solvers.setSolver(choice)) BOOST_THROW_EXCEPTION(runtime_error("Invalid SMT solver choice.")); - auto available = ModelChecker::availableSolvers(); - if (!available.z3) - m_enabledSolvers.z3 = false; - if (!available.cvc4) - m_enabledSolvers.cvc4 = false; + m_modelCheckerSettings.solvers &= ModelChecker::availableSolvers(); auto engine = ModelCheckerEngine::fromString(m_reader.stringSetting("SMTEngine", "all")); if (engine) @@ -51,7 +43,7 @@ SMTCheckerTest::SMTCheckerTest(string const& _filename): SyntaxTest(_filename, E else BOOST_THROW_EXCEPTION(runtime_error("Invalid SMT engine choice.")); - if (m_enabledSolvers.none() || m_modelCheckerSettings.engine.none()) + if (m_modelCheckerSettings.solvers.none() || m_modelCheckerSettings.engine.none()) m_shouldRun = false; auto const& ignoreCex = m_reader.stringSetting("SMTIgnoreCex", "no"); @@ -66,7 +58,6 @@ SMTCheckerTest::SMTCheckerTest(string const& _filename): SyntaxTest(_filename, E TestCase::TestResult SMTCheckerTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) { setupCompiler(); - compiler().setSMTSolverChoice(m_enabledSolvers); compiler().setModelCheckerSettings(m_modelCheckerSettings); parseAndAnalyze(); filterObtainedErrors(); diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp index 3f12b85fd..21ef80894 100644 --- a/test/solc/CommandLineParser.cpp +++ b/test/solc/CommandLineParser.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -82,6 +83,7 @@ BOOST_AUTO_TEST_CASE(no_options) expectedOptions.modelChecker.settings = { ModelCheckerContracts::Default(), ModelCheckerEngine::None(), + smtutil::SMTSolverChoice::All(), ModelCheckerTargets::Default(), nullopt, }; @@ -150,6 +152,7 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) "--yul-optimizations=agf", "--model-checker-contracts=contract1.yul:A,contract2.yul:B", "--model-checker-engine=bmc", + "--model-checker-solvers=z3,smtlib2", "--model-checker-targets=underflow,divByZero", "--model-checker-timeout=5", }; @@ -207,6 +210,7 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) expectedOptions.modelChecker.settings = { {{{"contract1.yul", {"A"}}, {"contract2.yul", {"B"}}}}, {true, false}, + {false, true, true}, {{VerificationTargetType::Underflow, VerificationTargetType::DivByZero}}, 5, }; @@ -276,6 +280,7 @@ BOOST_AUTO_TEST_CASE(assembly_mode_options) "contract1.yul:A," "contract2.yul:B", "--model-checker-engine=bmc", // Ignored in assembly mode + "--model-checker-solvers=z3,smtlib2", // Ignored in assembly mode "--model-checker-targets=" // Ignored in assembly mode "underflow," "divByZero", @@ -372,6 +377,7 @@ BOOST_AUTO_TEST_CASE(standard_json_mode_options) "contract1.yul:A," "contract2.yul:B", "--model-checker-engine=bmc", // Ignored in Standard JSON mode + "--model-checker-solvers=z3,smtlib2", // Ignored in Standard JSON mode "--model-checker-targets=" // Ignored in Standard JSON mode "underflow," "divByZero", diff --git a/test/tools/fuzzer_common.cpp b/test/tools/fuzzer_common.cpp index 99a3775e1..ef85288b0 100644 --- a/test/tools/fuzzer_common.cpp +++ b/test/tools/fuzzer_common.cpp @@ -16,7 +16,6 @@ */ // SPDX-License-Identifier: GPL-3.0 -#include "libsolidity/formal/ModelCheckerSettings.h" #include #include @@ -105,6 +104,7 @@ void FuzzerUtil::testCompiler( compiler.setModelCheckerSettings({ frontend::ModelCheckerContracts::Default(), frontend::ModelCheckerEngine::All(), + smtutil::SMTSolverChoice::All(), frontend::ModelCheckerTargets::Default(), /*timeout=*/1 }); From ad3d3c0df6bd9b468ab9c76840fbd95632c5f9e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 28 Jul 2021 01:02:13 +0200 Subject: [PATCH 78/98] cmdlineTests.sh: Accept test name patterns as positional arguments --- test/cmdlineTests.sh | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index 7deaa0c32..eefa8c8f2 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -37,11 +37,8 @@ source "${REPO_ROOT}/scripts/common.sh" # shellcheck source=scripts/common_cmdline.sh source "${REPO_ROOT}/scripts/common_cmdline.sh" -(( $# <= 1 )) || { printError "Too many arguments"; exit 1; } -(( $# == 0 )) || [[ $1 == '--update' ]] || { printError "Invalid argument: '$1'"; exit 1; } - AUTOUPDATE=false -[[ $1 == --update ]] && AUTOUPDATE=true +[[ $1 == --update ]] && AUTOUPDATE=true && shift case "$OSTYPE" in msys) @@ -294,8 +291,21 @@ test_solc_behaviour "${0}" "ctx:=/some/remapping/target" "" "" 1 "" "Invalid rem printTask "Running general commandline tests..." ( cd "$REPO_ROOT"/test/cmdlineTests/ - for tdir in */ + for tdir in ${*:-*/} do + if ! [[ -d $tdir ]]; then + if [[ $tdir =~ ^--.*$ ]]; then + if [[ $tdir == "--update" ]]; then + printError "The --update option must be given before any positional arguments." + else + printError "Invalid option: $tdir." + fi + else + printError "Test directory not found: $tdir" + fi + exit 1 + fi + printTask " - ${tdir}" # Strip trailing slash from $tdir. From 056d780b63b10fa847e8b274f4c41b600b9aa5bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 28 Jul 2021 01:00:52 +0200 Subject: [PATCH 79/98] Better error message when base path does not exist --- solc/CommandLineInterface.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 91f2baf9a..32224ac6e 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -383,10 +383,19 @@ bool CommandLineInterface::readInputFiles() m_fileReader.setBasePath(m_options.input.basePath); - if (m_fileReader.basePath() != "" && !boost::filesystem::is_directory(m_fileReader.basePath())) + if (m_fileReader.basePath() != "") { - serr() << "Base path must be a directory: " << m_fileReader.basePath() << endl; - return false; + if (!boost::filesystem::exists(m_fileReader.basePath())) + { + serr() << "Base path does not exist: " << m_fileReader.basePath() << endl; + return false; + } + + if (!boost::filesystem::is_directory(m_fileReader.basePath())) + { + serr() << "Base path is not a directory: " << m_fileReader.basePath() << endl; + return false; + } } for (boost::filesystem::path const& allowedDirectory: m_options.input.allowedDirectories) From 3a0d654dce128e4e0a32e7460c426ca42c3429e1 Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Wed, 28 Jul 2021 12:17:18 +0200 Subject: [PATCH 80/98] Document free memory pointer overflow check in Sol->Yul. Co-authored-by: chriseth --- docs/ir/ir-breaking-changes.rst | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/ir/ir-breaking-changes.rst b/docs/ir/ir-breaking-changes.rst index c9c0936cc..4f5e21c08 100644 --- a/docs/ir/ir-breaking-changes.rst +++ b/docs/ir/ir-breaking-changes.rst @@ -193,6 +193,30 @@ This causes differences in some contracts, for example: - Old code generator: ``aMod = 0`` and ``mMod = 2`` - New code generator: ``aMod = 4`` and ``mMod = 0`` +- The new code generator imposes a hard limit of ``type(uint64).max`` (``0xffffffffffffffff``) for the free memory pointer. Allocations that would increase its value beyond this limit revert. The old code generator does not have this limit. + + For example: + + .. code-block:: solidity + + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >0.8.0; + contract C { + function f() public { + uint[] memory arr; + // allocation size: 576460752303423481 + // assumes freeMemPtr points to 0x80 initially + uint solYulMaxAllocationBeforeMemPtrOverflow = (type(uint64).max - 0x80 - 31) / 32; + // freeMemPtr overflows UINT64_MAX + arr = new uint[](solYulMaxAllocationBeforeMemPtrOverflow); + } + } + + The function `f()` behaves as follows: + + - Old code generator: runs out of gas while zeroing the array contents after the large memory allocation + - New code generator: reverts due to free memory pointer overflow (does not run out of gas) + Internals ========= From c627e6af1087b31d5f0569fa597c330d3e584ba2 Mon Sep 17 00:00:00 2001 From: jaa2 <43010335+jaa2@users.noreply.github.com> Date: Wed, 28 Jul 2021 10:53:19 -0500 Subject: [PATCH 81/98] CommandLineParser: Handle --optimize-runs option in assembly mode Fixes #11708. --- Changelog.md | 1 + docs/internals/optimizer.rst | 2 + docs/yul.rst | 6 ++- solc/CommandLineInterface.cpp | 7 ++- solc/CommandLineInterface.h | 1 + solc/CommandLineParser.cpp | 2 + solc/CommandLineParser.h | 2 +- test/cmdlineTests/yul_optimize_runs/args | 1 + test/cmdlineTests/yul_optimize_runs/err | 1 + test/cmdlineTests/yul_optimize_runs/input.yul | 13 +++++ test/cmdlineTests/yul_optimize_runs/output | 52 +++++++++++++++++++ test/solc/CommandLineParser.cpp | 5 +- 12 files changed, 88 insertions(+), 5 deletions(-) create mode 100644 test/cmdlineTests/yul_optimize_runs/args create mode 100644 test/cmdlineTests/yul_optimize_runs/err create mode 100644 test/cmdlineTests/yul_optimize_runs/input.yul create mode 100644 test/cmdlineTests/yul_optimize_runs/output diff --git a/Changelog.md b/Changelog.md index 490d054dd..3546a754f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -22,6 +22,7 @@ Bugfixes: * Yul Code Generator: Fix internal compiler error when using a long literal with bitwise negation. * Yul Code Generator: Fix source location references for calls to builtin functions. * Yul Parser: Fix source location references for ``if`` statements. + * Commandline Interface: Apply ``--optimizer-runs`` option in assembly / yul mode. ### 0.8.6 (2021-06-22) diff --git a/docs/internals/optimizer.rst b/docs/internals/optimizer.rst index aee7c8493..7a36b06cc 100644 --- a/docs/internals/optimizer.rst +++ b/docs/internals/optimizer.rst @@ -49,6 +49,8 @@ differences, for example, functions may be inlined, combined, or rewritten to el redundancies, etc. (compare the output between the flags ``--ir`` and ``--optimize --ir-optimized``). +.. _optimizer-parameter-runs: + Optimizer Parameter Runs ======================== diff --git a/docs/yul.rst b/docs/yul.rst index b8557475f..83ddef935 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -1177,11 +1177,13 @@ intermediate states. This allows for easy debugging and verification of the opti Please refer to the general :ref:`optimizer documentation ` for more details about the different optimization stages and how to use the optimizer. -If you want to use Solidity in stand-alone Yul mode, you activate the optimizer using ``--optimize``: +If you want to use Solidity in stand-alone Yul mode, you activate the optimizer using ``--optimize`` +and optionally specify the :ref:`expected number of contract executions ` with +``--optimize-runs``: .. code-block:: sh - solc --strict-assembly --optimize + solc --strict-assembly --optimize --optimize-runs 200 In Solidity mode, the Yul optimizer is activated together with the regular optimizer. diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 32224ac6e..9519e679d 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -555,6 +555,7 @@ bool CommandLineInterface::processInput() m_options.assembly.inputLanguage, m_options.assembly.targetMachine, m_options.optimizer.enabled, + m_options.optimizer.expectedExecutionsPerDeployment, m_options.optimizer.yulSteps ); } @@ -595,7 +596,8 @@ bool CommandLineInterface::compile() m_compiler->enableEwasmGeneration(m_options.compiler.outputs.ewasm); OptimiserSettings settings = m_options.optimizer.enabled ? OptimiserSettings::standard() : OptimiserSettings::minimal(); - settings.expectedExecutionsPerDeployment = m_options.optimizer.expectedExecutionsPerDeployment; + if (m_options.optimizer.expectedExecutionsPerDeployment.has_value()) + settings.expectedExecutionsPerDeployment = m_options.optimizer.expectedExecutionsPerDeployment.value(); if (m_options.optimizer.noOptimizeYul) settings.runYulOptimiser = false; @@ -941,6 +943,7 @@ bool CommandLineInterface::assemble( yul::AssemblyStack::Language _language, yul::AssemblyStack::Machine _targetMachine, bool _optimize, + optional _expectedExecutionsPerDeployment, optional _yulOptimiserSteps ) { @@ -951,6 +954,8 @@ bool CommandLineInterface::assemble( for (auto const& src: m_fileReader.sourceCodes()) { OptimiserSettings settings = _optimize ? OptimiserSettings::full() : OptimiserSettings::minimal(); + if (_expectedExecutionsPerDeployment.has_value()) + settings.expectedExecutionsPerDeployment = _expectedExecutionsPerDeployment.value(); if (_yulOptimiserSteps.has_value()) settings.yulOptimiserSteps = _yulOptimiserSteps.value(); diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 8e17489a7..488ec1d71 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -78,6 +78,7 @@ private: yul::AssemblyStack::Language _language, yul::AssemblyStack::Machine _targetMachine, bool _optimize, + std::optional _expectedExecutionsPerDeployment = std::nullopt, std::optional _yulOptimiserSteps = std::nullopt ); diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 5a1daed1e..59972fb3b 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -967,6 +967,8 @@ General Information)").c_str(), m_options.optimizer.enabled = (m_args.count(g_strOptimize) > 0); m_options.optimizer.noOptimizeYul = (m_args.count(g_strNoOptimizeYul) > 0); + m_options.optimizer.expectedExecutionsPerDeployment = m_args.at(g_strOptimizeRuns).as(); + if (m_args.count(g_strYulOptimizations)) { if (!m_options.optimizer.enabled) diff --git a/solc/CommandLineParser.h b/solc/CommandLineParser.h index 176966601..423dacb5f 100644 --- a/solc/CommandLineParser.h +++ b/solc/CommandLineParser.h @@ -159,7 +159,7 @@ struct CommandLineOptions struct { bool enabled = false; - unsigned expectedExecutionsPerDeployment = 0; + std::optional expectedExecutionsPerDeployment; bool noOptimizeYul = false; std::optional yulSteps; } optimizer; diff --git a/test/cmdlineTests/yul_optimize_runs/args b/test/cmdlineTests/yul_optimize_runs/args new file mode 100644 index 000000000..b48e7aeed --- /dev/null +++ b/test/cmdlineTests/yul_optimize_runs/args @@ -0,0 +1 @@ +--yul --yul-dialect evm --optimize --ir-optimized --optimize-runs 10000 diff --git a/test/cmdlineTests/yul_optimize_runs/err b/test/cmdlineTests/yul_optimize_runs/err new file mode 100644 index 000000000..014a1178f --- /dev/null +++ b/test/cmdlineTests/yul_optimize_runs/err @@ -0,0 +1 @@ +Warning: Yul is still experimental. Please use the output with care. diff --git a/test/cmdlineTests/yul_optimize_runs/input.yul b/test/cmdlineTests/yul_optimize_runs/input.yul new file mode 100644 index 000000000..aeb7fd282 --- /dev/null +++ b/test/cmdlineTests/yul_optimize_runs/input.yul @@ -0,0 +1,13 @@ +object "RunsTest1" { + code { + // Deploy the contract + datacopy(0, dataoffset("Runtime"), datasize("Runtime")) + return(0, datasize("Runtime")) + } + object "Runtime" { + code { + let funcSel := shl(224, 0xabc12345) + mstore(0, funcSel) + } + } +} diff --git a/test/cmdlineTests/yul_optimize_runs/output b/test/cmdlineTests/yul_optimize_runs/output new file mode 100644 index 000000000..20196dcb5 --- /dev/null +++ b/test/cmdlineTests/yul_optimize_runs/output @@ -0,0 +1,52 @@ + +======= yul_optimize_runs/input.yul (EVM) ======= + +Pretty printed source: +object "RunsTest1" { + code { + { + let _1 := datasize("Runtime") + datacopy(0, dataoffset("Runtime"), _1) + return(0, _1) + } + } + object "Runtime" { + code { + { + mstore(0, 0xabc1234500000000000000000000000000000000000000000000000000000000) + } + } + } +} + + +Binary representation: +602480600d600039806000f3fe7fabc1234500000000000000000000000000000000000000000000000000000000600052 + +Text representation: + /* "yul_optimize_runs/input.yul":106:125 */ + dataSize(sub_0) + dup1 + /* "yul_optimize_runs/input.yul":83:104 */ + dataOffset(sub_0) + /* "yul_optimize_runs/input.yul":80:81 */ + 0x00 + /* "yul_optimize_runs/input.yul":71:126 */ + codecopy + /* "yul_optimize_runs/input.yul":145:164 */ + dup1 + /* "yul_optimize_runs/input.yul":80:81 */ + 0x00 + /* "yul_optimize_runs/input.yul":135:165 */ + return +stop + +sub_0: assembly { + /* "yul_optimize_runs/input.yul":237:257 */ + 0xabc1234500000000000000000000000000000000000000000000000000000000 + /* "yul_optimize_runs/input.yul":277:278 */ + 0x00 + /* "yul_optimize_runs/input.yul":270:288 */ + mstore +} + diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp index 21ef80894..8d24f97aa 100644 --- a/test/solc/CommandLineParser.cpp +++ b/test/solc/CommandLineParser.cpp @@ -294,7 +294,7 @@ BOOST_AUTO_TEST_CASE(assembly_mode_options) if (expectedLanguage == AssemblyStack::Language::StrictAssembly || expectedLanguage == AssemblyStack::Language::Ewasm) commandLine += vector{ "--optimize", - "--optimize-runs=1000", // Ignored in assembly mode + "--optimize-runs=1000", "--yul-optimizations=agf", }; @@ -332,7 +332,10 @@ BOOST_AUTO_TEST_CASE(assembly_mode_options) { expectedOptions.optimizer.enabled = true; expectedOptions.optimizer.yulSteps = "agf"; + expectedOptions.optimizer.expectedExecutionsPerDeployment = 1000; } + else + expectedOptions.optimizer.expectedExecutionsPerDeployment = OptimiserSettings{}.expectedExecutionsPerDeployment; stringstream sout, serr; optional parsedOptions = parseCommandLine(commandLine, sout, serr); From c6fc9934fa5370a815632a35eb35274d0ea1e9fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 27 Jul 2021 17:05:24 +0200 Subject: [PATCH 82/98] Clarify optional parts in remappings --- docs/path-resolution.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/path-resolution.rst b/docs/path-resolution.rst index 88f5fd224..1d4b200de 100644 --- a/docs/path-resolution.rst +++ b/docs/path-resolution.rst @@ -464,7 +464,8 @@ Here are the detailed rules governing the behaviour of remappings: #. **Prefix cannot be empty but context and target are optional.** - If ``target`` is omitted, it defaults to the value of the ``prefix``. + - If ``target`` is the empty string, ``prefix`` is simply removed from import paths. + - Empty ``context`` means that the remapping applies to all imports in all source units. .. index:: Remix IDE, file:// From 363278d2ec21830526ed9155d59dda200092c90d Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 2 Aug 2021 09:58:20 +0200 Subject: [PATCH 83/98] Clarify Yul scoping rules. --- docs/yul.rst | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/docs/yul.rst b/docs/yul.rst index 83ddef935..0f7d3cef3 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -546,11 +546,18 @@ as explained below) and all declarations introduce new identifiers into these scopes. Identifiers are visible in -the block they are defined in (including all sub-nodes and sub-blocks). +the block they are defined in (including all sub-nodes and sub-blocks): +Functions are visible in the whole block (even before their definitions) while +variables are only visible starting from the statement after the ``VariableDeclaration``. -As an exception, the scope of the "init" part of the or-loop +In particular, +variables cannot be referenced in the right hand side of their own variable +declaration. +Functions can be referenced already before their declaration (if they are visible). + +As an exception to the general scoping rule, the scope of the "init" part of the for-loop (the first block) extends across all other parts of the for loop. -This means that variables declared in the init part (but not inside a +This means that variables (and functions) declared in the init part (but not inside a block inside the init part) are visible in all other parts of the for-loop. Identifiers declared in the other parts of the for loop respect the regular @@ -559,21 +566,15 @@ syntactical scoping rules. This means a for-loop of the form ``for { I... } C { P... } { B... }`` is equivalent to ``{ I... for {} C { P... } { B... } }``. - The parameters and return parameters of functions are visible in the function body and their names have to be distinct. -Variables can only be referenced after their declaration. In particular, -variables cannot be referenced in the right hand side of their own variable -declaration. -Functions can be referenced already before their declaration (if they are visible). +Inside functions, it is not possible to reference a variable that was declared +outside of that function. Shadowing is disallowed, i.e. you cannot declare an identifier at a point where another identifier with the same name is also visible, even if it is -not accessible. - -Inside functions, it is not possible to access a variable that was declared -outside of that function. +not possible to reference it because it was declared outside the current function. Formal Specification -------------------- From 2ee6c7be646559c27b62e9eefd76b91bb74a24ed Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Tue, 13 Jul 2021 16:06:07 +0200 Subject: [PATCH 84/98] Print @src and @use-src locations in AsmPrinter --- libsolidity/codegen/ir/IRGenerator.cpp | 52 +- libyul/AsmPrinter.cpp | 125 ++- libyul/AsmPrinter.h | 66 +- libyul/Object.cpp | 27 +- libyul/Object.h | 34 +- libyul/ObjectParser.cpp | 5 +- libyul/ObjectParser.h | 1 - test/cmdlineTests.sh | 1 + .../constant_optimizer_yul/output | 7 + test/cmdlineTests/exp_base_literal/output | 5 +- .../output | 6 + .../ir_compiler_subobjects/output | 21 +- .../output | 3 + .../output | 3 + .../keccak_optimization_deploy_code/output | 7 + .../keccak_optimization_low_runs/output | 5 + test/cmdlineTests/name_simplifier/output | 13 +- .../cmdlineTests/optimizer_array_sload/output | 19 +- test/cmdlineTests/revert_strings/output | 5 +- .../output.json | 5 + .../standard_ir_requested/output.json | 5 +- .../standard_viair_requested/output.json | 9 +- test/cmdlineTests/viair_abicoder_v1/output | 5 +- test/cmdlineTests/viair_subobjects/output | 21 +- test/cmdlineTests/yul_optimizer_steps/output | 3 + .../yul_source_locations/output.json | 36 +- .../yul_source_locations_in_asm/input.json | 24 + .../yul_source_locations_in_asm/output.json | 888 ++++++++++++++++++ .../yul_string_format_ascii/output.json | 5 +- .../output.json | 5 +- .../output.json | 5 +- .../yul_string_format_ascii_long/output.json | 5 +- .../yul_string_format_hex/output.json | 5 +- test/libyul/ObjectParser.cpp | 2 +- 34 files changed, 1319 insertions(+), 109 deletions(-) create mode 100644 test/cmdlineTests/yul_source_locations_in_asm/input.json create mode 100644 test/cmdlineTests/yul_source_locations_in_asm/output.json diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index 258c867fa..ddbe4054b 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -279,8 +279,8 @@ InternalDispatchMap IRGenerator::generateInternalDispatchFunctions(ContractDefin string funName = IRNames::internalDispatch(arity); m_context.functionCollector().createFunction(funName, [&]() { Whiskers templ(R"( + function (fun, ) -> { - switch fun <#cases> case @@ -290,6 +290,7 @@ InternalDispatchMap IRGenerator::generateInternalDispatchFunctions(ContractDefin default { () } } + )"); templ("sourceLocationComment", sourceLocationComment(_contract, m_context)); templ("functionName", funName); @@ -336,14 +337,19 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function) return m_context.functionCollector().createFunction(functionName, [&]() { m_context.resetLocalVariables(); Whiskers t(R"( + function () -> { - } + )"); t("sourceLocationComment", sourceLocationComment(_function, m_context)); + t( + "contractSourceLocationComment", + sourceLocationComment(m_context.mostDerivedContract(), m_context) + ); t("functionName", functionName); vector params; @@ -398,12 +404,13 @@ string IRGenerator::generateModifier( return m_context.functionCollector().createFunction(functionName, [&]() { m_context.resetLocalVariables(); Whiskers t(R"( + function () -> { - } + )"); t("functionName", functionName); vector retParamsIn; @@ -428,6 +435,11 @@ string IRGenerator::generateModifier( ); solAssert(modifier, ""); t("sourceLocationComment", sourceLocationComment(*modifier, m_context)); + t( + "contractSourceLocationComment", + sourceLocationComment(m_context.mostDerivedContract(), m_context) + ); + switch (*_modifierInvocation.name().annotation().requiredLookup) { case VirtualLookup::Virtual: @@ -478,13 +490,18 @@ string IRGenerator::generateFunctionWithModifierInner(FunctionDefinition const& return m_context.functionCollector().createFunction(functionName, [&]() { m_context.resetLocalVariables(); Whiskers t(R"( + function () -> { - } + )"); t("sourceLocationComment", sourceLocationComment(_function, m_context)); + t( + "contractSourceLocationComment", + sourceLocationComment(m_context.mostDerivedContract(), m_context) + ); t("functionName", functionName); vector retParams; vector retParamsIn; @@ -522,12 +539,17 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) solAssert(paramTypes.empty(), ""); solUnimplementedAssert(type->sizeOnStack() == 1, ""); return Whiskers(R"( + function () -> rval { - rval := loadimmutable("") } + )") ("sourceLocationComment", sourceLocationComment(_varDecl, m_context)) + ( + "contractSourceLocationComment", + sourceLocationComment(m_context.mostDerivedContract(), m_context) + ) ("functionName", functionName) ("id", to_string(_varDecl.id())) .render(); @@ -536,12 +558,17 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) { solAssert(paramTypes.empty(), ""); return Whiskers(R"( + function () -> { - := () } + )") ("sourceLocationComment", sourceLocationComment(_varDecl, m_context)) + ( + "contractSourceLocationComment", + sourceLocationComment(m_context.mostDerivedContract(), m_context) + ) ("functionName", functionName) ("constantValueFunction", IRGeneratorForStatements(m_context, m_utils).constantValueFunction(_varDecl)) ("ret", suffixedVariableNameList("ret_", 0, _varDecl.type()->sizeOnStack())) @@ -653,16 +680,21 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) } return Whiskers(R"( + function () -> { - } + )") ("functionName", functionName) ("params", joinHumanReadable(parameters)) ("retVariables", joinHumanReadable(returnVariables)) ("code", std::move(code)) ("sourceLocationComment", sourceLocationComment(_varDecl, m_context)) + ( + "contractSourceLocationComment", + sourceLocationComment(m_context.mostDerivedContract(), m_context) + ) .render(); }); } @@ -769,6 +801,7 @@ void IRGenerator::generateConstructors(ContractDefinition const& _contract) m_context.resetLocalVariables(); m_context.functionCollector().createFunction(IRNames::constructor(*contract), [&]() { Whiskers t(R"( + function () { @@ -776,6 +809,7 @@ void IRGenerator::generateConstructors(ContractDefinition const& _contract) } + )"); vector params; if (contract->constructor()) @@ -788,6 +822,10 @@ void IRGenerator::generateConstructors(ContractDefinition const& _contract) contract->location(), m_context )); + t( + "contractSourceLocationComment", + sourceLocationComment(m_context.mostDerivedContract(), m_context) + ); t("params", joinHumanReadable(params)); vector baseParams = listAllParams(baseConstructorParams); diff --git a/libyul/AsmPrinter.cpp b/libyul/AsmPrinter.cpp index 8c5f26178..2a7b705f8 100644 --- a/libyul/AsmPrinter.cpp +++ b/libyul/AsmPrinter.cpp @@ -41,48 +41,55 @@ using namespace solidity; using namespace solidity::util; using namespace solidity::yul; -//@TODO source locations - -string AsmPrinter::operator()(Literal const& _literal) const +string AsmPrinter::operator()(Literal const& _literal) { + string const locationComment = formatSourceLocationComment(_literal); + switch (_literal.kind) { case LiteralKind::Number: yulAssert(isValidDecimal(_literal.value.str()) || isValidHex(_literal.value.str()), "Invalid number literal"); - return _literal.value.str() + appendTypeName(_literal.type); + return locationComment + _literal.value.str() + appendTypeName(_literal.type); case LiteralKind::Boolean: yulAssert(_literal.value == "true"_yulstring || _literal.value == "false"_yulstring, "Invalid bool literal."); - return ((_literal.value == "true"_yulstring) ? "true" : "false") + appendTypeName(_literal.type, true); + return locationComment + ((_literal.value == "true"_yulstring) ? "true" : "false") + appendTypeName(_literal.type, true); case LiteralKind::String: break; } - return escapeAndQuoteString(_literal.value.str()) + appendTypeName(_literal.type); + return locationComment + escapeAndQuoteString(_literal.value.str()) + appendTypeName(_literal.type); } -string AsmPrinter::operator()(Identifier const& _identifier) const +string AsmPrinter::operator()(Identifier const& _identifier) { yulAssert(!_identifier.name.empty(), "Invalid identifier."); - return _identifier.name.str(); + return formatSourceLocationComment(_identifier) + _identifier.name.str(); } -string AsmPrinter::operator()(ExpressionStatement const& _statement) const +string AsmPrinter::operator()(ExpressionStatement const& _statement) { - return std::visit(*this, _statement.expression); + string const locationComment = formatSourceLocationComment(_statement); + + return locationComment + std::visit(*this, _statement.expression); } -string AsmPrinter::operator()(Assignment const& _assignment) const +string AsmPrinter::operator()(Assignment const& _assignment) { + string const locationComment = formatSourceLocationComment(_assignment); + yulAssert(_assignment.variableNames.size() >= 1, ""); string variables = (*this)(_assignment.variableNames.front()); for (size_t i = 1; i < _assignment.variableNames.size(); ++i) variables += ", " + (*this)(_assignment.variableNames[i]); - return variables + " := " + std::visit(*this, *_assignment.value); + + return locationComment + variables + " := " + std::visit(*this, *_assignment.value); } -string AsmPrinter::operator()(VariableDeclaration const& _variableDeclaration) const +string AsmPrinter::operator()(VariableDeclaration const& _variableDeclaration) { - string out = "let "; + string out = formatSourceLocationComment(_variableDeclaration); + + out += "let "; out += boost::algorithm::join( _variableDeclaration.variables | ranges::views::transform( [this](TypedName argument) { return formatTypedName(argument); } @@ -97,10 +104,12 @@ string AsmPrinter::operator()(VariableDeclaration const& _variableDeclaration) c return out; } -string AsmPrinter::operator()(FunctionDefinition const& _functionDefinition) const +string AsmPrinter::operator()(FunctionDefinition const& _functionDefinition) { yulAssert(!_functionDefinition.name.empty(), "Invalid function name."); - string out = "function " + _functionDefinition.name.str() + "("; + + string out = formatSourceLocationComment(_functionDefinition); + out += "function " + _functionDefinition.name.str() + "("; out += boost::algorithm::join( _functionDefinition.parameters | ranges::views::transform( [this](TypedName argument) { return formatTypedName(argument); } @@ -122,30 +131,41 @@ string AsmPrinter::operator()(FunctionDefinition const& _functionDefinition) con return out + "\n" + (*this)(_functionDefinition.body); } -string AsmPrinter::operator()(FunctionCall const& _functionCall) const +string AsmPrinter::operator()(FunctionCall const& _functionCall) { + string const locationComment = formatSourceLocationComment(_functionCall); + string const functionName = (*this)(_functionCall.functionName); return - (*this)(_functionCall.functionName) + "(" + + locationComment + + functionName + "(" + boost::algorithm::join( _functionCall.arguments | ranges::views::transform([&](auto&& _node) { return std::visit(*this, _node); }), ", " ) + ")"; } -string AsmPrinter::operator()(If const& _if) const +string AsmPrinter::operator()(If const& _if) { yulAssert(_if.condition, "Invalid if condition."); + + string out = formatSourceLocationComment(_if); + out += "if " + std::visit(*this, *_if.condition); + string body = (*this)(_if.body); char delim = '\n'; if (body.find('\n') == string::npos) delim = ' '; - return "if " + std::visit(*this, *_if.condition) + delim + (*this)(_if.body); + + return out + delim + body; } -string AsmPrinter::operator()(Switch const& _switch) const +string AsmPrinter::operator()(Switch const& _switch) { yulAssert(_switch.expression, "Invalid expression pointer."); - string out = "switch " + std::visit(*this, *_switch.expression); + + string out = formatSourceLocationComment(_switch); + out += "switch " + std::visit(*this, *_switch.expression); + for (auto const& _case: _switch.cases) { if (!_case.value) @@ -157,12 +177,15 @@ string AsmPrinter::operator()(Switch const& _switch) const return out; } -string AsmPrinter::operator()(ForLoop const& _forLoop) const +string AsmPrinter::operator()(ForLoop const& _forLoop) { yulAssert(_forLoop.condition, "Invalid for loop condition."); + string const locationComment = formatSourceLocationComment(_forLoop); + string pre = (*this)(_forLoop.pre); string condition = std::visit(*this, *_forLoop.condition); string post = (*this)(_forLoop.post); + char delim = '\n'; if ( pre.size() + condition.size() + post.size() < 60 && @@ -171,46 +194,50 @@ string AsmPrinter::operator()(ForLoop const& _forLoop) const ) delim = ' '; return + locationComment + ("for " + move(pre) + delim + move(condition) + delim + move(post) + "\n") + (*this)(_forLoop.body); } -string AsmPrinter::operator()(Break const&) const +string AsmPrinter::operator()(Break const& _break) { - return "break"; + return formatSourceLocationComment(_break) + "break"; } -string AsmPrinter::operator()(Continue const&) const +string AsmPrinter::operator()(Continue const& _continue) { - return "continue"; + return formatSourceLocationComment(_continue) + "continue"; } -string AsmPrinter::operator()(Leave const&) const +// '_leave' and '__leave' is reserved in VisualStudio +string AsmPrinter::operator()(Leave const& leave_) { - return "leave"; + return formatSourceLocationComment(leave_) + "leave"; } -string AsmPrinter::operator()(Block const& _block) const +string AsmPrinter::operator()(Block const& _block) { + string const locationComment = formatSourceLocationComment(_block); + if (_block.statements.empty()) - return "{ }"; + return locationComment + "{ }"; string body = boost::algorithm::join( _block.statements | ranges::views::transform([&](auto&& _node) { return std::visit(*this, _node); }), "\n" ); if (body.size() < 30 && body.find('\n') == string::npos) - return "{ " + body + " }"; + return locationComment + "{ " + body + " }"; else { boost::replace_all(body, "\n", "\n "); - return "{\n " + body + "\n}"; + return locationComment + "{\n " + body + "\n}"; } } -string AsmPrinter::formatTypedName(TypedName _variable) const +string AsmPrinter::formatTypedName(TypedName _variable) { yulAssert(!_variable.name.empty(), "Invalid variable name."); - return _variable.name.str() + appendTypeName(_variable.type); + return formatSourceLocationComment(_variable) + _variable.name.str() + appendTypeName(_variable.type); } string AsmPrinter::appendTypeName(YulString _type, bool _isBoolLiteral) const @@ -228,3 +255,31 @@ string AsmPrinter::appendTypeName(YulString _type, bool _isBoolLiteral) const else return ":" + _type.str(); } + +string AsmPrinter::formatSourceLocationComment(shared_ptr const& _debugData, bool _statement) +{ + if ( + !_debugData || + m_lastLocation == _debugData->location || + m_nameToSourceIndex.empty() + ) + return ""; + + m_lastLocation = _debugData->location; + + string sourceIndex = "-1"; + if (_debugData->location.sourceName) + sourceIndex = to_string(m_nameToSourceIndex.at(*_debugData->location.sourceName)); + + string sourceLocation = + "@src " + + sourceIndex + + ":" + + to_string(_debugData->location.start) + + ":" + + to_string(_debugData->location.end); + return + _statement ? + "/// " + sourceLocation + "\n" : + "/** " + sourceLocation + " */ "; +} diff --git a/libyul/AsmPrinter.h b/libyul/AsmPrinter.h index 63b1bd3a2..940f663b4 100644 --- a/libyul/AsmPrinter.h +++ b/libyul/AsmPrinter.h @@ -24,9 +24,14 @@ #pragma once #include - #include +#include + +#include + +#include + namespace solidity::yul { struct Dialect; @@ -39,29 +44,52 @@ struct Dialect; class AsmPrinter { public: - AsmPrinter() {} - explicit AsmPrinter(Dialect const& _dialect): m_dialect(&_dialect) {} + explicit AsmPrinter( + Dialect const* _dialect = nullptr, + std::optional>> _sourceIndexToName = {} + ): + m_dialect(_dialect) + { + if (_sourceIndexToName) + for (auto&& [index, name]: *_sourceIndexToName) + m_nameToSourceIndex[*name] = index; + } - std::string operator()(Literal const& _literal) const; - std::string operator()(Identifier const& _identifier) const; - std::string operator()(ExpressionStatement const& _expr) const; - std::string operator()(Assignment const& _assignment) const; - std::string operator()(VariableDeclaration const& _variableDeclaration) const; - std::string operator()(FunctionDefinition const& _functionDefinition) const; - std::string operator()(FunctionCall const& _functionCall) const; - std::string operator()(If const& _if) const; - std::string operator()(Switch const& _switch) const; - std::string operator()(ForLoop const& _forLoop) const; - std::string operator()(Break const& _break) const; - std::string operator()(Continue const& _continue) const; - std::string operator()(Leave const& _continue) const; - std::string operator()(Block const& _block) const; + + explicit AsmPrinter( + Dialect const& _dialect, + std::optional>> _sourceIndexToName = {} + ): AsmPrinter(&_dialect, _sourceIndexToName) {} + + std::string operator()(Literal const& _literal); + std::string operator()(Identifier const& _identifier); + std::string operator()(ExpressionStatement const& _expr); + std::string operator()(Assignment const& _assignment); + std::string operator()(VariableDeclaration const& _variableDeclaration); + std::string operator()(FunctionDefinition const& _functionDefinition); + std::string operator()(FunctionCall const& _functionCall); + std::string operator()(If const& _if); + std::string operator()(Switch const& _switch); + std::string operator()(ForLoop const& _forLoop); + std::string operator()(Break const& _break); + std::string operator()(Continue const& _continue); + std::string operator()(Leave const& _continue); + std::string operator()(Block const& _block); private: - std::string formatTypedName(TypedName _variable) const; + std::string formatTypedName(TypedName _variable); std::string appendTypeName(YulString _type, bool _isBoolLiteral = false) const; + std::string formatSourceLocationComment(std::shared_ptr const& _debugData, bool _statement); + template + std::string formatSourceLocationComment(T const& _node) + { + bool isExpression = std::is_constructible::value; + return formatSourceLocationComment(_node.debugData, !isExpression); + } - Dialect const* m_dialect = nullptr; + Dialect const* const m_dialect = nullptr; + std::map m_nameToSourceIndex; + langutil::SourceLocation m_lastLocation = {}; }; } diff --git a/libyul/Object.cpp b/libyul/Object.cpp index 3dadf9cb5..05ad9cb82 100644 --- a/libyul/Object.cpp +++ b/libyul/Object.cpp @@ -25,11 +25,14 @@ #include #include +#include #include #include #include +#include + using namespace std; using namespace solidity; using namespace solidity::yul; @@ -47,18 +50,36 @@ string indent(std::string const& _input) } -string Data::toString(Dialect const*) const +string Data::toString(Dialect const*, optional) const { return "data \"" + name.str() + "\" hex\"" + util::toHex(data) + "\""; } string Object::toString(Dialect const* _dialect) const +{ + string useSrcComment; + + if (debugData && debugData->sourceNames) + useSrcComment = + "/// @use-src " + + joinHumanReadable(ranges::views::transform(*debugData->sourceNames, [](auto&& _pair) { + return to_string(_pair.first) + ":" + util::escapeAndQuoteString(*_pair.second); + })) + + "\n"; + return useSrcComment + toString(_dialect, debugData ? debugData->sourceNames : optional{}); +} + +string Object::toString(Dialect const* _dialect, std::optional _sourceNames) const { yulAssert(code, "No code"); - string inner = "code " + (_dialect ? AsmPrinter{*_dialect} : AsmPrinter{})(*code); + string inner = "code " + AsmPrinter{_dialect, _sourceNames}(*code); for (auto const& obj: subObjects) - inner += "\n" + obj->toString(_dialect); + { + if (auto const* o = dynamic_cast(obj.get())) + yulAssert(!o->debugData || !o->debugData->sourceNames, ""); + inner += "\n" + obj->toString(_dialect, _sourceNames); + } return "object \"" + name.str() + "\" {\n" + indent(inner) + "\n}"; } diff --git a/libyul/Object.h b/libyul/Object.h index 81406a1df..9b7519b1e 100644 --- a/libyul/Object.h +++ b/libyul/Object.h @@ -35,39 +35,55 @@ struct Dialect; struct AsmAnalysisInfo; +using SourceNameMap = std::map>; + +struct Object; + /** * Generic base class for both Yul objects and Yul data. */ struct ObjectNode { virtual ~ObjectNode() = default; - virtual std::string toString(Dialect const* _dialect) const = 0; - std::string toString() { return toString(nullptr); } /// Name of the object. /// Can be empty since .yul files can also just contain code, without explicitly placing it in an object. YulString name; +protected: + virtual std::string toString(Dialect const* _dialect, std::optional _sourceNames) const = 0; + + /// Object should have access to toString + friend struct Object; }; /** * Named data in Yul objects. */ -struct Data: ObjectNode +struct Data: public ObjectNode { Data(YulString _name, bytes _data): data(std::move(_data)) { name = _name; } - std::string toString(Dialect const* _dialect) const override; bytes data; + +protected: + std::string toString(Dialect const* _dialect, std::optional _sourceNames) const override; }; + +struct ObjectDebugData +{ + std::optional sourceNames = {}; +}; + + /** * Yul code and data object container. */ -struct Object: ObjectNode +struct Object: public ObjectNode { public: - /// @returns a (parseable) string representation. Includes types if @a _yul is set. - std::string toString(Dialect const* _dialect) const override; + /// @returns a (parseable) string representation. + std::string toString(Dialect const* _dialect) const; /// @returns the set of names of data objects accessible from within the code of /// this object, including the name of object itself @@ -94,8 +110,12 @@ public: std::map subIndexByName; std::shared_ptr analysisInfo; + std::shared_ptr debugData; + /// @returns the name of the special metadata data object. static std::string metadataName() { return ".metadata"; } +protected: + std::string toString(Dialect const* _dialect, std::optional _sourceNames) const override; }; } diff --git a/libyul/ObjectParser.cpp b/libyul/ObjectParser.cpp index ab03e81a1..9a38c7b60 100644 --- a/libyul/ObjectParser.cpp +++ b/libyul/ObjectParser.cpp @@ -58,8 +58,9 @@ shared_ptr ObjectParser::parse(shared_ptr const& _scanner, bool } else object = parseObject(); - if (object && !_reuseScanner) + if (!_reuseScanner) expectToken(Token::EOS); + object->debugData = make_shared(ObjectDebugData{m_sourceNameMapping}); return object; } catch (FatalError const&) @@ -111,7 +112,7 @@ shared_ptr ObjectParser::parseCode() return parseBlock(); } -optional ObjectParser::tryParseSourceNameMapping() const +optional ObjectParser::tryParseSourceNameMapping() const { // @use-src 0:"abc.sol", 1:"foo.sol", 2:"bar.sol" // diff --git a/libyul/ObjectParser.h b/libyul/ObjectParser.h index 58b3a851c..86a5ef77c 100644 --- a/libyul/ObjectParser.h +++ b/libyul/ObjectParser.h @@ -55,7 +55,6 @@ public: /// @returns an empty shared pointer on error. std::shared_ptr parse(std::shared_ptr const& _scanner, bool _reuseScanner); - using SourceNameMap = std::map>; std::optional const& sourceNameMapping() const noexcept { return m_sourceNameMapping; } private: diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index eefa8c8f2..37a538328 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -165,6 +165,7 @@ EOF # Replace escaped newlines by actual newlines for readability # shellcheck disable=SC1003 sed -i.bak -E -e 's/\\n/\'$'\n/g' "$stdout_path" + sed -i.bak -e 's/\(^[ ]*auxdata: \)0x[0-9a-f]*$/\1/' "$stdout_path" rm "$stdout_path.bak" else sed -i.bak -e '/^Warning: This is a pre-release compiler version, please do not use it in production./d' "$stderr_path" diff --git a/test/cmdlineTests/constant_optimizer_yul/output b/test/cmdlineTests/constant_optimizer_yul/output index 9ef151e3b..7fdb98670 100644 --- a/test/cmdlineTests/constant_optimizer_yul/output +++ b/test/cmdlineTests/constant_optimizer_yul/output @@ -6,12 +6,16 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"constant_optimizer_yul/input.sol", 1:"#utility.yul" object "C_12" { code { { + /// @src 0:61:418 mstore(64, 128) if callvalue() { revert(0, 0) } + /// @src 0:103:238 sstore(0, shl(180, 1)) + /// @src 0:61:418 let _1 := datasize("C_12_deployed") codecopy(128, dataoffset("C_12_deployed"), _1) return(128, _1) @@ -20,9 +24,12 @@ object "C_12" { object "C_12_deployed" { code { { + /// @src 0:61:418 mstore(64, 128) if callvalue() { revert(0, 0) } + /// @src 0:279:410 sstore(0, 0x1000000000000000000000000000000000000000000000) + /// @src 0:61:418 stop() } } diff --git a/test/cmdlineTests/exp_base_literal/output b/test/cmdlineTests/exp_base_literal/output index 2eb1998e7..b7aaea4ba 100644 --- a/test/cmdlineTests/exp_base_literal/output +++ b/test/cmdlineTests/exp_base_literal/output @@ -25,11 +25,13 @@ object "C_81" { memPtr := mload(64) } + /// @src 0:82:370 function constructor_C_81() { /// @src 0:82:370 } + /// @src 0:82:370 function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) @@ -212,8 +214,8 @@ object "C_81" { converted := cleanup_t_int256(value) } + /// @src 0:96:368 function fun_f_80(var_a_4, var_b_6, var_c_8, var_d_10) -> var__13, var__15, var__17, var__19 { - /// @src 0:96:368 /// @src 0:160:164 let zero_t_uint256_1 := zero_value_for_split_t_uint256() var__13 := zero_t_uint256_1 @@ -336,6 +338,7 @@ object "C_81" { leave } + /// @src 0:82:370 function panic_error_0x11() { mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) diff --git a/test/cmdlineTests/ir_compiler_inheritance_nosubobjects/output b/test/cmdlineTests/ir_compiler_inheritance_nosubobjects/output index ad951774e..1af925406 100644 --- a/test/cmdlineTests/ir_compiler_inheritance_nosubobjects/output +++ b/test/cmdlineTests/ir_compiler_inheritance_nosubobjects/output @@ -6,9 +6,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"ir_compiler_inheritance_nosubobjects/input.sol", 1:"#utility.yul" object "C_7" { code { { + /// @src 0:82:117 mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_7_deployed") @@ -19,6 +21,7 @@ object "C_7" { object "C_7_deployed" { code { { + /// @src 0:82:117 mstore(64, 128) revert(0, 0) } @@ -35,9 +38,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"ir_compiler_inheritance_nosubobjects/input.sol", 1:"#utility.yul" object "D_10" { code { { + /// @src 0:118:137 mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("D_10_deployed") @@ -48,6 +53,7 @@ object "D_10" { object "D_10_deployed" { code { { + /// @src 0:118:137 mstore(64, 128) revert(0, 0) } diff --git a/test/cmdlineTests/ir_compiler_subobjects/output b/test/cmdlineTests/ir_compiler_subobjects/output index c10198bbb..ba93e6886 100644 --- a/test/cmdlineTests/ir_compiler_subobjects/output +++ b/test/cmdlineTests/ir_compiler_subobjects/output @@ -6,9 +6,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"ir_compiler_subobjects/input.sol", 1:"#utility.yul" object "C_3" { code { { + /// @src 0:82:95 mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_3_deployed") @@ -19,6 +21,7 @@ object "C_3" { object "C_3_deployed" { code { { + /// @src 0:82:95 mstore(64, 128) revert(0, 0) } @@ -35,9 +38,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"ir_compiler_subobjects/input.sol", 1:"#utility.yul" object "D_16" { code { { + /// @src 0:96:165 mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("D_16_deployed") @@ -48,6 +53,7 @@ object "D_16" { object "D_16_deployed" { code { { + /// @src 0:96:165 mstore(64, 128) if iszero(lt(calldatasize(), 4)) { @@ -56,17 +62,22 @@ object "D_16" { { if callvalue() { revert(_1, _1) } if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) } + /// @src 0:149:156 let _2 := datasize("C_3") - let _3 := add(128, _2) - if or(gt(_3, 0xffffffffffffffff), lt(_3, 128)) + let _3 := add(/** @src 0:96:165 */ 128, /** @src 0:149:156 */ _2) + if or(gt(_3, 0xffffffffffffffff), lt(_3, /** @src 0:96:165 */ 128)) + /// @src 0:149:156 { + /// @src 0:96:165 mstore(_1, shl(224, 0x4e487b71)) mstore(4, 0x41) revert(_1, 0x24) } - datacopy(128, dataoffset("C_3"), _2) - if iszero(create(_1, 128, _2)) + /// @src 0:149:156 + datacopy(/** @src 0:96:165 */ 128, /** @src 0:149:156 */ dataoffset("C_3"), _2) + if iszero(create(/** @src 0:96:165 */ _1, 128, /** @src 0:149:156 */ _2)) { + /// @src 0:96:165 let pos := mload(64) returndatacopy(pos, _1, returndatasize()) revert(pos, returndatasize()) @@ -80,6 +91,7 @@ object "D_16" { object "C_3" { code { { + /// @src 0:82:95 mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_3_deployed") @@ -90,6 +102,7 @@ object "D_16" { object "C_3_deployed" { code { { + /// @src 0:82:95 mstore(64, 128) revert(0, 0) } diff --git a/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/output b/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/output index 4efea77ce..89ee1b5d9 100644 --- a/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/output +++ b/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/output @@ -6,9 +6,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"ir_with_assembly_no_memoryguard_creation/input.sol", 1:"#utility.yul" object "D_12" { code { { + /// @src 0:82:161 mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("D_12_deployed") @@ -19,6 +21,7 @@ object "D_12" { object "D_12_deployed" { code { { + /// @src 0:82:161 mstore(64, 128) if iszero(lt(calldatasize(), 4)) { diff --git a/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/output b/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/output index 9b39c93e6..1fcf2fcc8 100644 --- a/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/output +++ b/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/output @@ -6,9 +6,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"ir_with_assembly_no_memoryguard_runtime/input.sol", 1:"#utility.yul" object "D_8" { code { { + /// @src 0:82:153 mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("D_8_deployed") @@ -19,6 +21,7 @@ object "D_8" { object "D_8_deployed" { code { { + /// @src 0:82:153 mstore(64, 128) if iszero(lt(calldatasize(), 4)) { diff --git a/test/cmdlineTests/keccak_optimization_deploy_code/output b/test/cmdlineTests/keccak_optimization_deploy_code/output index 907b08b70..83c7c3929 100644 --- a/test/cmdlineTests/keccak_optimization_deploy_code/output +++ b/test/cmdlineTests/keccak_optimization_deploy_code/output @@ -6,13 +6,17 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"keccak_optimization_deploy_code/input.sol", 1:"#utility.yul" object "C_12" { code { { + /// @src 0:62:463 mstore(64, 128) if callvalue() { revert(0, 0) } + /// @src 0:103:275 mstore(0, 100) sstore(0, keccak256(0, 32)) + /// @src 0:62:463 let _1 := datasize("C_12_deployed") codecopy(128, dataoffset("C_12_deployed"), _1) return(128, _1) @@ -21,10 +25,13 @@ object "C_12" { object "C_12_deployed" { code { { + /// @src 0:62:463 mstore(64, 128) if callvalue() { revert(0, 0) } + /// @src 0:317:454 mstore(0, 100) sstore(0, 17385872270140913825666367956517731270094621555228275961425792378517567244498) + /// @src 0:62:463 stop() } } diff --git a/test/cmdlineTests/keccak_optimization_low_runs/output b/test/cmdlineTests/keccak_optimization_low_runs/output index 99af8c821..203600cfa 100644 --- a/test/cmdlineTests/keccak_optimization_low_runs/output +++ b/test/cmdlineTests/keccak_optimization_low_runs/output @@ -6,9 +6,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"keccak_optimization_low_runs/input.sol", 1:"#utility.yul" object "C_7" { code { { + /// @src 0:62:285 mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_7_deployed") @@ -19,10 +21,13 @@ object "C_7" { object "C_7_deployed" { code { { + /// @src 0:62:285 mstore(64, 128) if callvalue() { revert(0, 0) } + /// @src 0:109:277 mstore(0, 100) sstore(0, keccak256(0, 32)) + /// @src 0:62:285 stop() } } diff --git a/test/cmdlineTests/name_simplifier/output b/test/cmdlineTests/name_simplifier/output index d8e7d4f7c..7d0fba09f 100644 --- a/test/cmdlineTests/name_simplifier/output +++ b/test/cmdlineTests/name_simplifier/output @@ -6,9 +6,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"name_simplifier/input.sol", 1:"#utility.yul" object "C_59" { code { { + /// @src 0:346:625 mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_59_deployed") @@ -19,6 +21,7 @@ object "C_59" { object "C_59_deployed" { code { { + /// @src 0:346:625 mstore(64, 128) if iszero(lt(calldatasize(), 4)) { @@ -90,14 +93,18 @@ object "C_59" { if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } mstore(64, newFreePtr) } + /// @src 0:381:623 function fun_sumArray(var_s_mpos) -> var, var_mpos { + /// @src 0:346:625 if iszero(mload(var_s_mpos)) { panic_error_0x32() } - sstore(0x00, mload(mload(add(var_s_mpos, 32)))) + sstore(/** @src 0:472:473 */ 0x00, /** @src 0:346:625 */ mload(/** @src 0:469:474 */ mload(/** @src 0:346:625 */ add(var_s_mpos, 32)))) if iszero(lt(1, mload(var_s_mpos))) { panic_error_0x32() } - let _1 := mload(mload(add(var_s_mpos, 64))) + let _1 := mload(/** @src 0:489:494 */ mload(/** @src 0:346:625 */ add(var_s_mpos, 64))) sstore(0x02, _1) + /// @src 0:500:619 var := _1 + /// @src 0:346:625 let memPtr := mload(64) let newFreePtr := add(memPtr, 160) if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } @@ -107,8 +114,10 @@ object "C_59" { mstore(add(memPtr, 64), "ngstringlongstringlongstringlong") mstore(add(memPtr, 96), "stringlongstringlongstringlongst") mstore(add(memPtr, 128), "ring") + /// @src 0:500:619 var_mpos := memPtr } + /// @src 0:346:625 function panic_error_0x32() { mstore(0, shl(224, 0x4e487b71)) diff --git a/test/cmdlineTests/optimizer_array_sload/output b/test/cmdlineTests/optimizer_array_sload/output index c80e9df87..36dc23663 100644 --- a/test/cmdlineTests/optimizer_array_sload/output +++ b/test/cmdlineTests/optimizer_array_sload/output @@ -6,9 +6,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"optimizer_array_sload/input.sol", 1:"#utility.yul" object "Arraysum_34" { code { { + /// @src 0:80:429 mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("Arraysum_34_deployed") @@ -19,6 +21,7 @@ object "Arraysum_34" { object "Arraysum_34_deployed" { code { { + /// @src 0:80:429 mstore(64, 128) if iszero(lt(calldatasize(), 4)) { @@ -28,19 +31,27 @@ object "Arraysum_34" { if callvalue() { revert(_1, _1) } if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) } let var_sum := _1 - let var_i := _1 + /// @src 0:368:378 + let var_i := /** @src 0:80:429 */ _1 let _2 := sload(_1) + /// @src 0:364:423 for { } - lt(var_i, _2) + /** @src 0:380:397 */ lt(var_i, _2) + /// @src 0:368:378 { + /// @src 0:80:429 if eq(var_i, not(0)) { panic_error_0x11() } - var_i := add(var_i, 1) + /// @src 0:399:402 + var_i := /** @src 0:80:429 */ add(var_i, 1) } + /// @src 0:399:402 { + /// @src 0:80:429 mstore(_1, _1) let _3 := sload(add(18569430475105882587588266137607568536673111973893317399460219858819262702947, var_i)) if gt(var_sum, not(_3)) { panic_error_0x11() } - var_sum := add(var_sum, _3) + /// @src 0:407:423 + var_sum := /** @src 0:80:429 */ add(var_sum, _3) } let memPos := mload(64) return(memPos, sub(abi_encode_uint256(memPos, var_sum), memPos)) diff --git a/test/cmdlineTests/revert_strings/output b/test/cmdlineTests/revert_strings/output index c572ef6c2..6fa650eb1 100644 --- a/test/cmdlineTests/revert_strings/output +++ b/test/cmdlineTests/revert_strings/output @@ -25,11 +25,13 @@ object "C_15" { memPtr := mload(64) } + /// @src 0:59:147 function constructor_C_15() { /// @src 0:59:147 } + /// @src 0:59:147 function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { @@ -217,10 +219,11 @@ object "C_15" { mstore(64, newFreePtr) } + /// @src 0:93:145 function fun_f_14(var__7_mpos, var_e_10) { - /// @src 0:93:145 } + /// @src 0:59:147 function panic_error_0x41() { mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) diff --git a/test/cmdlineTests/standard_irOptimized_requested/output.json b/test/cmdlineTests/standard_irOptimized_requested/output.json index 73e9653d2..8b9ada71a 100644 --- a/test/cmdlineTests/standard_irOptimized_requested/output.json +++ b/test/cmdlineTests/standard_irOptimized_requested/output.json @@ -5,8 +5,10 @@ * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:\"A\", 1:\"#utility.yul\" object \"C_7\" { code { + /// @src 0:79:121 mstore(64, 128) if callvalue() { @@ -25,6 +27,7 @@ object \"C_7\" { } object \"C_7_deployed\" { code { + /// @src 0:79:121 mstore(64, 128) if iszero(lt(calldatasize(), 4)) { @@ -56,8 +59,10 @@ object \"C_7\" { { tail := add(headStart, 0) } function allocate_unbounded() -> memPtr { memPtr := mload(64) } + /// @src 0:92:119 function fun_f_6() { } + /// @src 0:79:121 function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { revert(0, 0) } function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() diff --git a/test/cmdlineTests/standard_ir_requested/output.json b/test/cmdlineTests/standard_ir_requested/output.json index 90f54bb21..2eb815d33 100644 --- a/test/cmdlineTests/standard_ir_requested/output.json +++ b/test/cmdlineTests/standard_ir_requested/output.json @@ -24,11 +24,13 @@ object \"C_7\" { memPtr := mload(64) } + /// @src 0:79:121 function constructor_C_7() { /// @src 0:79:121 } + /// @src 0:79:121 function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) @@ -76,10 +78,11 @@ object \"C_7\" { memPtr := mload(64) } + /// @src 0:92:119 function fun_f_6() { - /// @src 0:92:119 } + /// @src 0:79:121 function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { revert(0, 0) diff --git a/test/cmdlineTests/standard_viair_requested/output.json b/test/cmdlineTests/standard_viair_requested/output.json index e159ebe96..dca052187 100644 --- a/test/cmdlineTests/standard_viair_requested/output.json +++ b/test/cmdlineTests/standard_viair_requested/output.json @@ -24,11 +24,13 @@ object \"C_3\" { memPtr := mload(64) } + /// @src 0:79:92 function constructor_C_3() { /// @src 0:79:92 } + /// @src 0:79:92 function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) @@ -98,11 +100,13 @@ object \"D_16\" { memPtr := mload(64) } + /// @src 0:93:146 function constructor_D_16() { /// @src 0:93:146 } + /// @src 0:93:146 function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) @@ -150,8 +154,8 @@ object \"D_16\" { memPtr := mload(64) } + /// @src 0:106:144 function fun_f_15() { - /// @src 0:106:144 /// @src 0:134:141 let _1 := allocate_unbounded() @@ -168,6 +172,7 @@ object \"D_16\" { let var_c_8_address := expr_12_address } + /// @src 0:93:146 function panic_error_0x41() { mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) @@ -226,11 +231,13 @@ object \"D_16\" { memPtr := mload(64) } + /// @src 0:79:92 function constructor_C_3() { /// @src 0:79:92 } + /// @src 0:79:92 function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) diff --git a/test/cmdlineTests/viair_abicoder_v1/output b/test/cmdlineTests/viair_abicoder_v1/output index 17d53335c..d42a032f8 100644 --- a/test/cmdlineTests/viair_abicoder_v1/output +++ b/test/cmdlineTests/viair_abicoder_v1/output @@ -25,11 +25,13 @@ object "test_11" { memPtr := mload(64) } + /// @src 0:79:169 function constructor_test_11() { /// @src 0:79:169 } + /// @src 0:79:169 function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) @@ -87,8 +89,8 @@ object "test_11" { cleaned := iszero(iszero(value)) } + /// @src 0:99:167 function fun_f_10() -> var__5 { - /// @src 0:99:167 /// @src 0:133:137 let zero_t_bool_1 := zero_value_for_split_t_bool() var__5 := zero_t_bool_1 @@ -100,6 +102,7 @@ object "test_11" { leave } + /// @src 0:79:169 function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { revert(0, 0) diff --git a/test/cmdlineTests/viair_subobjects/output b/test/cmdlineTests/viair_subobjects/output index cf52c5e97..9ca620d06 100644 --- a/test/cmdlineTests/viair_subobjects/output +++ b/test/cmdlineTests/viair_subobjects/output @@ -12,9 +12,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"viair_subobjects/input.sol", 1:"#utility.yul" object "C_3" { code { { + /// @src 0:82:95 mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_3_deployed") @@ -25,6 +27,7 @@ object "C_3" { object "C_3_deployed" { code { { + /// @src 0:82:95 mstore(64, 128) revert(0, 0) } @@ -47,9 +50,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"viair_subobjects/input.sol", 1:"#utility.yul" object "D_16" { code { { + /// @src 0:96:165 mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("D_16_deployed") @@ -60,6 +65,7 @@ object "D_16" { object "D_16_deployed" { code { { + /// @src 0:96:165 mstore(64, 128) if iszero(lt(calldatasize(), 4)) { @@ -68,17 +74,22 @@ object "D_16" { { if callvalue() { revert(_1, _1) } if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) } + /// @src 0:149:156 let _2 := datasize("C_3") - let _3 := add(128, _2) - if or(gt(_3, 0xffffffffffffffff), lt(_3, 128)) + let _3 := add(/** @src 0:96:165 */ 128, /** @src 0:149:156 */ _2) + if or(gt(_3, 0xffffffffffffffff), lt(_3, /** @src 0:96:165 */ 128)) + /// @src 0:149:156 { + /// @src 0:96:165 mstore(_1, shl(224, 0x4e487b71)) mstore(4, 0x41) revert(_1, 0x24) } - datacopy(128, dataoffset("C_3"), _2) - if iszero(create(_1, 128, _2)) + /// @src 0:149:156 + datacopy(/** @src 0:96:165 */ 128, /** @src 0:149:156 */ dataoffset("C_3"), _2) + if iszero(create(/** @src 0:96:165 */ _1, 128, /** @src 0:149:156 */ _2)) { + /// @src 0:96:165 let pos := mload(64) returndatacopy(pos, _1, returndatasize()) revert(pos, returndatasize()) @@ -92,6 +103,7 @@ object "D_16" { object "C_3" { code { { + /// @src 0:82:95 mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_3_deployed") @@ -102,6 +114,7 @@ object "D_16" { object "C_3_deployed" { code { { + /// @src 0:82:95 mstore(64, 128) revert(0, 0) } diff --git a/test/cmdlineTests/yul_optimizer_steps/output b/test/cmdlineTests/yul_optimizer_steps/output index 6f42b4761..af9ef0988 100644 --- a/test/cmdlineTests/yul_optimizer_steps/output +++ b/test/cmdlineTests/yul_optimizer_steps/output @@ -6,9 +6,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"yul_optimizer_steps/input.sol", 1:"#utility.yul" object "C_7" { code { { + /// @src 0:80:112 mstore(64, 128) if callvalue() { @@ -26,6 +28,7 @@ object "C_7" { object "C_7_deployed" { code { { + /// @src 0:80:112 mstore(64, 128) if iszero(lt(calldatasize(), 4)) { diff --git a/test/cmdlineTests/yul_source_locations/output.json b/test/cmdlineTests/yul_source_locations/output.json index b1ddda7a2..f5f57a6d6 100644 --- a/test/cmdlineTests/yul_source_locations/output.json +++ b/test/cmdlineTests/yul_source_locations/output.json @@ -53,6 +53,7 @@ object \"C_54\" { cleaned := value } + /// @src 0:175:223 function constructor_C_54(var__init_12) { /// @src 0:175:223 @@ -70,6 +71,7 @@ object \"C_54\" { let expr_17 := expr_16 } + /// @src 0:79:428 function convert_t_int256_to_t_int256(value) -> converted { converted := cleanup_t_int256(value) @@ -303,17 +305,18 @@ object \"C_54\" { mstore(64, newFreePtr) } + /// @src 0:343:426 function fun_f2_53() -> var__42 { - /// @src 0:343:426 /// @src 0:375:378 let zero_t_int256_4 := zero_value_for_split_t_int256() var__42 := zero_t_int256_4 var__42 := modifier_m_40(var__42) } + /// @src 0:79:428 + /// @src 0:343:426 function fun_f2_53_inner(_8) -> var__42 { - /// @src 0:343:426 var__42 := _8 /// @src 0:392:400 @@ -359,9 +362,10 @@ object \"C_54\" { leave } + /// @src 0:79:428 + /// @src 0:226:302 function fun_f_30() -> var__23 { - /// @src 0:226:302 /// @src 0:262:265 let zero_t_int256_1 := zero_value_for_split_t_int256() var__23 := zero_t_int256_1 @@ -379,9 +383,10 @@ object \"C_54\" { leave } + /// @src 0:79:428 + /// @src 0:152:171 function getter_fun_stateVar_10() -> ret { - /// @src 0:152:171 let slot := 0 let offset := 0 @@ -389,6 +394,7 @@ object \"C_54\" { ret := read_from_storage_split_dynamic_t_int256(slot, offset) } + /// @src 0:79:428 function increment_t_int256(value) -> ret { value := cleanup_t_int256(value) @@ -396,8 +402,8 @@ object \"C_54\" { ret := add(value, 1) } + /// @src 0:304:341 function modifier_m_40(var__42) -> _5 { - /// @src 0:304:341 _5 := var__42 /// @src 0:322:332 @@ -409,6 +415,7 @@ object \"C_54\" { _5 := fun_f2_53_inner(var__42) } + /// @src 0:79:428 function panic_error_0x11() { mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) @@ -599,6 +606,7 @@ object \"D_72\" { cleaned := value } + /// @src 0:175:223 function constructor_C_54(var__init_12) { /// @src 0:175:223 @@ -616,7 +624,9 @@ object \"D_72\" { let expr_17 := expr_16 } + /// @src 1:91:166 + /// @src 1:113:164 function constructor_D_72(var__init2_63) { /// @src 1:107:108 let expr_60 := 0x03 @@ -635,6 +645,7 @@ object \"D_72\" { update_storage_value_offset_0t_int256_to_t_int256(0x00, expr_68) } + /// @src 1:91:166 function convert_t_int256_to_t_int256(value) -> converted { converted := cleanup_t_int256(value) @@ -894,17 +905,18 @@ object \"D_72\" { mstore(64, newFreePtr) } + /// @src 0:343:426 function fun_f2_53() -> var__42 { - /// @src 0:343:426 /// @src 0:375:378 let zero_t_int256_4 := zero_value_for_split_t_int256() var__42 := zero_t_int256_4 var__42 := modifier_m_40(var__42) } + /// @src 1:91:166 + /// @src 0:343:426 function fun_f2_53_inner(_8) -> var__42 { - /// @src 0:343:426 var__42 := _8 /// @src 0:392:400 @@ -950,9 +962,10 @@ object \"D_72\" { leave } + /// @src 1:91:166 + /// @src 0:226:302 function fun_f_30() -> var__23 { - /// @src 0:226:302 /// @src 0:262:265 let zero_t_int256_1 := zero_value_for_split_t_int256() var__23 := zero_t_int256_1 @@ -970,9 +983,10 @@ object \"D_72\" { leave } + /// @src 1:91:166 + /// @src 0:152:171 function getter_fun_stateVar_10() -> ret { - /// @src 0:152:171 let slot := 0 let offset := 0 @@ -980,6 +994,7 @@ object \"D_72\" { ret := read_from_storage_split_dynamic_t_int256(slot, offset) } + /// @src 1:91:166 function increment_t_int256(value) -> ret { value := cleanup_t_int256(value) @@ -987,8 +1002,8 @@ object \"D_72\" { ret := add(value, 1) } + /// @src 0:304:341 function modifier_m_40(var__42) -> _5 { - /// @src 0:304:341 _5 := var__42 /// @src 0:322:332 @@ -1000,6 +1015,7 @@ object \"D_72\" { _5 := fun_f2_53_inner(var__42) } + /// @src 1:91:166 function panic_error_0x11() { mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) diff --git a/test/cmdlineTests/yul_source_locations_in_asm/input.json b/test/cmdlineTests/yul_source_locations_in_asm/input.json new file mode 100644 index 000000000..c1a7209b3 --- /dev/null +++ b/test/cmdlineTests/yul_source_locations_in_asm/input.json @@ -0,0 +1,24 @@ +{ + "language": "Solidity", + "sources": + { + "C": + { + "content": "//SPDX-License-Identifier: GPL-2.0\npragma solidity >=0.0;\npragma abicoder v2;\n\ncontract C\n{\n int constant constVar = 41;\n int immutable immutVar = 42;\n int public stateVar;\n\n constructor(int _init)\n {\n stateVar = _init;\n }\n\n function f() external pure returns (int)\n {\n return constVar + immutVar;\n }\n modifier m()\n {\n stateVar++;\n _;\n }\n function f2() m public returns (int)\n {\n return stateVar + this.f() + immutVar;\n }\n}\n" + }, + "D": + { + "content": "//SPDX-License-Identifier: GPL-2.0\npragma solidity >=0.0;\npragma abicoder v2;\nimport \"C\";\n\ncontract D is C(3)\n{\n constructor(int _init2)\n {\n stateVar += _init2;\n }\n}\n" + } + }, + "settings": + { + "viaIR": true, + "optimizer": { "enabled": true }, + + "outputSelection": + { + "*": { "*": ["evm.assembly"] } + } + } +} diff --git a/test/cmdlineTests/yul_source_locations_in_asm/output.json b/test/cmdlineTests/yul_source_locations_in_asm/output.json new file mode 100644 index 000000000..7c96a12db --- /dev/null +++ b/test/cmdlineTests/yul_source_locations_in_asm/output.json @@ -0,0 +1,888 @@ +{"contracts":{"C":{"C":{"evm":{"assembly":" /* \"C\":79:428 contract C... */ + mstore(0x40, 0xa0) + jumpi(tag_1, iszero(callvalue)) + 0x00 + dup1 + revert +tag_1: + bytecodeSize + codesize + dup2 + swap1 + sub + 0xa0 + 0x1f + dup3 + add + not(0x1f) + and + dup2 + add + swap1 + sub(shl(0x40, 0x01), 0x01) + dup3 + gt + swap1 + dup3 + lt + or + iszero + tag_2 + jumpi + mstore(0x00, shl(0xe0, 0x4e487b71)) + mstore(0x04, 0x41) + revert(0x00, 0x24) +tag_2: + 0x40 + mstore + dup1 + dup3 + 0xa0 + codecopy + 0x20 + dup2 + slt + iszero + tag_3 + jumpi + 0x00 + dup1 + revert +tag_3: + pop + pop + tag_4 + mload(0xa0) + /* \"C\":147:149 42 */ + mstore(0x80, 0x2a) + 0x00 + /* \"C\":79:428 contract C... */ + sstore + /* \"C\":175:223 constructor(int _init)... */ + jump + /* \"C\":79:428 contract C... */ +tag_4: + mload(0x40) + dataSize(sub_0) + dup1 + dataOffset(sub_0) + dup4 + codecopy + mload(0x80) + dup3 + assignImmutable(\"0xe4b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10\") + dup1 + dup3 + return +stop + +sub_0: assembly { + /* \"C\":79:428 contract C... */ + mstore(0x40, 0x80) + jumpi(tag_1, lt(calldatasize, 0x04)) + 0x00 + dup1 + calldataload + 0xe0 + shr + 0x26121ff0 + dup2 + eq + tag_3 + jumpi + 0x793816ec + dup2 + eq + tag_4 + jumpi + 0x9942ec6f + dup2 + eq + tag_5 + jumpi + jump(tag_2) + tag_3: + jumpi(tag_6, iszero(callvalue)) + dup2 + dup3 + revert + tag_6: + tag_7 + calldatasize + tag_8 + jump\t// in + tag_7: + /* \"C\":279:298 constVar + immutVar */ + tag_9 + /* \"C\":290:298 immutVar */ + immutable(\"0xe4b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10\") + /* \"C\":279:298 constVar + immutVar */ + tag_10 + jump\t// in + tag_9: + /* \"C\":79:428 contract C... */ + mload(0x40) + dup2 + dup2 + mstore + 0x20 + dup2 + return + tag_4: + jumpi(tag_13, iszero(callvalue)) + dup2 + dup3 + revert + tag_13: + tag_14 + calldatasize + tag_8 + jump\t// in + tag_14: + dup2 + sload + mload(0x40) + dup2 + dup2 + mstore + 0x20 + dup2 + return + tag_5: + jumpi(tag_16, iszero(callvalue)) + dup2 + dup3 + revert + tag_16: + tag_17 + calldatasize + tag_8 + jump\t// in + tag_17: + /* \"C\":375:378 int */ + tag_9 + tag_19 + jump\t// in + /* \"C\":79:428 contract C... */ + tag_2: + pop + pop + tag_1: + 0x00 + dup1 + revert + tag_8: + 0x00 + not(0x03) + dup3 + add + slt + iszero + tag_23 + jumpi + 0x00 + dup1 + revert + tag_23: + pop + jump\t// out + tag_24: + 0x00 + 0x20 + dup3 + dup5 + sub + slt + iszero + tag_26 + jumpi + 0x00 + dup1 + revert + tag_26: + pop + mload + swap2 + swap1 + pop + jump\t// out + tag_10: + 0x00 + sub(shl(0xff, 0x01), 0x2a) + dup3 + sgt + 0x01 + and + iszero + tag_30 + jumpi + tag_30 + tag_31 + jump\t// in + tag_30: + pop + /* \"C\":117:119 41 */ + 0x29 + /* \"C\":79:428 contract C... */ + add + swap1 + jump\t// out + tag_32: + 0x00 + dup1 + dup3 + slt + dup1 + iszero + sub(shl(0xff, 0x01), 0x01) + dup5 + swap1 + sub + dup6 + sgt + and + iszero + tag_35 + jumpi + tag_35 + tag_31 + jump\t// in + tag_35: + shl(0xff, 0x01) + dup4 + swap1 + sub + dup5 + slt + dup2 + and + iszero + tag_37 + jumpi + tag_37 + tag_31 + jump\t// in + tag_37: + pop + pop + add + swap1 + jump\t// out + /* \"C\":304:341 modifier m()... */ + tag_19: + 0x00 + /* \"C\":79:428 contract C... */ + dup1 + sload + /* \"C\":304:341 modifier m()... */ + dup2 + swap1 + sub(shl(0xff, 0x01), 0x01) + /* \"C\":79:428 contract C... */ + dup2 + eq + iszero + tag_40 + jumpi + tag_40 + tag_31 + jump\t// in + tag_40: + 0x01 + add + dup1 + dup3 + sstore + /* \"C\":403:407 this */ + address + /* \"C\":403:411 this.f() */ + extcodesize + tag_41 + jumpi + /* \"C\":79:428 contract C... */ + dup2 + dup3 + revert + /* \"C\":403:411 this.f() */ + tag_41: + /* \"C\":79:428 contract C... */ + mload(0x40) + shl(0xe4, 0x026121ff) + /* \"C\":403:411 this.f() */ + dup2 + mstore + 0x20 + /* \"C\":79:428 contract C... */ + dup2 + /* \"C\":403:411 this.f() */ + 0x04 + /* \"C\":79:428 contract C... */ + dup2 + /* \"C\":403:407 this */ + address + /* \"C\":403:411 this.f() */ + gas + staticcall + dup1 + tag_42 + jumpi + /* \"C\":79:428 contract C... */ + mload(0x40) + returndatasize + dup6 + dup3 + returndatacopy + returndatasize + dup2 + revert + /* \"C\":403:411 this.f() */ + tag_42: + /* \"C\":79:428 contract C... */ + dup4 + /* \"C\":403:411 this.f() */ + dup2 + iszero + tag_43 + jumpi + returndatasize + /* \"C\":79:428 contract C... */ + 0x1f + add + not(0x1f) + and + /* \"C\":117:119 41 */ + dup4 + add + 0xffffffffffffffff + dup2 + gt + dup5 + dup3 + lt + or + iszero + tag_44 + jumpi + shl(0xe0, 0x4e487b71) + /* \"C\":79:428 contract C... */ + dup7 + mstore + 0x41 + /* \"C\":403:411 this.f() */ + 0x04 + /* \"C\":79:428 contract C... */ + mstore + 0x24 + dup7 + revert + /* \"C\":117:119 41 */ + tag_44: + /* \"C\":79:428 contract C... */ + 0x40 + /* \"C\":117:119 41 */ + mstore + /* \"C\":403:411 this.f() */ + tag_45 + returndatasize + dup5 + add + dup5 + tag_24 + jump\t// in + tag_45: + swap1 + pop + tag_43: + /* \"C\":392:411 stateVar + this.f() */ + tag_46 + dup2 + dup6 + tag_32 + jump\t// in + tag_46: + swap5 + pop + pop + pop + pop + pop + /* \"C\":392:422 stateVar + this.f() + immutVar */ + tag_47 + /* \"C\":414:422 immutVar */ + immutable(\"0xe4b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10\") + /* \"C\":392:422 stateVar + this.f() + immutVar */ + dup3 + tag_32 + jump\t// in + tag_47: + /* \"C\":336:337 _ */ + swap2 + pop + pop + /* \"C\":304:341 modifier m()... */ + swap1 + jump\t// out + /* \"C\":79:428 contract C... */ + tag_31: + mstore(0x00, shl(0xe0, 0x4e487b71)) + mstore(0x04, 0x11) + revert(0x00, 0x24) + + auxdata: +} +"}}},"D":{"D":{"evm":{"assembly":" /* \"D\":91:166 contract D is C(3)... */ + mstore(0x40, 0xa0) + jumpi(tag_1, iszero(callvalue)) + 0x00 + dup1 + revert +tag_1: + bytecodeSize + codesize + dup2 + swap1 + sub + 0xa0 + 0x1f + dup3 + add + not(0x1f) + and + dup2 + add + swap1 + sub(shl(0x40, 0x01), 0x01) + dup3 + gt + swap1 + dup3 + lt + or + iszero + tag_2 + jumpi + mstore(0x00, shl(0xe0, 0x4e487b71)) + mstore(0x04, 0x41) + revert(0x00, 0x24) +tag_2: + 0x40 + mstore + dup1 + dup3 + 0xa0 + codecopy + 0x20 + dup2 + slt + iszero + tag_3 + jumpi + 0x00 + dup1 + revert +tag_3: + pop + pop + tag_4 + mload(0xa0) + tag_5 + jump\t// in +tag_4: + mload(0x40) + dataSize(sub_0) + dup1 + dataOffset(sub_0) + dup4 + codecopy + mload(0x80) + dup3 + assignImmutable(\"0xe4b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10\") + dup1 + dup3 + return + /* \"D\":113:164 constructor(int _init2)... */ +tag_5: + /* \"C\":147:149 42 */ + mstore(0x80, 0x2a) + /* \"D\":107:108 3 */ + 0x03 + 0x00 + /* \"D\":91:166 contract D is C(3)... */ + sstore + sub(shl(0xff, 0x01), 0x04) + dup2 + sgt + 0x01 + and + iszero + tag_8 + jumpi + mstore(0x00, shl(0xe0, 0x4e487b71)) + mstore(0x04, 0x11) + revert(0x00, 0x24) +tag_8: + /* \"D\":107:108 3 */ + 0x03 + /* \"D\":91:166 contract D is C(3)... */ + add + 0x00 + sstore + /* \"D\":113:164 constructor(int _init2)... */ + jump\t// out +stop + +sub_0: assembly { + /* \"D\":91:166 contract D is C(3)... */ + mstore(0x40, 0x80) + jumpi(tag_1, lt(calldatasize, 0x04)) + 0x00 + dup1 + calldataload + 0xe0 + shr + 0x26121ff0 + dup2 + eq + tag_3 + jumpi + 0x793816ec + dup2 + eq + tag_4 + jumpi + 0x9942ec6f + dup2 + eq + tag_5 + jumpi + jump(tag_2) + tag_3: + jumpi(tag_6, iszero(callvalue)) + dup2 + dup3 + revert + tag_6: + tag_7 + calldatasize + tag_8 + jump\t// in + tag_7: + /* \"C\":279:298 constVar + immutVar */ + tag_9 + /* \"C\":290:298 immutVar */ + immutable(\"0xe4b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10\") + /* \"C\":279:298 constVar + immutVar */ + tag_10 + jump\t// in + tag_9: + /* \"D\":91:166 contract D is C(3)... */ + mload(0x40) + dup2 + dup2 + mstore + 0x20 + dup2 + return + tag_4: + jumpi(tag_13, iszero(callvalue)) + dup2 + dup3 + revert + tag_13: + tag_14 + calldatasize + tag_8 + jump\t// in + tag_14: + dup2 + sload + mload(0x40) + dup2 + dup2 + mstore + 0x20 + dup2 + return + tag_5: + jumpi(tag_16, iszero(callvalue)) + dup2 + dup3 + revert + tag_16: + tag_17 + calldatasize + tag_8 + jump\t// in + tag_17: + /* \"C\":375:378 int */ + tag_9 + tag_19 + jump\t// in + /* \"D\":91:166 contract D is C(3)... */ + tag_2: + pop + pop + tag_1: + 0x00 + dup1 + revert + tag_8: + 0x00 + not(0x03) + dup3 + add + slt + iszero + tag_23 + jumpi + 0x00 + dup1 + revert + tag_23: + pop + jump\t// out + tag_24: + 0x00 + 0x20 + dup3 + dup5 + sub + slt + iszero + tag_26 + jumpi + 0x00 + dup1 + revert + tag_26: + pop + mload + swap2 + swap1 + pop + jump\t// out + tag_10: + 0x00 + sub(shl(0xff, 0x01), 0x2a) + dup3 + sgt + 0x01 + and + iszero + tag_30 + jumpi + tag_30 + tag_31 + jump\t// in + tag_30: + pop + /* \"C\":117:119 41 */ + 0x29 + /* \"D\":91:166 contract D is C(3)... */ + add + swap1 + jump\t// out + tag_32: + 0x00 + dup1 + dup3 + slt + dup1 + iszero + sub(shl(0xff, 0x01), 0x01) + dup5 + swap1 + sub + dup6 + sgt + and + iszero + tag_35 + jumpi + tag_35 + tag_31 + jump\t// in + tag_35: + shl(0xff, 0x01) + dup4 + swap1 + sub + dup5 + slt + dup2 + and + iszero + tag_37 + jumpi + tag_37 + tag_31 + jump\t// in + tag_37: + pop + pop + add + swap1 + jump\t// out + /* \"C\":304:341 modifier m()... */ + tag_19: + 0x00 + /* \"D\":91:166 contract D is C(3)... */ + dup1 + sload + /* \"C\":304:341 modifier m()... */ + dup2 + swap1 + sub(shl(0xff, 0x01), 0x01) + /* \"D\":91:166 contract D is C(3)... */ + dup2 + eq + iszero + tag_40 + jumpi + tag_40 + tag_31 + jump\t// in + tag_40: + 0x01 + add + dup1 + dup3 + sstore + /* \"C\":403:407 this */ + address + /* \"C\":403:411 this.f() */ + extcodesize + tag_41 + jumpi + /* \"D\":91:166 contract D is C(3)... */ + dup2 + dup3 + revert + /* \"C\":403:411 this.f() */ + tag_41: + /* \"D\":91:166 contract D is C(3)... */ + mload(0x40) + shl(0xe4, 0x026121ff) + /* \"C\":403:411 this.f() */ + dup2 + mstore + 0x20 + /* \"D\":91:166 contract D is C(3)... */ + dup2 + /* \"C\":403:411 this.f() */ + 0x04 + /* \"D\":91:166 contract D is C(3)... */ + dup2 + /* \"C\":403:407 this */ + address + /* \"C\":403:411 this.f() */ + gas + staticcall + dup1 + tag_42 + jumpi + /* \"D\":91:166 contract D is C(3)... */ + mload(0x40) + returndatasize + dup6 + dup3 + returndatacopy + returndatasize + dup2 + revert + /* \"C\":403:411 this.f() */ + tag_42: + /* \"D\":91:166 contract D is C(3)... */ + dup4 + /* \"C\":403:411 this.f() */ + dup2 + iszero + tag_43 + jumpi + returndatasize + /* \"D\":91:166 contract D is C(3)... */ + 0x1f + add + not(0x1f) + and + /* \"C\":117:119 41 */ + dup4 + add + 0xffffffffffffffff + dup2 + gt + dup5 + dup3 + lt + or + iszero + tag_44 + jumpi + shl(0xe0, 0x4e487b71) + /* \"D\":91:166 contract D is C(3)... */ + dup7 + mstore + 0x41 + /* \"C\":403:411 this.f() */ + 0x04 + /* \"D\":91:166 contract D is C(3)... */ + mstore + 0x24 + dup7 + revert + /* \"C\":117:119 41 */ + tag_44: + /* \"D\":91:166 contract D is C(3)... */ + 0x40 + /* \"C\":117:119 41 */ + mstore + /* \"C\":403:411 this.f() */ + tag_45 + returndatasize + dup5 + add + dup5 + tag_24 + jump\t// in + tag_45: + swap1 + pop + tag_43: + /* \"C\":392:411 stateVar + this.f() */ + tag_46 + dup2 + dup6 + tag_32 + jump\t// in + tag_46: + swap5 + pop + pop + pop + pop + pop + /* \"C\":392:422 stateVar + this.f() + immutVar */ + tag_47 + /* \"C\":414:422 immutVar */ + immutable(\"0xe4b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10\") + /* \"C\":392:422 stateVar + this.f() + immutVar */ + dup3 + tag_32 + jump\t// in + tag_47: + /* \"C\":336:337 _ */ + swap2 + pop + pop + /* \"C\":304:341 modifier m()... */ + swap1 + jump\t// out + /* \"D\":91:166 contract D is C(3)... */ + tag_31: + mstore(0x00, shl(0xe0, 0x4e487b71)) + mstore(0x04, 0x11) + revert(0x00, 0x24) + + auxdata: +} +"}}}},"sources":{"C":{"id":0},"D":{"id":1}}} diff --git a/test/cmdlineTests/yul_string_format_ascii/output.json b/test/cmdlineTests/yul_string_format_ascii/output.json index 08c864157..a48cc0d68 100644 --- a/test/cmdlineTests/yul_string_format_ascii/output.json +++ b/test/cmdlineTests/yul_string_format_ascii/output.json @@ -24,11 +24,13 @@ object \"C_11\" { memPtr := mload(64) } + /// @src 0:78:164 function constructor_C_11() { /// @src 0:78:164 } + /// @src 0:78:164 function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) @@ -150,8 +152,8 @@ object \"C_11\" { mstore(64, newFreePtr) } + /// @src 0:91:162 function fun_f_10() -> var__5_mpos { - /// @src 0:91:162 /// @src 0:127:140 let zero_t_string_memory_ptr_1_mpos := zero_value_for_split_t_string_memory_ptr() var__5_mpos := zero_t_string_memory_ptr_1_mpos @@ -161,6 +163,7 @@ object \"C_11\" { leave } + /// @src 0:78:164 function panic_error_0x41() { mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json index 25bdeaec3..6dabd7fdd 100644 --- a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json @@ -24,11 +24,13 @@ object \"C_11\" { memPtr := mload(64) } + /// @src 0:78:158 function constructor_C_11() { /// @src 0:78:158 } + /// @src 0:78:158 function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) @@ -90,8 +92,8 @@ object \"C_11\" { converted := 0x6162636162630000000000000000000000000000000000000000000000000000 } + /// @src 0:91:156 function fun_f_10() -> var__5 { - /// @src 0:91:156 /// @src 0:127:134 let zero_t_bytes32_1 := zero_value_for_split_t_bytes32() var__5 := zero_t_bytes32_1 @@ -101,6 +103,7 @@ object \"C_11\" { leave } + /// @src 0:78:158 function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { revert(0, 0) diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json index 776944b3c..09e81312b 100644 --- a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json @@ -24,11 +24,13 @@ object \"C_11\" { memPtr := mload(64) } + /// @src 0:78:159 function constructor_C_11() { /// @src 0:78:159 } + /// @src 0:78:159 function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) @@ -94,8 +96,8 @@ object \"C_11\" { converted := shift_left_224(cleanup_t_rational_1633837924_by_1(value)) } + /// @src 0:91:157 function fun_f_10() -> var__5 { - /// @src 0:91:157 /// @src 0:127:133 let zero_t_bytes4_1 := zero_value_for_split_t_bytes4() var__5 := zero_t_bytes4_1 @@ -107,6 +109,7 @@ object \"C_11\" { leave } + /// @src 0:78:159 function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { revert(0, 0) diff --git a/test/cmdlineTests/yul_string_format_ascii_long/output.json b/test/cmdlineTests/yul_string_format_ascii_long/output.json index df7065d2d..f9110a134 100644 --- a/test/cmdlineTests/yul_string_format_ascii_long/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_long/output.json @@ -24,11 +24,13 @@ object \"C_11\" { memPtr := mload(64) } + /// @src 0:78:243 function constructor_C_11() { /// @src 0:78:243 } + /// @src 0:78:243 function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) @@ -150,8 +152,8 @@ object \"C_11\" { mstore(64, newFreePtr) } + /// @src 0:91:241 function fun_f_10() -> var__5_mpos { - /// @src 0:91:241 /// @src 0:127:140 let zero_t_string_memory_ptr_1_mpos := zero_value_for_split_t_string_memory_ptr() var__5_mpos := zero_t_string_memory_ptr_1_mpos @@ -161,6 +163,7 @@ object \"C_11\" { leave } + /// @src 0:78:243 function panic_error_0x41() { mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) diff --git a/test/cmdlineTests/yul_string_format_hex/output.json b/test/cmdlineTests/yul_string_format_hex/output.json index de11d0fe9..e944cf5e1 100644 --- a/test/cmdlineTests/yul_string_format_hex/output.json +++ b/test/cmdlineTests/yul_string_format_hex/output.json @@ -24,11 +24,13 @@ object \"C_11\" { memPtr := mload(64) } + /// @src 0:78:159 function constructor_C_11() { /// @src 0:78:159 } + /// @src 0:78:159 function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) @@ -94,8 +96,8 @@ object \"C_11\" { converted := shift_left_224(cleanup_t_rational_2864434397_by_1(value)) } + /// @src 0:91:157 function fun_f_10() -> var__5 { - /// @src 0:91:157 /// @src 0:127:133 let zero_t_bytes4_1 := zero_value_for_split_t_bytes4() var__5 := zero_t_bytes4_1 @@ -107,6 +109,7 @@ object \"C_11\" { leave } + /// @src 0:78:159 function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { revert(0, 0) diff --git a/test/libyul/ObjectParser.cpp b/test/libyul/ObjectParser.cpp index 946538078..049cca8b5 100644 --- a/test/libyul/ObjectParser.cpp +++ b/test/libyul/ObjectParser.cpp @@ -108,7 +108,7 @@ Error expectError(string const& _source, bool _allowWarnings = false) return *error; } -tuple, ErrorList> tryGetSourceLocationMapping(string _source) +tuple, ErrorList> tryGetSourceLocationMapping(string _source) { vector lines; boost::split(lines, _source, boost::is_any_of("\n")); From 81e9225304656de436099d5727a26353489c3740 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Tue, 3 Aug 2021 14:41:10 +0200 Subject: [PATCH 85/98] Eliminate some unnecessary header inclusions in headers. --- liblangutil/ParserBase.h | 1 + liblangutil/SemVerHandler.cpp | 8 ++++++++ liblangutil/SemVerHandler.h | 6 +----- liblangutil/Token.cpp | 19 +++++++++++++++++++ liblangutil/Token.h | 20 ++------------------ libyul/AsmJsonImporter.cpp | 1 + libyul/AsmParser.cpp | 17 ++++++++++++++++- libyul/AsmParser.h | 14 +------------- 8 files changed, 49 insertions(+), 37 deletions(-) diff --git a/liblangutil/ParserBase.h b/liblangutil/ParserBase.h index 098826f1c..d75a97707 100644 --- a/liblangutil/ParserBase.h +++ b/liblangutil/ParserBase.h @@ -33,6 +33,7 @@ namespace solidity::langutil class ErrorReporter; class Scanner; +struct SourceLocation; struct ErrorId; class ParserBase diff --git a/liblangutil/SemVerHandler.cpp b/liblangutil/SemVerHandler.cpp index af408789c..ede1cd7be 100644 --- a/liblangutil/SemVerHandler.cpp +++ b/liblangutil/SemVerHandler.cpp @@ -23,12 +23,20 @@ #include +#include + #include using namespace std; using namespace solidity; using namespace solidity::langutil; +SemVerMatchExpressionParser::SemVerMatchExpressionParser(vector _tokens, vector _literals): + m_tokens(std::move(_tokens)), m_literals(std::move(_literals)) +{ + solAssert(m_tokens.size() == m_literals.size(), ""); +} + SemVerVersion::SemVerVersion(string const& _versionString) { auto i = _versionString.begin(); diff --git a/liblangutil/SemVerHandler.h b/liblangutil/SemVerHandler.h index 0c4f57a03..bce4d736b 100644 --- a/liblangutil/SemVerHandler.h +++ b/liblangutil/SemVerHandler.h @@ -85,11 +85,7 @@ struct SemVerMatchExpression class SemVerMatchExpressionParser { public: - SemVerMatchExpressionParser(std::vector _tokens, std::vector _literals): - m_tokens(std::move(_tokens)), m_literals(std::move(_literals)) - { - solAssert(m_tokens.size() == m_literals.size(), ""); - } + SemVerMatchExpressionParser(std::vector _tokens, std::vector _literals); /// Returns an expression if it was parseable, or nothing otherwise. std::optional parse(); diff --git a/liblangutil/Token.cpp b/liblangutil/Token.cpp index b6cb1e0ed..8bdaff1fd 100644 --- a/liblangutil/Token.cpp +++ b/liblangutil/Token.cpp @@ -41,6 +41,7 @@ // along with solidity. If not, see . #include +#include #include using namespace std; @@ -48,6 +49,24 @@ using namespace std; namespace solidity::langutil { +Token TokenTraits::AssignmentToBinaryOp(Token op) +{ + solAssert(isAssignmentOp(op) && op != Token::Assign, ""); + return static_cast(static_cast(op) + (static_cast(Token::BitOr) - static_cast(Token::AssignBitOr))); +} + +std::string ElementaryTypeNameToken::toString(bool const& tokenValue) const +{ + std::string name = TokenTraits::toString(m_token); + if (tokenValue || (firstNumber() == 0 && secondNumber() == 0)) + return name; + solAssert(name.size() >= 3, "Token name size should be greater than 3. Should not reach here."); + if (m_token == Token::FixedMxN || m_token == Token::UFixedMxN) + return name.substr(0, name.size() - 3) + std::to_string(m_firstNumber) + "x" + std::to_string(m_secondNumber); + else + return name.substr(0, name.size() - 1) + std::to_string(m_firstNumber); +} + void ElementaryTypeNameToken::assertDetails(Token _baseType, unsigned const& _first, unsigned const& _second) { solAssert(TokenTraits::isElementaryTypeName(_baseType), "Expected elementary type name: " + string(TokenTraits::toString(_baseType))); diff --git a/liblangutil/Token.h b/liblangutil/Token.h index 13f836589..eb8015f4d 100644 --- a/liblangutil/Token.h +++ b/liblangutil/Token.h @@ -42,8 +42,6 @@ #pragma once -#include -#include #include #include @@ -329,11 +327,7 @@ namespace TokenTraits bool isYulKeyword(std::string const& _literal); - inline Token AssignmentToBinaryOp(Token op) - { - solAssert(isAssignmentOp(op) && op != Token::Assign, ""); - return static_cast(static_cast(op) + (static_cast(Token::BitOr) - static_cast(Token::AssignBitOr))); - } + Token AssignmentToBinaryOp(Token op); // @returns the precedence > 0 for binary and compare // operators; returns 0 otherwise. @@ -393,17 +387,7 @@ public: Token token() const { return m_token; } ///if tokValue is set to true, then returns the actual token type name, otherwise, returns full type - std::string toString(bool const& tokenValue = false) const - { - std::string name = TokenTraits::toString(m_token); - if (tokenValue || (firstNumber() == 0 && secondNumber() == 0)) - return name; - solAssert(name.size() >= 3, "Token name size should be greater than 3. Should not reach here."); - if (m_token == Token::FixedMxN || m_token == Token::UFixedMxN) - return name.substr(0, name.size() - 3) + std::to_string(m_firstNumber) + "x" + std::to_string(m_secondNumber); - else - return name.substr(0, name.size() - 1) + std::to_string(m_firstNumber); - } + std::string toString(bool const& tokenValue = false) const; private: Token m_token; diff --git a/libyul/AsmJsonImporter.cpp b/libyul/AsmJsonImporter.cpp index 87ce31497..0c4273aa7 100644 --- a/libyul/AsmJsonImporter.cpp +++ b/libyul/AsmJsonImporter.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index e8f5b3e4e..b5e542926 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -24,8 +24,9 @@ #include #include #include -#include #include +#include +#include #include #include @@ -70,6 +71,20 @@ optional toInt(string const& _value) } +std::shared_ptr Parser::createDebugData() const +{ + switch (m_useSourceLocationFrom) + { + case UseSourceLocationFrom::Scanner: + return DebugData::create(ParserBase::currentLocation()); + case UseSourceLocationFrom::LocationOverride: + return DebugData::create(m_locationOverride); + case UseSourceLocationFrom::Comments: + return m_debugDataOverride; + } + solAssert(false, ""); +} + unique_ptr Parser::parse(std::shared_ptr const& _scanner, bool _reuseScanner) { m_recursionDepth = 0; diff --git a/libyul/AsmParser.h b/libyul/AsmParser.h index 23ee935e5..cfde8bec4 100644 --- a/libyul/AsmParser.h +++ b/libyul/AsmParser.h @@ -103,19 +103,7 @@ protected: void fetchSourceLocationFromComment(); /// Creates a DebugData object with the correct source location set. - std::shared_ptr createDebugData() const - { - switch (m_useSourceLocationFrom) - { - case UseSourceLocationFrom::Scanner: - return DebugData::create(ParserBase::currentLocation()); - case UseSourceLocationFrom::LocationOverride: - return DebugData::create(m_locationOverride); - case UseSourceLocationFrom::Comments: - return m_debugDataOverride; - } - solAssert(false, ""); - } + std::shared_ptr createDebugData() const; /// Creates an inline assembly node with the current source location. template T createWithLocation() const From ffc5cfd9a5cfb0f43e615f6fe3d6f5171d7145f8 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 14 Jul 2021 12:53:39 +0200 Subject: [PATCH 86/98] Remove scanner from compiler stack. --- liblangutil/ParserBase.h | 3 - liblangutil/Scanner.cpp | 81 ++--- liblangutil/Scanner.h | 26 +- libsolidity/ast/ASTJsonImporter.cpp | 3 +- libsolidity/codegen/CompilerContext.cpp | 10 +- libsolidity/interface/CompilerStack.cpp | 34 +-- libsolidity/interface/CompilerStack.h | 3 +- libsolidity/parsing/Parser.cpp | 11 +- libsolidity/parsing/Parser.h | 5 +- libyul/AsmJsonImporter.cpp | 6 +- libyul/AsmParser.h | 1 + libyul/ObjectParser.cpp | 3 +- libyul/backends/wasm/EVMToEwasmTranslator.cpp | 11 +- test/liblangutil/Scanner.cpp | 284 ++++++++++-------- test/libsolidity/Assembly.cpp | 5 +- test/libsolidity/SemVerMatcher.cpp | 3 +- .../SolidityExpressionCompiler.cpp | 5 +- test/libsolidity/SolidityParser.cpp | 3 +- test/libyul/Common.cpp | 3 +- test/libyul/ObjectParser.cpp | 5 +- test/libyul/Parser.cpp | 3 +- test/tools/yulopti.cpp | 6 +- tools/yulPhaser/Program.cpp | 2 +- 23 files changed, 275 insertions(+), 241 deletions(-) diff --git a/liblangutil/ParserBase.h b/liblangutil/ParserBase.h index d75a97707..6c5ce081b 100644 --- a/liblangutil/ParserBase.h +++ b/liblangutil/ParserBase.h @@ -24,7 +24,6 @@ #pragma once #include -#include #include #include @@ -50,8 +49,6 @@ public: virtual ~ParserBase() = default; - std::shared_ptr source() const { return m_scanner->charStream(); } - protected: /// Utility class that creates an error and throws an exception if the /// recursion depth is too deep. diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index 760b4803a..87be458fd 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -135,26 +135,11 @@ private: bool m_complete; }; -void Scanner::reset(CharStream _source) -{ - m_source = make_shared(std::move(_source)); - m_sourceName = make_shared(m_source->name()); - reset(); -} - -void Scanner::reset(shared_ptr _source) -{ - solAssert(_source.get() != nullptr, "You MUST provide a CharStream when resetting."); - m_source = std::move(_source); - m_sourceName = make_shared(m_source->name()); - reset(); -} - void Scanner::reset() { - m_source->reset(); + m_source.reset(); m_kind = ScannerKind::Solidity; - m_char = m_source->get(); + m_char = m_source.get(); skipWhitespace(); next(); next(); @@ -163,7 +148,7 @@ void Scanner::reset() void Scanner::setPosition(size_t _offset) { - m_char = m_source->setPosition(_offset); + m_char = m_source.setPosition(_offset); scanToken(); next(); next(); @@ -229,7 +214,7 @@ void Scanner::rescan() rollbackTo = static_cast(m_tokens[Current].location.start); else rollbackTo = static_cast(m_skippedComments[Current].location.start); - m_char = m_source->rollback(m_source->position() - rollbackTo); + m_char = m_source.rollback(m_source.position() - rollbackTo); next(); next(); next(); @@ -324,12 +309,12 @@ Token Scanner::skipSingleLineComment() { // Line terminator is not part of the comment. If it is a // non-ascii line terminator, it will result in a parser error. - size_t startPosition = m_source->position(); + size_t startPosition = m_source.position(); while (!isUnicodeLinebreak()) if (!advance()) break; - ScannerError unicodeDirectionError = validateBiDiMarkup(*m_source, startPosition); + ScannerError unicodeDirectionError = validateBiDiMarkup(m_source, startPosition); if (unicodeDirectionError != ScannerError::NoError) return setError(unicodeDirectionError); @@ -362,28 +347,28 @@ bool Scanner::tryScanEndOfLine() size_t Scanner::scanSingleLineDocComment() { LiteralScope literal(this, LITERAL_TYPE_COMMENT); - size_t endPosition = m_source->position(); + size_t endPosition = m_source.position(); skipWhitespaceExceptUnicodeLinebreak(); while (!isSourcePastEndOfInput()) { - endPosition = m_source->position(); + endPosition = m_source.position(); if (tryScanEndOfLine()) { // Check if next line is also a single-line comment. // If any whitespaces were skipped, use source position before. if (!skipWhitespaceExceptUnicodeLinebreak()) - endPosition = m_source->position(); + endPosition = m_source.position(); - if (!m_source->isPastEndOfInput(3) && - m_source->get(0) == '/' && - m_source->get(1) == '/' && - m_source->get(2) == '/') + if (!m_source.isPastEndOfInput(3) && + m_source.get(0) == '/' && + m_source.get(1) == '/' && + m_source.get(2) == '/') { - if (!m_source->isPastEndOfInput(4) && m_source->get(3) == '/') + if (!m_source.isPastEndOfInput(4) && m_source.get(3) == '/') break; // "////" is not a documentation comment - m_char = m_source->advanceAndGet(3); + m_char = m_source.advanceAndGet(3); if (atEndOfLine()) continue; addCommentLiteralChar('\n'); @@ -404,7 +389,7 @@ size_t Scanner::scanSingleLineDocComment() Token Scanner::skipMultiLineComment() { - size_t startPosition = m_source->position(); + size_t startPosition = m_source.position(); while (!isSourcePastEndOfInput()) { char prevChar = m_char; @@ -415,7 +400,7 @@ Token Scanner::skipMultiLineComment() // multi-line comments are treated as whitespace. if (prevChar == '*' && m_char == '/') { - ScannerError unicodeDirectionError = validateBiDiMarkup(*m_source, startPosition); + ScannerError unicodeDirectionError = validateBiDiMarkup(m_source, startPosition); if (unicodeDirectionError != ScannerError::NoError) return setError(unicodeDirectionError); @@ -442,22 +427,22 @@ Token Scanner::scanMultiLineDocComment() if (atEndOfLine()) { skipWhitespace(); - if (!m_source->isPastEndOfInput(1) && m_source->get(0) == '*' && m_source->get(1) == '*') + if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) == '*') { // it is unknown if this leads to the end of the comment addCommentLiteralChar('*'); advance(); } - else if (!m_source->isPastEndOfInput(1) && m_source->get(0) == '*' && m_source->get(1) != '/') + else if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) != '/') { // skip first '*' in subsequent lines - m_char = m_source->advanceAndGet(1); + m_char = m_source.advanceAndGet(1); if (atEndOfLine()) // ignores empty lines continue; if (charsAdded) addCommentLiteralChar('\n'); // corresponds to the end of previous line } - else if (!m_source->isPastEndOfInput(1) && m_source->get(0) == '*' && m_source->get(1) == '/') + else if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) == '/') { // if after newline the comment ends, don't insert the newline - m_char = m_source->advanceAndGet(2); + m_char = m_source.advanceAndGet(2); endFound = true; break; } @@ -465,9 +450,9 @@ Token Scanner::scanMultiLineDocComment() addCommentLiteralChar('\n'); } - if (!m_source->isPastEndOfInput(1) && m_source->get(0) == '*' && m_source->get(1) == '/') + if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) == '/') { - m_char = m_source->advanceAndGet(2); + m_char = m_source.advanceAndGet(2); endFound = true; break; } @@ -822,11 +807,11 @@ bool Scanner::isUnicodeLinebreak() if (0x0a <= m_char && m_char <= 0x0d) // line feed, vertical tab, form feed, carriage return return true; - if (!m_source->isPastEndOfInput(1) && uint8_t(m_source->get(0)) == 0xc2 && uint8_t(m_source->get(1)) == 0x85) + if (!m_source.isPastEndOfInput(1) && uint8_t(m_source.get(0)) == 0xc2 && uint8_t(m_source.get(1)) == 0x85) // NEL - U+0085, C2 85 in utf8 return true; - if (!m_source->isPastEndOfInput(2) && uint8_t(m_source->get(0)) == 0xe2 && uint8_t(m_source->get(1)) == 0x80 && ( - uint8_t(m_source->get(2)) == 0xa8 || uint8_t(m_source->get(2)) == 0xa9 + if (!m_source.isPastEndOfInput(2) && uint8_t(m_source.get(0)) == 0xe2 && uint8_t(m_source.get(1)) == 0x80 && ( + uint8_t(m_source.get(2)) == 0xa8 || uint8_t(m_source.get(2)) == 0xa9 )) // LS - U+2028, E2 80 A8 in utf8 // PS - U+2029, E2 80 A9 in utf8 @@ -836,7 +821,7 @@ bool Scanner::isUnicodeLinebreak() Token Scanner::scanString(bool const _isUnicode) { - size_t startPosition = m_source->position(); + size_t startPosition = m_source.position(); char const quote = m_char; advance(); // consume quote LiteralScope literal(this, LITERAL_TYPE_STRING); @@ -867,7 +852,7 @@ Token Scanner::scanString(bool const _isUnicode) if (_isUnicode) { - ScannerError unicodeDirectionError = validateBiDiMarkup(*m_source, startPosition); + ScannerError unicodeDirectionError = validateBiDiMarkup(m_source, startPosition); if (unicodeDirectionError != ScannerError::NoError) return setError(unicodeDirectionError); } @@ -921,7 +906,7 @@ void Scanner::scanDecimalDigits() // May continue with decimal digit or underscore for grouping. do addLiteralCharAndAdvance(); - while (!m_source->isPastEndOfInput() && (isDecimalDigit(m_char) || m_char == '_')); + while (!m_source.isPastEndOfInput() && (isDecimalDigit(m_char) || m_char == '_')); // Defer further validation of underscore to SyntaxChecker. } @@ -967,7 +952,7 @@ Token Scanner::scanNumber(char _charSeen) scanDecimalDigits(); // optional if (m_char == '.') { - if (!m_source->isPastEndOfInput(1) && m_source->get(1) == '_') + if (!m_source.isPastEndOfInput(1) && m_source.get(1) == '_') { // Assume the input may be a floating point number with leading '_' in fraction part. // Recover by consuming it all but returning `Illegal` right away. @@ -975,7 +960,7 @@ Token Scanner::scanNumber(char _charSeen) addLiteralCharAndAdvance(); // '_' scanDecimalDigits(); } - if (m_source->isPastEndOfInput() || !isDecimalDigit(m_source->get(1))) + if (m_source.isPastEndOfInput() || !isDecimalDigit(m_source.get(1))) { // A '.' has to be followed by a number. literal.complete(); @@ -992,7 +977,7 @@ Token Scanner::scanNumber(char _charSeen) solAssert(kind != HEX, "'e'/'E' must be scanned as part of the hex number"); if (kind != DECIMAL) return setError(ScannerError::IllegalExponent); - else if (!m_source->isPastEndOfInput(1) && m_source->get(1) == '_') + else if (!m_source.isPastEndOfInput(1) && m_source.get(1) == '_') { // Recover from wrongly placed underscore as delimiter in literal with scientific // notation by consuming until the end. diff --git a/liblangutil/Scanner.h b/liblangutil/Scanner.h index 347c75779..3ef6f7a09 100644 --- a/liblangutil/Scanner.h +++ b/liblangutil/Scanner.h @@ -100,17 +100,13 @@ class Scanner { friend class LiteralScope; public: - explicit Scanner(std::shared_ptr _source) { reset(std::move(_source)); } - explicit Scanner(CharStream _source = CharStream()) { reset(std::move(_source)); } + explicit Scanner(CharStream& _source): + m_source(_source), + m_sourceName{std::make_shared(_source.name())} + { + reset(); + } - std::string const& source() const noexcept { return m_source->source(); } - - std::shared_ptr charStream() noexcept { return m_source; } - std::shared_ptr charStream() const noexcept { return m_source; } - - /// Resets the scanner as if newly constructed with _source as input. - void reset(CharStream _source); - void reset(std::shared_ptr _source); /// Resets scanner to the start of input. void reset(); @@ -201,8 +197,8 @@ private: void addUnicodeAsUTF8(unsigned codepoint); ///@} - bool advance() { m_char = m_source->advanceAndGet(); return !m_source->isPastEndOfInput(); } - void rollback(size_t _amount) { m_char = m_source->rollback(_amount); } + bool advance() { m_char = m_source.advanceAndGet(); return !m_source.isPastEndOfInput(); } + void rollback(size_t _amount) { m_char = m_source.rollback(_amount); } /// Rolls back to the start of the current token and re-runs the scanner. void rescan(); @@ -251,15 +247,15 @@ private: bool isUnicodeLinebreak(); /// Return the current source position. - size_t sourcePos() const { return m_source->position(); } - bool isSourcePastEndOfInput() const { return m_source->isPastEndOfInput(); } + size_t sourcePos() const { return m_source.position(); } + bool isSourcePastEndOfInput() const { return m_source.isPastEndOfInput(); } enum TokenIndex { Current, Next, NextNext }; TokenDesc m_skippedComments[3] = {}; // desc for the current, next and nextnext skipped comment TokenDesc m_tokens[3] = {}; // desc for the current, next and nextnext token - std::shared_ptr m_source; + CharStream& m_source; std::shared_ptr m_sourceName; ScannerKind m_kind = ScannerKind::Solidity; diff --git a/libsolidity/ast/ASTJsonImporter.cpp b/libsolidity/ast/ASTJsonImporter.cpp index 041bf49a6..5d7ba1211 100644 --- a/libsolidity/ast/ASTJsonImporter.cpp +++ b/libsolidity/ast/ASTJsonImporter.cpp @@ -958,7 +958,8 @@ Json::Value ASTJsonImporter::member(Json::Value const& _node, string const& _nam Token ASTJsonImporter::scanSingleToken(Json::Value const& _node) { - langutil::Scanner scanner{langutil::CharStream(_node.asString(), "")}; + langutil::CharStream charStream(_node.asString(), ""); + langutil::Scanner scanner{charStream}; astAssert(scanner.peekNextToken() == Token::EOS, "Token string is too long."); return scanner.currentToken(); } diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 8b7f2751b..083dc570c 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -434,14 +434,14 @@ void CompilerContext::appendInlineAssembly( ErrorList errors; ErrorReporter errorReporter(errors); - auto scanner = make_shared(langutil::CharStream(_assembly, _sourceName)); + langutil::CharStream charStream(_assembly, _sourceName); yul::EVMDialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(m_evmVersion); optional locationOverride; if (!_system) locationOverride = m_asm->currentSourceLocation(); shared_ptr parserResult = yul::Parser(errorReporter, dialect, std::move(locationOverride)) - .parse(scanner, false); + .parse(make_shared(charStream), false); #ifdef SOL_OUTPUT_ASM cout << yul::AsmPrinter(&dialect)(*parserResult) << endl; #endif @@ -457,7 +457,7 @@ void CompilerContext::appendInlineAssembly( for (auto const& error: errorReporter.errors()) // TODO if we have "locationOverride", it will be the wrong char stream, // but we do not have access to the solidity scanner. - message += SourceReferenceFormatter::formatErrorInformation(*error, *scanner->charStream()); + message += SourceReferenceFormatter::formatErrorInformation(*error, charStream); message += "-------------------------------------------\n"; solAssert(false, message); @@ -491,8 +491,8 @@ void CompilerContext::appendInlineAssembly( solAssert(m_generatedYulUtilityCode.empty(), ""); m_generatedYulUtilityCode = yul::AsmPrinter(dialect)(*obj.code); string code = yul::AsmPrinter{dialect}(*obj.code); - scanner = make_shared(langutil::CharStream(m_generatedYulUtilityCode, _sourceName)); - obj.code = yul::Parser(errorReporter, dialect).parse(scanner, false); + langutil::CharStream charStream(m_generatedYulUtilityCode, _sourceName); + obj.code = yul::Parser(errorReporter, dialect).parse(make_shared(charStream), false); *obj.analysisInfo = yul::AsmAnalyzer::analyzeStrictAssertCorrect(dialect, obj); } diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index f32b47d2a..757fdebc3 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -313,7 +313,7 @@ void CompilerStack::setSources(StringMap _sources) if (m_stackState != Empty) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set sources before parsing.")); for (auto source: _sources) - m_sources[source.first].scanner = make_shared(CharStream(/*content*/std::move(source.second), /*name*/source.first)); + m_sources[source.first].charStream = make_unique(/*content*/std::move(source.second), /*name*/source.first); m_stackState = SourcesSet; } @@ -336,8 +336,7 @@ bool CompilerStack::parse() { string const& path = sourcesToParse[i]; Source& source = m_sources[path]; - source.scanner->reset(); - source.ast = parser.parse(source.scanner); + source.ast = parser.parse(*source.charStream); if (!source.ast) solAssert(!Error::containsOnlyWarnings(m_errorReporter.errors()), "Parser returned null but did not report error."); else @@ -348,7 +347,7 @@ bool CompilerStack::parse() { string const& newPath = newSource.first; string const& newContents = newSource.second; - m_sources[newPath].scanner = make_shared(CharStream(newContents, newPath)); + m_sources[newPath].charStream = make_shared(newContents, newPath); sourcesToParse.push_back(newPath); } } @@ -377,10 +376,11 @@ void CompilerStack::importASTs(map const& _sources) string const& path = src.first; Source source; source.ast = src.second; - string srcString = util::jsonCompactPrint(m_sourceJsons[src.first]); - ASTPointer scanner = make_shared(langutil::CharStream(srcString, src.first)); - source.scanner = scanner; - m_sources[path] = source; + source.charStream = make_shared( + util::jsonCompactPrint(m_sourceJsons[src.first]), + src.first + ); + m_sources[path] = move(source); } m_stackState = ParsedAndImported; m_importedSources = true; @@ -754,7 +754,8 @@ Json::Value CompilerStack::generatedSources(string const& _contractName, bool _r unsigned sourceIndex = sourceIndices()[sourceName]; ErrorList errors; ErrorReporter errorReporter(errors); - auto scanner = make_shared(langutil::CharStream(source, sourceName)); + CharStream charStream(source, sourceName); + shared_ptr scanner = make_shared(charStream); yul::EVMDialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(m_evmVersion); shared_ptr parserResult = yul::Parser{errorReporter, dialect}.parse(scanner, false); solAssert(parserResult, ""); @@ -1031,10 +1032,9 @@ CharStream const& CompilerStack::charStream(string const& _sourceName) const if (m_stackState < SourcesSet) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No sources set.")); - solAssert(source(_sourceName).scanner, ""); - solAssert(source(_sourceName).scanner->charStream(), ""); + solAssert(source(_sourceName).charStream, ""); - return *source(_sourceName).scanner->charStream(); + return *source(_sourceName).charStream; } SourceUnit const& CompilerStack::ast(string const& _sourceName) const @@ -1079,21 +1079,21 @@ size_t CompilerStack::functionEntryPoint( h256 const& CompilerStack::Source::keccak256() const { if (keccak256HashCached == h256{}) - keccak256HashCached = util::keccak256(scanner->source()); + keccak256HashCached = util::keccak256(charStream->source()); return keccak256HashCached; } h256 const& CompilerStack::Source::swarmHash() const { if (swarmHashCached == h256{}) - swarmHashCached = util::bzzr1Hash(scanner->source()); + swarmHashCached = util::bzzr1Hash(charStream->source()); return swarmHashCached; } string const& CompilerStack::Source::ipfsUrl() const { if (ipfsUrlCached.empty()) - ipfsUrlCached = "dweb:/ipfs/" + util::ipfsHashBase58(scanner->source()); + ipfsUrlCached = "dweb:/ipfs/" + util::ipfsHashBase58(charStream->source()); return ipfsUrlCached; } @@ -1454,12 +1454,12 @@ string CompilerStack::createMetadata(Contract const& _contract) const if (!referencedSources.count(s.first)) continue; - solAssert(s.second.scanner, "Scanner not available"); + solAssert(s.second.charStream, "Character stream not available"); meta["sources"][s.first]["keccak256"] = "0x" + toHex(s.second.keccak256().asBytes()); if (optional licenseString = s.second.ast->licenseString()) meta["sources"][s.first]["license"] = *licenseString; if (m_metadataLiteralSources) - meta["sources"][s.first]["content"] = s.second.scanner->source(); + meta["sources"][s.first]["content"] = s.second.charStream->source(); else { meta["sources"][s.first]["urls"] = Json::arrayValue; diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index efdae0c36..9c713da24 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -57,7 +57,6 @@ namespace solidity::langutil { -class Scanner; class CharStream; } @@ -344,7 +343,7 @@ private: /// The state per source unit. Filled gradually during parsing. struct Source { - std::shared_ptr scanner; + std::shared_ptr charStream; std::shared_ptr ast; util::h256 mutable keccak256HashCached; util::h256 mutable swarmHashCached; diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 852f056ac..c5a62c76e 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -81,13 +81,14 @@ private: SourceLocation m_location; }; -ASTPointer Parser::parse(shared_ptr const& _scanner) +ASTPointer Parser::parse(CharStream& _charStream) { solAssert(!m_insideModifier, ""); try { m_recursionDepth = 0; - m_scanner = _scanner; + m_source = &_charStream; + m_scanner = make_shared(_charStream); ASTNodeFactory nodeFactory(*this); vector> nodes; @@ -2056,14 +2057,16 @@ bool Parser::variableDeclarationStart() optional Parser::findLicenseString(std::vector> const& _nodes) { + solAssert(!!m_source, ""); + // We circumvent the scanner here, because it skips non-docstring comments. static regex const licenseRegex("SPDX-License-Identifier:\\s*([a-zA-Z0-9 ()+.-]+)"); // Search inside all parts of the source not covered by parsed nodes. // This will leave e.g. "global comments". - string const& source = m_scanner->source(); - using iter = decltype(source.begin()); + using iter = std::string::const_iterator; vector> sequencesToSearch; + string const& source = m_source->source(); sequencesToSearch.emplace_back(source.begin(), source.end()); for (ASTPointer const& node: _nodes) if (node->location().hasText()) diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 559f804c2..cdfeddd63 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -29,7 +29,7 @@ namespace solidity::langutil { -class Scanner; +class CharStream; } namespace solidity::frontend @@ -47,7 +47,7 @@ public: m_evmVersion(_evmVersion) {} - ASTPointer parse(std::shared_ptr const& _scanner); + ASTPointer parse(langutil::CharStream& _charStream); private: class ASTNodeFactory; @@ -211,6 +211,7 @@ private: /// Creates an empty ParameterList at the current location (used if parameters can be omitted). ASTPointer createEmptyParameterList(); + langutil::CharStream* m_source = nullptr; /// Flag that signifies whether '_' is parsed as a PlaceholderStatement or a regular identifier. bool m_insideModifier = false; langutil::EVMVersion m_evmVersion; diff --git a/libyul/AsmJsonImporter.cpp b/libyul/AsmJsonImporter.cpp index 0c4273aa7..f74fac4de 100644 --- a/libyul/AsmJsonImporter.cpp +++ b/libyul/AsmJsonImporter.cpp @@ -166,7 +166,8 @@ Literal AsmJsonImporter::createLiteral(Json::Value const& _node) if (kind == "number") { - langutil::Scanner scanner{langutil::CharStream(lit.value.str(), "")}; + langutil::CharStream charStream(lit.value.str(), ""); + langutil::Scanner scanner{charStream}; lit.kind = LiteralKind::Number; yulAssert( scanner.currentToken() == Token::Number, @@ -175,7 +176,8 @@ Literal AsmJsonImporter::createLiteral(Json::Value const& _node) } else if (kind == "bool") { - langutil::Scanner scanner{langutil::CharStream(lit.value.str(), "")}; + langutil::CharStream charStream(lit.value.str(), ""); + langutil::Scanner scanner{charStream}; lit.kind = LiteralKind::Boolean; yulAssert( scanner.currentToken() == Token::TrueLiteral || diff --git a/libyul/AsmParser.h b/libyul/AsmParser.h index 3f1f30963..c386bbe6f 100644 --- a/libyul/AsmParser.h +++ b/libyul/AsmParser.h @@ -90,6 +90,7 @@ public: /// @param _reuseScanner if true, do check for end of input after the `}`. /// @returns an empty shared pointer on error. std::unique_ptr parse(std::shared_ptr const& _scanner, bool _reuseScanner); + // TODO: pass CharStream here instead ^^^^^^ protected: langutil::SourceLocation currentLocation() const override diff --git a/libyul/ObjectParser.cpp b/libyul/ObjectParser.cpp index ab03e81a1..347096a3b 100644 --- a/libyul/ObjectParser.cpp +++ b/libyul/ObjectParser.cpp @@ -130,7 +130,8 @@ optional ObjectParser::tryParseSourceNameMapping() solAssert(sm.size() == 2, ""); auto text = m_scanner->currentCommentLiteral().substr(static_cast(sm.position() + sm.length())); - Scanner scanner(make_shared(text, "")); + CharStream charStream(text, ""); + Scanner scanner(charStream); if (scanner.currentToken() == Token::EOS) return SourceNameMap{}; SourceNameMap sourceNames; diff --git a/libyul/backends/wasm/EVMToEwasmTranslator.cpp b/libyul/backends/wasm/EVMToEwasmTranslator.cpp index 65687cf59..418d0dbcb 100644 --- a/libyul/backends/wasm/EVMToEwasmTranslator.cpp +++ b/libyul/backends/wasm/EVMToEwasmTranslator.cpp @@ -125,7 +125,7 @@ void EVMToEwasmTranslator::parsePolyfill() { ErrorList errors; ErrorReporter errorReporter(errors); - shared_ptr scanner{make_shared(CharStream( + CharStream charStream( "{" + string(solidity::yul::wasm::polyfill::Arithmetic) + string(solidity::yul::wasm::polyfill::Bitwise) + @@ -135,15 +135,18 @@ void EVMToEwasmTranslator::parsePolyfill() string(solidity::yul::wasm::polyfill::Keccak) + string(solidity::yul::wasm::polyfill::Logical) + string(solidity::yul::wasm::polyfill::Memory) + - "}", ""))}; - m_polyfill = Parser(errorReporter, WasmDialect::instance()).parse(scanner, false); + "}", ""); + m_polyfill = Parser(errorReporter, WasmDialect::instance()).parse( + make_shared(charStream), + false + ); if (!errors.empty()) { string message; for (auto const& err: errors) message += langutil::SourceReferenceFormatter::formatErrorInformation( *err, - SingletonCharStreamProvider(*scanner->charStream()) + SingletonCharStreamProvider(charStream) ); yulAssert(false, message); } diff --git a/test/liblangutil/Scanner.cpp b/test/liblangutil/Scanner.cpp index b8d51aca5..51d051236 100644 --- a/test/liblangutil/Scanner.cpp +++ b/test/liblangutil/Scanner.cpp @@ -34,13 +34,15 @@ BOOST_AUTO_TEST_SUITE(ScannerTest) BOOST_AUTO_TEST_CASE(test_empty) { - Scanner scanner(CharStream{}); + CharStream stream{}; + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::EOS); } BOOST_AUTO_TEST_CASE(smoke_test) { - Scanner scanner(CharStream("function break;765 \t \"string1\",'string2'\nidentifier1", "")); + CharStream stream("function break;765 \t \"string1\",'string2'\nidentifier1", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Break); BOOST_CHECK_EQUAL(scanner.next(), Token::Semicolon); @@ -58,7 +60,8 @@ BOOST_AUTO_TEST_CASE(smoke_test) BOOST_AUTO_TEST_CASE(assembly_assign) { - Scanner scanner(CharStream("let a := 1", "")); + CharStream stream("let a := 1", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Let); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::AssemblyAssign); @@ -69,7 +72,8 @@ BOOST_AUTO_TEST_CASE(assembly_assign) BOOST_AUTO_TEST_CASE(assembly_multiple_assign) { - Scanner scanner(CharStream("let a, b, c := 1", "")); + CharStream stream("let a, b, c := 1", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Let); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); @@ -89,14 +93,16 @@ BOOST_AUTO_TEST_CASE(string_printable) // Escape \ and " (since we are quoting with ") if (v == '\\' || v == '"') lit = string{'\\'} + lit; - Scanner scanner(CharStream(" { \"" + lit + "\"", "")); + CharStream stream(" { \"" + lit + "\"", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), string{static_cast(v)}); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } // Special case of unescaped " for strings quoted with ' - Scanner scanner(CharStream(" { '\"'", "")); + CharStream stream(" { '\"'", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "\""); @@ -110,7 +116,8 @@ BOOST_AUTO_TEST_CASE(string_nonprintable) if (v >= 0x20 && v <= 0x7e) continue; string lit{static_cast(v)}; - Scanner scanner(CharStream(" { \"" + lit + "\"", "")); + CharStream stream(" { \"" + lit + "\"", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); if (v == '\n' || v == '\v' || v == '\f' || v == '\r') @@ -123,7 +130,8 @@ BOOST_AUTO_TEST_CASE(string_nonprintable) BOOST_AUTO_TEST_CASE(string_escapes) { - Scanner scanner(CharStream(" { \"a\\x61\"", "")); + CharStream stream(" { \"a\\x61\"", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "aa"); @@ -131,25 +139,46 @@ BOOST_AUTO_TEST_CASE(string_escapes) BOOST_AUTO_TEST_CASE(string_escapes_all) { - Scanner scanner(CharStream(" { \"a\\x61\\n\\r\\t\"", "")); + CharStream stream(" { \"a\\x61\\n\\r\\t\"", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "aa\n\r\t"); } +struct TestScanner +{ + unique_ptr stream; + unique_ptr scanner; + explicit TestScanner(string _text) { reset(move(_text)); } + + void reset(std::string _text) + { + stream = make_unique(move(_text), ""); + scanner = make_unique(*stream); + } + + decltype(auto) currentToken() { return scanner->currentToken(); } + decltype(auto) next() { return scanner->next(); } + decltype(auto) currentError() { return scanner->currentError(); } + decltype(auto) currentLiteral() { return scanner->currentLiteral(); } + decltype(auto) currentCommentLiteral() { return scanner->currentCommentLiteral(); } + decltype(auto) currentLocation() { return scanner->currentLocation(); } +}; + BOOST_AUTO_TEST_CASE(string_escapes_legal_before_080) { - Scanner scanner(CharStream(" { \"a\\b", "")); + TestScanner scanner(" { \"a\\b"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalEscapeSequence); BOOST_CHECK_EQUAL(scanner.currentLiteral(), ""); - scanner.reset(CharStream(" { \"a\\f", "")); + scanner.reset(" { \"a\\f"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalEscapeSequence); BOOST_CHECK_EQUAL(scanner.currentLiteral(), ""); - scanner.reset(CharStream(" { \"a\\v", "")); + scanner.reset(" { \"a\\v"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalEscapeSequence); @@ -158,7 +187,7 @@ BOOST_AUTO_TEST_CASE(string_escapes_legal_before_080) BOOST_AUTO_TEST_CASE(string_escapes_with_zero) { - Scanner scanner(CharStream(" { \"a\\x61\\x00abc\"", "")); + TestScanner scanner(" { \"a\\x61\\x00abc\""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("aa\0abc", 6)); @@ -166,7 +195,8 @@ BOOST_AUTO_TEST_CASE(string_escapes_with_zero) BOOST_AUTO_TEST_CASE(string_escape_illegal) { - Scanner scanner(CharStream(" bla \"\\x6rf\" (illegalescape)", "")); + CharStream stream(" bla \"\\x6rf\" (illegalescape)", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalEscapeSequence); @@ -180,7 +210,7 @@ BOOST_AUTO_TEST_CASE(string_escape_illegal) BOOST_AUTO_TEST_CASE(hex_numbers) { - Scanner scanner(CharStream("var x = 0x765432536763762734623472346;", "")); + TestScanner scanner("var x = 0x765432536763762734623472346;"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Assign); @@ -188,34 +218,35 @@ BOOST_AUTO_TEST_CASE(hex_numbers) BOOST_CHECK_EQUAL(scanner.currentLiteral(), "0x765432536763762734623472346"); BOOST_CHECK_EQUAL(scanner.next(), Token::Semicolon); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream("0x1234", "")); + scanner.reset("0x1234"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "0x1234"); - scanner.reset(CharStream("0X1234", "")); + scanner.reset("0X1234"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); } BOOST_AUTO_TEST_CASE(octal_numbers) { - Scanner scanner(CharStream("07", "")); + TestScanner scanner("07"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); - scanner.reset(CharStream("007", "")); + scanner.reset("007"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); - scanner.reset(CharStream("-07", "")); + scanner.reset("-07"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Sub); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); - scanner.reset(CharStream("-.07", "")); + scanner.reset("-.07"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Sub); BOOST_CHECK_EQUAL(scanner.next(), Token::Number); - scanner.reset(CharStream("0", "")); + scanner.reset("0"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); - scanner.reset(CharStream("0.1", "")); + scanner.reset("0.1"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); } BOOST_AUTO_TEST_CASE(scientific_notation) { - Scanner scanner(CharStream("var x = 2e10;", "")); + CharStream stream("var x = 2e10;", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Assign); @@ -227,14 +258,14 @@ BOOST_AUTO_TEST_CASE(scientific_notation) BOOST_AUTO_TEST_CASE(leading_dot_in_identifier) { - Scanner scanner(CharStream("function .a(", "")); + TestScanner scanner("function .a("); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Period); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream("function .a(", "")); - scanner.setScannerMode(ScannerKind::Yul); + scanner.reset("function .a("); + scanner.scanner->setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Period); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); @@ -244,7 +275,7 @@ BOOST_AUTO_TEST_CASE(leading_dot_in_identifier) BOOST_AUTO_TEST_CASE(middle_dot_in_identifier) { - Scanner scanner(CharStream("function a..a(", "")); + TestScanner scanner("function a..a("); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Period); @@ -252,8 +283,8 @@ BOOST_AUTO_TEST_CASE(middle_dot_in_identifier) BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream("function a...a(", "")); - scanner.setScannerMode(ScannerKind::Yul); + scanner.reset("function a...a("); + scanner.scanner->setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); @@ -262,14 +293,14 @@ BOOST_AUTO_TEST_CASE(middle_dot_in_identifier) BOOST_AUTO_TEST_CASE(trailing_dot_in_identifier) { - Scanner scanner(CharStream("function a.(", "")); + TestScanner scanner("function a.("); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Period); BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream("function a.(", "")); - scanner.setScannerMode(ScannerKind::Yul); + scanner.reset("function a.("); + scanner.scanner->setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); @@ -278,19 +309,19 @@ BOOST_AUTO_TEST_CASE(trailing_dot_in_identifier) BOOST_AUTO_TEST_CASE(trailing_dot_in_numbers) { - Scanner scanner(CharStream("2.5", "")); + TestScanner scanner("2.5"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream("2.5e10", "")); + scanner.reset("2.5e10"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream(".5", "")); + scanner.reset(".5"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream(".5e10", "")); + scanner.reset(".5e10"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream("2.", "")); + scanner.reset("2."); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::Period); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); @@ -299,7 +330,8 @@ BOOST_AUTO_TEST_CASE(trailing_dot_in_numbers) BOOST_AUTO_TEST_CASE(leading_underscore_decimal_is_identifier) { // Actual error is cought by SyntaxChecker. - Scanner scanner(CharStream("_1.2", "")); + CharStream stream("_1.2", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); @@ -308,11 +340,11 @@ BOOST_AUTO_TEST_CASE(leading_underscore_decimal_is_identifier) BOOST_AUTO_TEST_CASE(leading_underscore_decimal_after_dot_illegal) { // Actual error is cought by SyntaxChecker. - Scanner scanner(CharStream("1._2", "")); + TestScanner scanner("1._2"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream("1._", "")); + scanner.reset("1._"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } @@ -320,7 +352,8 @@ BOOST_AUTO_TEST_CASE(leading_underscore_decimal_after_dot_illegal) BOOST_AUTO_TEST_CASE(leading_underscore_exp_are_identifier) { // Actual error is cought by SyntaxChecker. - Scanner scanner(CharStream("_1e2", "")); + CharStream stream("_1e2", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } @@ -328,7 +361,8 @@ BOOST_AUTO_TEST_CASE(leading_underscore_exp_are_identifier) BOOST_AUTO_TEST_CASE(leading_underscore_exp_after_e_illegal) { // Actual error is cought by SyntaxChecker. - Scanner scanner(CharStream("1e_2", "")); + CharStream stream("1e_2", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "1e_2"); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); @@ -336,7 +370,8 @@ BOOST_AUTO_TEST_CASE(leading_underscore_exp_after_e_illegal) BOOST_AUTO_TEST_CASE(leading_underscore_hex_illegal) { - Scanner scanner(CharStream("0x_abc", "")); + CharStream stream("0x_abc", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); @@ -345,7 +380,8 @@ BOOST_AUTO_TEST_CASE(leading_underscore_hex_illegal) BOOST_AUTO_TEST_CASE(fixed_number_invalid_underscore_front) { // Actual error is cought by SyntaxChecker. - Scanner scanner(CharStream("12._1234_1234", "")); + CharStream stream("12._1234_1234", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } @@ -353,22 +389,22 @@ BOOST_AUTO_TEST_CASE(fixed_number_invalid_underscore_front) BOOST_AUTO_TEST_CASE(number_literals_with_trailing_underscore_at_eos) { // Actual error is cought by SyntaxChecker. - Scanner scanner(CharStream("0x123_", "")); + TestScanner scanner("0x123_"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream("123_", "")); + scanner.reset("123_"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream("12.34_", "")); + scanner.reset("12.34_"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } BOOST_AUTO_TEST_CASE(negative_numbers) { - Scanner scanner(CharStream("var x = -.2 + -0x78 + -7.3 + 8.9 + 2e-2;", "")); + TestScanner scanner("var x = -.2 + -0x78 + -7.3 + 8.9 + 2e-2;"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Assign); @@ -395,7 +431,7 @@ BOOST_AUTO_TEST_CASE(negative_numbers) BOOST_AUTO_TEST_CASE(locations) { - Scanner scanner(CharStream("function_identifier has ; -0x743/*comment*/\n ident //comment", "")); + TestScanner scanner("function_identifier has ; -0x743/*comment*/\n ident //comment"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.currentLocation().start, 0); BOOST_CHECK_EQUAL(scanner.currentLocation().end, 19); @@ -418,7 +454,7 @@ BOOST_AUTO_TEST_CASE(locations) BOOST_AUTO_TEST_CASE(ambiguities) { // test scanning of some operators which need look-ahead - Scanner scanner(CharStream("<=" "<" "+ +=a++ =>" "<<" ">>" " >>=" ">>>" ">>>=" " >>>>>=><<=", "")); + TestScanner scanner("<=" "<" "+ +=a++ =>" "<<" ">>" " >>=" ">>>" ">>>=" " >>>>>=><<="); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LessThanOrEqual); BOOST_CHECK_EQUAL(scanner.next(), Token::LessThan); BOOST_CHECK_EQUAL(scanner.next(), Token::Add); @@ -440,21 +476,21 @@ BOOST_AUTO_TEST_CASE(ambiguities) BOOST_AUTO_TEST_CASE(documentation_comments_parsed_begin) { - Scanner scanner(CharStream("/// Send $(value / 1000) chocolates to the user", "")); + TestScanner scanner("/// Send $(value / 1000) chocolates to the user"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::EOS); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "Send $(value / 1000) chocolates to the user"); } BOOST_AUTO_TEST_CASE(multiline_documentation_comments_parsed_begin) { - Scanner scanner(CharStream("/** Send $(value / 1000) chocolates to the user*/", "")); + TestScanner scanner("/** Send $(value / 1000) chocolates to the user*/"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::EOS); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "Send $(value / 1000) chocolates to the user"); } BOOST_AUTO_TEST_CASE(documentation_comments_parsed) { - Scanner scanner(CharStream("some other tokens /// Send $(value / 1000) chocolates to the user", "")); + TestScanner scanner("some other tokens /// Send $(value / 1000) chocolates to the user"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); @@ -464,9 +500,9 @@ BOOST_AUTO_TEST_CASE(documentation_comments_parsed) BOOST_AUTO_TEST_CASE(multiline_documentation_comments_parsed) { - Scanner scanner(CharStream("some other tokens /**\n" - "* Send $(value / 1000) chocolates to the user\n" - "*/", "")); + TestScanner scanner("some other tokens /**\n" + "* Send $(value / 1000) chocolates to the user\n" + "*/"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); @@ -476,9 +512,9 @@ BOOST_AUTO_TEST_CASE(multiline_documentation_comments_parsed) BOOST_AUTO_TEST_CASE(multiline_documentation_no_stars) { - Scanner scanner(CharStream("some other tokens /**\n" - " Send $(value / 1000) chocolates to the user\n" - "*/", "")); + TestScanner scanner("some other tokens /**\n" + " Send $(value / 1000) chocolates to the user\n" + "*/"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); @@ -488,9 +524,9 @@ BOOST_AUTO_TEST_CASE(multiline_documentation_no_stars) BOOST_AUTO_TEST_CASE(multiline_documentation_whitespace_hell) { - Scanner scanner(CharStream("some other tokens /** \t \r \n" - "\t \r * Send $(value / 1000) chocolates to the user\n" - "*/", "")); + TestScanner scanner("some other tokens /** \t \r \n" + "\t \r * Send $(value / 1000) chocolates to the user\n" + "*/"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); @@ -500,37 +536,37 @@ BOOST_AUTO_TEST_CASE(multiline_documentation_whitespace_hell) BOOST_AUTO_TEST_CASE(comment_before_eos) { - Scanner scanner(CharStream("//", "")); + TestScanner scanner("//"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::EOS); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), ""); } BOOST_AUTO_TEST_CASE(documentation_comment_before_eos) { - Scanner scanner(CharStream("///", "")); + TestScanner scanner("///"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::EOS); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), ""); } BOOST_AUTO_TEST_CASE(empty_multiline_comment) { - Scanner scanner(CharStream("/**/", "")); + TestScanner scanner("/**/"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::EOS); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), ""); } BOOST_AUTO_TEST_CASE(empty_multiline_documentation_comment_before_eos) { - Scanner scanner(CharStream("/***/", "")); + TestScanner scanner("/***/"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::EOS); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), ""); } BOOST_AUTO_TEST_CASE(comments_mixed_in_sequence) { - Scanner scanner(CharStream("hello_world ///documentation comment \n" - "//simple comment \n" - "<<", "")); + TestScanner scanner("hello_world ///documentation comment \n" + "//simple comment \n" + "<<"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::SHL); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "documentation comment "); @@ -538,7 +574,7 @@ BOOST_AUTO_TEST_CASE(comments_mixed_in_sequence) BOOST_AUTO_TEST_CASE(ether_subdenominations) { - Scanner scanner(CharStream("wei gwei ether", "")); + TestScanner scanner("wei gwei ether"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::SubWei); BOOST_CHECK_EQUAL(scanner.next(), Token::SubGwei); BOOST_CHECK_EQUAL(scanner.next(), Token::SubEther); @@ -546,7 +582,7 @@ BOOST_AUTO_TEST_CASE(ether_subdenominations) BOOST_AUTO_TEST_CASE(time_subdenominations) { - Scanner scanner(CharStream("seconds minutes hours days weeks years", "")); + TestScanner scanner("seconds minutes hours days weeks years"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::SubSecond); BOOST_CHECK_EQUAL(scanner.next(), Token::SubMinute); BOOST_CHECK_EQUAL(scanner.next(), Token::SubHour); @@ -557,7 +593,7 @@ BOOST_AUTO_TEST_CASE(time_subdenominations) BOOST_AUTO_TEST_CASE(empty_comment) { - Scanner scanner(CharStream("//\ncontract{}", "")); + TestScanner scanner("//\ncontract{}"); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), ""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Contract); BOOST_CHECK_EQUAL(scanner.next(), Token::LBrace); @@ -569,7 +605,7 @@ BOOST_AUTO_TEST_CASE(empty_comment) BOOST_AUTO_TEST_CASE(valid_unicode_string_escape) { - Scanner scanner(CharStream("{ \"\\u00DAnicode\"", "")); + TestScanner scanner("{ \"\\u00DAnicode\""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("\xC3\x9Anicode", 8)); @@ -577,7 +613,7 @@ BOOST_AUTO_TEST_CASE(valid_unicode_string_escape) BOOST_AUTO_TEST_CASE(valid_unicode_string_escape_7f) { - Scanner scanner(CharStream("{ \"\\u007Fnicode\"", "")); + TestScanner scanner("{ \"\\u007Fnicode\""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("\x7Fnicode", 7)); @@ -585,7 +621,7 @@ BOOST_AUTO_TEST_CASE(valid_unicode_string_escape_7f) BOOST_AUTO_TEST_CASE(valid_unicode_string_escape_7ff) { - Scanner scanner(CharStream("{ \"\\u07FFnicode\"", "")); + TestScanner scanner("{ \"\\u07FFnicode\""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("\xDF\xBFnicode", 8)); @@ -593,7 +629,7 @@ BOOST_AUTO_TEST_CASE(valid_unicode_string_escape_7ff) BOOST_AUTO_TEST_CASE(valid_unicode_string_escape_ffff) { - Scanner scanner(CharStream("{ \"\\uFFFFnicode\"", "")); + TestScanner scanner("{ \"\\uFFFFnicode\""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("\xEF\xBF\xBFnicode", 9)); @@ -601,7 +637,7 @@ BOOST_AUTO_TEST_CASE(valid_unicode_string_escape_ffff) BOOST_AUTO_TEST_CASE(invalid_short_unicode_string_escape) { - Scanner scanner(CharStream("{ \"\\uFFnicode\"", "")); + TestScanner scanner("{ \"\\uFFnicode\""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); } @@ -610,12 +646,12 @@ BOOST_AUTO_TEST_CASE(invalid_short_unicode_string_escape) BOOST_AUTO_TEST_CASE(unicode_prefix_only) { - Scanner scanner(CharStream("{ unicode", "")); + TestScanner scanner("{ unicode"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); - scanner.reset(CharStream("{ unicode", "")); - scanner.setScannerMode(ScannerKind::Yul); + scanner.reset("{ unicode"); + scanner.scanner->setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "unicode"); @@ -623,7 +659,7 @@ BOOST_AUTO_TEST_CASE(unicode_prefix_only) BOOST_AUTO_TEST_CASE(unicode_invalid_space) { - Scanner scanner(CharStream("{ unicode ", "")); + TestScanner scanner("{ unicode "); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); @@ -631,12 +667,12 @@ BOOST_AUTO_TEST_CASE(unicode_invalid_space) BOOST_AUTO_TEST_CASE(unicode_invalid_token) { - Scanner scanner(CharStream("{ unicode test", "")); + TestScanner scanner("{ unicode test"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); - scanner.reset(CharStream("{ unicode test", "")); - scanner.setScannerMode(ScannerKind::Yul); + scanner.reset("{ unicode test"); + scanner.scanner->setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "unicode"); @@ -646,7 +682,7 @@ BOOST_AUTO_TEST_CASE(unicode_invalid_token) BOOST_AUTO_TEST_CASE(valid_unicode_literal) { - Scanner scanner(CharStream("{ unicode\"Hello 😃\"", "")); + TestScanner scanner("{ unicode\"Hello 😃\""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::UnicodeStringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("Hello \xf0\x9f\x98\x83", 10)); @@ -655,7 +691,7 @@ BOOST_AUTO_TEST_CASE(valid_unicode_literal) BOOST_AUTO_TEST_CASE(valid_nonprintable_in_unicode_literal) { // Non-printable characters are allowed in unicode strings... - Scanner scanner(CharStream("{ unicode\"Hello \007😃\"", "")); + TestScanner scanner("{ unicode\"Hello \007😃\""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::UnicodeStringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("Hello \x07\xf0\x9f\x98\x83", 11)); @@ -665,19 +701,19 @@ BOOST_AUTO_TEST_CASE(valid_nonprintable_in_unicode_literal) BOOST_AUTO_TEST_CASE(hex_prefix_only) { - Scanner scanner(CharStream("{ hex", "")); + TestScanner scanner("{ hex"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); - scanner.reset(CharStream("{ hex", "")); - scanner.setScannerMode(ScannerKind::Yul); + scanner.reset("{ hex"); + scanner.scanner->setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); } BOOST_AUTO_TEST_CASE(hex_invalid_space) { - Scanner scanner(CharStream("{ hex ", "")); + TestScanner scanner("{ hex "); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); @@ -685,12 +721,12 @@ BOOST_AUTO_TEST_CASE(hex_invalid_space) BOOST_AUTO_TEST_CASE(hex_invalid_token) { - Scanner scanner(CharStream("{ hex test", "")); + TestScanner scanner("{ hex test"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); - scanner.reset(CharStream("{ hex test", "")); - scanner.setScannerMode(ScannerKind::Yul); + scanner.reset("{ hex test"); + scanner.scanner->setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); @@ -698,7 +734,7 @@ BOOST_AUTO_TEST_CASE(hex_invalid_token) BOOST_AUTO_TEST_CASE(valid_hex_literal) { - Scanner scanner(CharStream("{ hex\"00112233FF\"", "")); + TestScanner scanner("{ hex\"00112233FF\""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::HexStringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("\x00\x11\x22\x33\xFF", 5)); @@ -706,7 +742,7 @@ BOOST_AUTO_TEST_CASE(valid_hex_literal) BOOST_AUTO_TEST_CASE(invalid_short_hex_literal) { - Scanner scanner(CharStream("{ hex\"00112233F\"", "")); + TestScanner scanner("{ hex\"00112233F\""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalHexString); @@ -714,7 +750,7 @@ BOOST_AUTO_TEST_CASE(invalid_short_hex_literal) BOOST_AUTO_TEST_CASE(invalid_hex_literal_with_space) { - Scanner scanner(CharStream("{ hex\"00112233FF \"", "")); + TestScanner scanner("{ hex\"00112233FF \""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalHexString); @@ -722,7 +758,7 @@ BOOST_AUTO_TEST_CASE(invalid_hex_literal_with_space) BOOST_AUTO_TEST_CASE(invalid_hex_literal_with_wrong_quotes) { - Scanner scanner(CharStream("{ hex\"00112233FF'", "")); + TestScanner scanner("{ hex\"00112233FF'"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalHexString); @@ -730,7 +766,7 @@ BOOST_AUTO_TEST_CASE(invalid_hex_literal_with_wrong_quotes) BOOST_AUTO_TEST_CASE(invalid_hex_literal_nonhex_string) { - Scanner scanner(CharStream("{ hex\"hello\"", "")); + TestScanner scanner("{ hex\"hello\""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalHexString); @@ -741,7 +777,7 @@ BOOST_AUTO_TEST_CASE(invalid_hex_literal_nonhex_string) BOOST_AUTO_TEST_CASE(invalid_multiline_comment_close) { // This used to parse as "comment", "identifier" - Scanner scanner(CharStream("/** / x", "")); + TestScanner scanner("/** / x"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } @@ -749,14 +785,14 @@ BOOST_AUTO_TEST_CASE(invalid_multiline_comment_close) BOOST_AUTO_TEST_CASE(multiline_doc_comment_at_eos) { // This used to parse as "whitespace" - Scanner scanner(CharStream("/**", "")); + TestScanner scanner("/**"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } BOOST_AUTO_TEST_CASE(multiline_comment_at_eos) { - Scanner scanner(CharStream("/*", "")); + TestScanner scanner("/*"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } @@ -765,7 +801,7 @@ BOOST_AUTO_TEST_CASE(regular_line_break_in_single_line_comment) { for (auto const& nl: {"\r", "\n", "\r\n"}) { - Scanner scanner(CharStream("// abc " + string(nl) + " def ", "")); + TestScanner scanner("// abc " + string(nl) + " def "); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), ""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "def"); @@ -777,7 +813,7 @@ BOOST_AUTO_TEST_CASE(irregular_line_breaks_in_single_line_comment) { for (auto const& nl: {"\v", "\f", "\xE2\x80\xA8", "\xE2\x80\xA9"}) { - Scanner scanner(CharStream("// abc " + string(nl) + " def ", "")); + TestScanner scanner("// abc " + string(nl) + " def "); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), ""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); for (size_t i = 0; i < string(nl).size() - 1; i++) @@ -792,7 +828,7 @@ BOOST_AUTO_TEST_CASE(regular_line_breaks_in_single_line_doc_comment) { for (auto const& nl: {"\r", "\n", "\r\n"}) { - Scanner scanner(CharStream("/// abc " + string(nl) + " def ", "")); + TestScanner scanner("/// abc " + string(nl) + " def "); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "abc "); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "def"); @@ -806,7 +842,7 @@ BOOST_AUTO_TEST_CASE(regular_line_breaks_in_multiline_doc_comment) // Any accepted non-LF is being canonicalized to LF. for (auto const& nl : {"\r"s, "\n"s, "\r\n"s}) { - Scanner scanner{CharStream{"/// Hello" + nl + "/// World" + nl + "ident", ""}}; + TestScanner scanner{"/// Hello" + nl + "/// World" + nl + "ident"}; auto const& lit = scanner.currentCommentLiteral(); BOOST_CHECK_EQUAL(lit, "Hello\n World"); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "Hello\n World"); @@ -820,7 +856,7 @@ BOOST_AUTO_TEST_CASE(irregular_line_breaks_in_single_line_doc_comment) { for (auto const& nl: {"\v", "\f", "\xE2\x80\xA8", "\xE2\x80\xA9"}) { - Scanner scanner(CharStream("/// abc " + string(nl) + " def ", "")); + TestScanner scanner("/// abc " + string(nl) + " def "); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "abc "); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); for (size_t i = 0; i < string(nl).size() - 1; i++) @@ -835,7 +871,7 @@ BOOST_AUTO_TEST_CASE(regular_line_breaks_in_strings) { for (auto const& nl: {"\r"s, "\n"s, "\r\n"s}) { - Scanner scanner(CharStream("\"abc " + nl + " def\"", "")); + TestScanner scanner("\"abc " + nl + " def\""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "def"); @@ -848,7 +884,7 @@ BOOST_AUTO_TEST_CASE(irregular_line_breaks_in_strings) { for (auto const& nl: {"\v", "\f", "\xE2\x80\xA8", "\xE2\x80\xA9"}) { - Scanner scanner(CharStream("\"abc " + string(nl) + " def\"", "")); + TestScanner scanner("\"abc " + string(nl) + " def\""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); for (size_t i = 0; i < string(nl).size(); i++) BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); @@ -863,7 +899,7 @@ BOOST_AUTO_TEST_CASE(solidity_keywords) { // These are tokens which have a different meaning in Yul. string keywords = "return byte bool address var in true false leave switch case default"; - Scanner scanner(CharStream(keywords, "")); + TestScanner scanner(keywords); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Return); BOOST_CHECK_EQUAL(scanner.next(), Token::Byte); BOOST_CHECK_EQUAL(scanner.next(), Token::Bool); @@ -877,8 +913,8 @@ BOOST_AUTO_TEST_CASE(solidity_keywords) BOOST_CHECK_EQUAL(scanner.next(), Token::Case); BOOST_CHECK_EQUAL(scanner.next(), Token::Default); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream(keywords, "")); - scanner.setScannerMode(ScannerKind::Yul); + scanner.reset(keywords); + scanner.scanner->setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); @@ -896,28 +932,28 @@ BOOST_AUTO_TEST_CASE(solidity_keywords) BOOST_AUTO_TEST_CASE(yul_keyword_like) { - Scanner scanner(CharStream("leave.function", "")); + TestScanner scanner("leave.function"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Period); BOOST_CHECK_EQUAL(scanner.next(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream("leave.function", "")); - scanner.setScannerMode(ScannerKind::Yul); + scanner.reset("leave.function"); + scanner.scanner->setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } BOOST_AUTO_TEST_CASE(yul_identifier_with_dots) { - Scanner scanner(CharStream("mystorage.slot := 1", "")); + TestScanner scanner("mystorage.slot := 1"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Period); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::AssemblyAssign); BOOST_CHECK_EQUAL(scanner.next(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream("mystorage.slot := 1", "")); - scanner.setScannerMode(ScannerKind::Yul); + scanner.reset("mystorage.slot := 1"); + scanner.scanner->setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::AssemblyAssign); BOOST_CHECK_EQUAL(scanner.next(), Token::Number); @@ -927,7 +963,7 @@ BOOST_AUTO_TEST_CASE(yul_identifier_with_dots) BOOST_AUTO_TEST_CASE(yul_function) { string sig = "function f(a, b) -> x, y"; - Scanner scanner(CharStream(sig, "")); + TestScanner scanner(sig); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); @@ -940,8 +976,8 @@ BOOST_AUTO_TEST_CASE(yul_function) BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream(sig, "")); - scanner.setScannerMode(ScannerKind::Yul); + scanner.reset(sig); + scanner.scanner->setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); @@ -959,7 +995,7 @@ BOOST_AUTO_TEST_CASE(yul_function) BOOST_AUTO_TEST_CASE(yul_function_with_whitespace) { string sig = "function f (a, b) - > x, y"; - Scanner scanner(CharStream(sig, "")); + TestScanner scanner(sig); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); @@ -973,8 +1009,8 @@ BOOST_AUTO_TEST_CASE(yul_function_with_whitespace) BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream(sig, "")); - scanner.setScannerMode(ScannerKind::Yul); + scanner.reset(sig); + scanner.scanner->setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index cfa25e813..76b1ba2a6 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -26,7 +26,8 @@ #include #include -#include +#include + #include #include #include @@ -58,7 +59,7 @@ evmasm::AssemblyItems compileContract(std::shared_ptr _sourceCode) ErrorReporter errorReporter(errors); Parser parser(errorReporter, solidity::test::CommonOptions::get().evmVersion()); ASTPointer sourceUnit; - BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared(_sourceCode))); + BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(*_sourceCode)); BOOST_CHECK(!!sourceUnit); Scoper::assignScopes(*sourceUnit); diff --git a/test/libsolidity/SemVerMatcher.cpp b/test/libsolidity/SemVerMatcher.cpp index 7206bc251..b0fbb8547 100644 --- a/test/libsolidity/SemVerMatcher.cpp +++ b/test/libsolidity/SemVerMatcher.cpp @@ -43,7 +43,8 @@ namespace SemVerMatchExpression parseExpression(string const& _input) { - Scanner scanner{CharStream(_input, "")}; + CharStream stream(_input, ""); + Scanner scanner{stream}; vector literals; vector tokens; while (scanner.currentToken() != Token::EOS) diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 748718bec..ee1bf0239 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -101,15 +101,14 @@ bytes compileFirstExpression( ) { string sourceCode = "pragma solidity >=0.0; // SPDX-License-Identifier: GPL-3\n" + _sourceCode; + CharStream stream(sourceCode, ""); ASTPointer sourceUnit; try { ErrorList errors; ErrorReporter errorReporter(errors); - sourceUnit = Parser(errorReporter, solidity::test::CommonOptions::get().evmVersion()).parse( - make_shared(CharStream(sourceCode, "")) - ); + sourceUnit = Parser(errorReporter, solidity::test::CommonOptions::get().evmVersion()).parse(stream); if (!sourceUnit) return bytes(); } diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 48d87c6bb..58024d9a6 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -42,11 +42,12 @@ namespace ASTPointer parseText(std::string const& _source, ErrorList& _errors, bool errorRecovery = false) { ErrorReporter errorReporter(_errors); + auto charStream = CharStream(_source, ""); ASTPointer sourceUnit = Parser( errorReporter, solidity::test::CommonOptions::get().evmVersion(), errorRecovery - ).parse(std::make_shared(CharStream(_source, ""))); + ).parse(charStream); if (!sourceUnit) return ASTPointer(); for (ASTPointer const& node: sourceUnit->nodes()) diff --git a/test/libyul/Common.cpp b/test/libyul/Common.cpp index 853b7dafc..8099037f3 100644 --- a/test/libyul/Common.cpp +++ b/test/libyul/Common.cpp @@ -74,7 +74,8 @@ pair, shared_ptr> yul::test::parse( ) { ErrorReporter errorReporter(_errors); - shared_ptr scanner = make_shared(CharStream(_source, "")); + CharStream stream(_source, ""); + shared_ptr scanner = make_shared(stream); shared_ptr parserResult = yul::ObjectParser(errorReporter, _dialect).parse(scanner, false); if (!parserResult) return {}; diff --git a/test/libyul/ObjectParser.cpp b/test/libyul/ObjectParser.cpp index 946538078..bedb6230a 100644 --- a/test/libyul/ObjectParser.cpp +++ b/test/libyul/ObjectParser.cpp @@ -23,6 +23,8 @@ #include +#include + #include #include @@ -118,7 +120,8 @@ tuple, ErrorList> tryGetSourceLocationMapp ErrorReporter reporter(errors); Dialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(EVMVersion::berlin()); ObjectParser objectParser{reporter, dialect}; - objectParser.parse(make_shared(CharStream(move(source), "")), false); + CharStream stream(move(source), ""); + objectParser.parse(make_shared(stream), false); return {objectParser.sourceNameMapping(), std::move(errors)}; } diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp index 553e84d4b..3cea20dde 100644 --- a/test/libyul/Parser.cpp +++ b/test/libyul/Parser.cpp @@ -55,7 +55,8 @@ shared_ptr parse(string const& _source, Dialect const& _dialect, ErrorRep { try { - auto scanner = make_shared(CharStream(_source, "")); + auto stream = CharStream(_source, ""); + auto scanner = make_shared(stream); map> indicesToSourceNames; indicesToSourceNames[0] = make_shared("source0"); indicesToSourceNames[1] = make_shared("source1"); diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index aaf7cc516..284561473 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -82,7 +82,7 @@ public: { SourceReferenceFormatter{ cerr, - SingletonCharStreamProvider(*m_scanner->charStream()), + SingletonCharStreamProvider(*m_charStream), true, false }.printErrorInformation(m_errors); @@ -91,7 +91,8 @@ public: bool parse(string const& _input) { ErrorReporter errorReporter(m_errors); - m_scanner = make_shared(CharStream(_input, "")); + m_charStream = make_shared(_input, ""); + m_scanner = make_shared(*m_charStream); m_ast = yul::Parser(errorReporter, m_dialect).parse(m_scanner, false); if (!m_ast || !errorReporter.errors().empty()) { @@ -234,6 +235,7 @@ public: private: ErrorList m_errors; + shared_ptr m_charStream; shared_ptr m_scanner; shared_ptr m_ast; Dialect const& m_dialect{EVMDialect::strictAssemblyForEVMObjects(EVMVersion{})}; diff --git a/tools/yulPhaser/Program.cpp b/tools/yulPhaser/Program.cpp index 2635b27fa..5c61701d8 100644 --- a/tools/yulPhaser/Program.cpp +++ b/tools/yulPhaser/Program.cpp @@ -120,7 +120,7 @@ variant, ErrorList> Program::parseObject(Dialect const& _diale { ErrorList errors; ErrorReporter errorReporter(errors); - auto scanner = make_shared(move(_source)); + auto scanner = make_shared(_source); ObjectParser parser(errorReporter, _dialect); shared_ptr object = parser.parse(scanner, false); From ac0c749a32a6660bb6e9134723b547538455ac63 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Tue, 3 Aug 2021 14:50:01 +0200 Subject: [PATCH 87/98] Eliminate some unnecessary header inclusions in headers in SourceLocation.h --- liblangutil/SourceLocation.cpp | 15 +++++++++++++++ liblangutil/SourceLocation.h | 21 ++++----------------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/liblangutil/SourceLocation.cpp b/liblangutil/SourceLocation.cpp index 058cc6b57..72f2506e8 100644 --- a/liblangutil/SourceLocation.cpp +++ b/liblangutil/SourceLocation.cpp @@ -21,6 +21,8 @@ #include #include +#include + using namespace solidity; using namespace solidity::langutil; using namespace std; @@ -50,3 +52,16 @@ SourceLocation solidity::langutil::parseSourceLocation(string const& _input, vec result.sourceName = _sourceNames.at(static_cast(sourceIndex)); return result; } + +std::ostream& solidity::langutil::operator<<(std::ostream& _out, SourceLocation const& _location) +{ + if (!_location.isValid()) + return _out << "NO_LOCATION_SPECIFIED"; + + if (_location.sourceName) + _out << *_location.sourceName; + + _out << "[" << _location.start << "," << _location.end << "]"; + + return _out; +} diff --git a/liblangutil/SourceLocation.h b/liblangutil/SourceLocation.h index 845b269ca..66cef52fb 100644 --- a/liblangutil/SourceLocation.h +++ b/liblangutil/SourceLocation.h @@ -23,16 +23,14 @@ #pragma once -#include -#include - -#include +#include #include #include +#include +#include namespace solidity::langutil { -struct SourceLocationError: virtual util::Exception {}; /** * Representation of an interval of source positions. @@ -112,17 +110,6 @@ SourceLocation parseSourceLocation( ); /// Stream output for Location (used e.g. in boost exceptions). -inline std::ostream& operator<<(std::ostream& _out, SourceLocation const& _location) -{ - if (!_location.isValid()) - return _out << "NO_LOCATION_SPECIFIED"; - - if (_location.sourceName) - _out << *_location.sourceName; - - _out << "[" << _location.start << "," << _location.end << "]"; - - return _out; -} +std::ostream& operator<<(std::ostream& _out, SourceLocation const& _location); } From 6294aa871acb2694459d37abee3fb195d17f24be Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Tue, 3 Aug 2021 15:36:00 +0200 Subject: [PATCH 88/98] Simplify AsmParser::parse(.) usage --- libsolidity/codegen/CompilerContext.cpp | 4 ++-- libsolidity/interface/CompilerStack.cpp | 3 +-- libsolidity/parsing/Parser.cpp | 2 +- libyul/AsmParser.cpp | 15 ++++++++++----- libyul/AsmParser.h | 9 ++++++--- libyul/ObjectParser.cpp | 2 +- libyul/backends/wasm/EVMToEwasmTranslator.cpp | 5 +---- test/libyul/Parser.cpp | 4 +--- test/tools/yulopti.cpp | 6 +----- 9 files changed, 24 insertions(+), 26 deletions(-) diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 083dc570c..291bbc255 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -441,7 +441,7 @@ void CompilerContext::appendInlineAssembly( locationOverride = m_asm->currentSourceLocation(); shared_ptr parserResult = yul::Parser(errorReporter, dialect, std::move(locationOverride)) - .parse(make_shared(charStream), false); + .parse(charStream); #ifdef SOL_OUTPUT_ASM cout << yul::AsmPrinter(&dialect)(*parserResult) << endl; #endif @@ -492,7 +492,7 @@ void CompilerContext::appendInlineAssembly( m_generatedYulUtilityCode = yul::AsmPrinter(dialect)(*obj.code); string code = yul::AsmPrinter{dialect}(*obj.code); langutil::CharStream charStream(m_generatedYulUtilityCode, _sourceName); - obj.code = yul::Parser(errorReporter, dialect).parse(make_shared(charStream), false); + obj.code = yul::Parser(errorReporter, dialect).parse(charStream); *obj.analysisInfo = yul::AsmAnalyzer::analyzeStrictAssertCorrect(dialect, obj); } diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 757fdebc3..cbdfff454 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -755,9 +755,8 @@ Json::Value CompilerStack::generatedSources(string const& _contractName, bool _r ErrorList errors; ErrorReporter errorReporter(errors); CharStream charStream(source, sourceName); - shared_ptr scanner = make_shared(charStream); yul::EVMDialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(m_evmVersion); - shared_ptr parserResult = yul::Parser{errorReporter, dialect}.parse(scanner, false); + shared_ptr parserResult = yul::Parser{errorReporter, dialect}.parse(charStream); solAssert(parserResult, ""); sources[0]["ast"] = yul::AsmJsonConverter{sourceIndex}(*parserResult); sources[0]["name"] = sourceName; diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index c5a62c76e..efb31545a 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -1301,7 +1301,7 @@ ASTPointer Parser::parseInlineAssembly(ASTPointer con } yul::Parser asmParser(m_errorReporter, dialect); - shared_ptr block = asmParser.parse(m_scanner, true); + shared_ptr block = asmParser.parseInline(m_scanner); if (block == nullptr) BOOST_THROW_EXCEPTION(FatalError()); diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index b5e542926..d1465c737 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -85,7 +85,15 @@ std::shared_ptr Parser::createDebugData() const solAssert(false, ""); } -unique_ptr Parser::parse(std::shared_ptr const& _scanner, bool _reuseScanner) +unique_ptr Parser::parse(CharStream& _charStream) +{ + m_scanner = make_shared(_charStream); + unique_ptr block = parseInline(m_scanner); + expectToken(Token::EOS); + return block; +} + +unique_ptr Parser::parseInline(std::shared_ptr const& _scanner) { m_recursionDepth = 0; @@ -97,10 +105,7 @@ unique_ptr Parser::parse(std::shared_ptr const& _scanner, bool _ m_scanner = _scanner; if (m_sourceNames) fetchSourceLocationFromComment(); - auto block = make_unique(parseBlock()); - if (!_reuseScanner) - expectToken(Token::EOS); - return block; + return make_unique(parseBlock()); } catch (FatalError const&) { diff --git a/libyul/AsmParser.h b/libyul/AsmParser.h index c386bbe6f..da6eb7226 100644 --- a/libyul/AsmParser.h +++ b/libyul/AsmParser.h @@ -87,10 +87,13 @@ public: {} /// Parses an inline assembly block starting with `{` and ending with `}`. - /// @param _reuseScanner if true, do check for end of input after the `}`. /// @returns an empty shared pointer on error. - std::unique_ptr parse(std::shared_ptr const& _scanner, bool _reuseScanner); - // TODO: pass CharStream here instead ^^^^^^ + std::unique_ptr parseInline(std::shared_ptr const& _scanner); + + /// Parses an assembly block starting with `{` and ending with `}` + /// and expects end of input after the '}'. + /// @returns an empty shared pointer on error. + std::unique_ptr parse(langutil::CharStream& _charStream); protected: langutil::SourceLocation currentLocation() const override diff --git a/libyul/ObjectParser.cpp b/libyul/ObjectParser.cpp index 347096a3b..d100351b2 100644 --- a/libyul/ObjectParser.cpp +++ b/libyul/ObjectParser.cpp @@ -168,7 +168,7 @@ optional ObjectParser::tryParseSourceNameMapping() shared_ptr ObjectParser::parseBlock() { Parser parser(m_errorReporter, m_dialect, m_sourceNameMapping); - shared_ptr block = parser.parse(m_scanner, true); + shared_ptr block = parser.parseInline(m_scanner); yulAssert(block || m_errorReporter.hasErrors(), "Invalid block but no error!"); return block; } diff --git a/libyul/backends/wasm/EVMToEwasmTranslator.cpp b/libyul/backends/wasm/EVMToEwasmTranslator.cpp index 418d0dbcb..a5d355ede 100644 --- a/libyul/backends/wasm/EVMToEwasmTranslator.cpp +++ b/libyul/backends/wasm/EVMToEwasmTranslator.cpp @@ -136,10 +136,7 @@ void EVMToEwasmTranslator::parsePolyfill() string(solidity::yul::wasm::polyfill::Logical) + string(solidity::yul::wasm::polyfill::Memory) + "}", ""); - m_polyfill = Parser(errorReporter, WasmDialect::instance()).parse( - make_shared(charStream), - false - ); + m_polyfill = Parser(errorReporter, WasmDialect::instance()).parse(charStream); if (!errors.empty()) { string message; diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp index 3cea20dde..4a95f625e 100644 --- a/test/libyul/Parser.cpp +++ b/test/libyul/Parser.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include @@ -56,7 +55,6 @@ shared_ptr parse(string const& _source, Dialect const& _dialect, ErrorRep try { auto stream = CharStream(_source, ""); - auto scanner = make_shared(stream); map> indicesToSourceNames; indicesToSourceNames[0] = make_shared("source0"); indicesToSourceNames[1] = make_shared("source1"); @@ -65,7 +63,7 @@ shared_ptr parse(string const& _source, Dialect const& _dialect, ErrorRep errorReporter, _dialect, move(indicesToSourceNames) - ).parse(scanner, false); + ).parse(stream); if (parserResult) { yul::AsmAnalysisInfo analysisInfo; diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index 284561473..357311595 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -45,7 +44,6 @@ #include #include -#include #include #include @@ -92,8 +90,7 @@ public: { ErrorReporter errorReporter(m_errors); m_charStream = make_shared(_input, ""); - m_scanner = make_shared(*m_charStream); - m_ast = yul::Parser(errorReporter, m_dialect).parse(m_scanner, false); + m_ast = yul::Parser(errorReporter, m_dialect).parse(*m_charStream); if (!m_ast || !errorReporter.errors().empty()) { cerr << "Error parsing source." << endl; @@ -236,7 +233,6 @@ public: private: ErrorList m_errors; shared_ptr m_charStream; - shared_ptr m_scanner; shared_ptr m_ast; Dialect const& m_dialect{EVMDialect::strictAssemblyForEVMObjects(EVMVersion{})}; shared_ptr m_analysisInfo; From af18b8afc2cec97c76a46e32e18be0d747b02610 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Tue, 3 Aug 2021 15:20:00 +0200 Subject: [PATCH 89/98] Eliminate some unnecessary header inclusions in headers in Scanner.h and SourceReferenceFormatter.h --- liblangutil/Scanner.h | 2 -- liblangutil/SourceReferenceFormatter.cpp | 10 ++++++++++ liblangutil/SourceReferenceFormatter.h | 12 ++++-------- libyul/AsmParser.h | 1 + test/libyul/YulOptimizerTest.cpp | 1 + tools/yulPhaser/Phaser.cpp | 1 + 6 files changed, 17 insertions(+), 10 deletions(-) diff --git a/liblangutil/Scanner.h b/liblangutil/Scanner.h index ef12703ef..347c75779 100644 --- a/liblangutil/Scanner.h +++ b/liblangutil/Scanner.h @@ -55,8 +55,6 @@ #include #include #include -#include -#include #include #include diff --git a/liblangutil/SourceReferenceFormatter.cpp b/liblangutil/SourceReferenceFormatter.cpp index 2a0799036..20d0f3306 100644 --- a/liblangutil/SourceReferenceFormatter.cpp +++ b/liblangutil/SourceReferenceFormatter.cpp @@ -21,6 +21,8 @@ #include #include +#include +#include #include #include #include @@ -45,6 +47,14 @@ std::string replaceNonTabs(std::string_view _utf8Input, char _filler) } +std::string SourceReferenceFormatter::formatErrorInformation(Error const& _error, CharStream const& _charStream) +{ + return formatErrorInformation( + _error, + SingletonCharStreamProvider(_charStream) + ); +} + AnsiColorized SourceReferenceFormatter::normalColored() const { return AnsiColorized(m_stream, m_colored, {WHITE}); diff --git a/liblangutil/SourceReferenceFormatter.h b/liblangutil/SourceReferenceFormatter.h index 630f00de5..fc1f418a2 100644 --- a/liblangutil/SourceReferenceFormatter.h +++ b/liblangutil/SourceReferenceFormatter.h @@ -23,7 +23,6 @@ #include #include -#include #include @@ -33,6 +32,9 @@ namespace solidity::langutil { + +class CharStream; +class CharStreamProvider; struct SourceLocation; class SourceReferenceFormatter @@ -80,13 +82,7 @@ public: ); } - static std::string formatErrorInformation(Error const& _error, CharStream const& _charStream) - { - return formatErrorInformation( - _error, - SingletonCharStreamProvider(_charStream) - ); - } + static std::string formatErrorInformation(Error const& _error, CharStream const& _charStream); private: util::AnsiColorized normalColored() const; diff --git a/libyul/AsmParser.h b/libyul/AsmParser.h index cfde8bec4..3f1f30963 100644 --- a/libyul/AsmParser.h +++ b/libyul/AsmParser.h @@ -31,6 +31,7 @@ #include #include +#include #include #include #include diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 3454f09ac..ada942152 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include diff --git a/tools/yulPhaser/Phaser.cpp b/tools/yulPhaser/Phaser.cpp index db7d79c31..30d3b8876 100644 --- a/tools/yulPhaser/Phaser.cpp +++ b/tools/yulPhaser/Phaser.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include From 01a1316b36c90ef7358767423e9500df8cfffb9d Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 29 Jul 2021 21:48:42 +0200 Subject: [PATCH 90/98] Only set "runs" if used. --- solc/CommandLineParser.cpp | 6 ++++-- test/solc/CommandLineParser.cpp | 3 --- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 59972fb3b..a01aa59f5 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -967,7 +967,8 @@ General Information)").c_str(), m_options.optimizer.enabled = (m_args.count(g_strOptimize) > 0); m_options.optimizer.noOptimizeYul = (m_args.count(g_strNoOptimizeYul) > 0); - m_options.optimizer.expectedExecutionsPerDeployment = m_args.at(g_strOptimizeRuns).as(); + if (!m_args[g_strOptimizeRuns].defaulted()) + m_options.optimizer.expectedExecutionsPerDeployment = m_args.at(g_strOptimizeRuns).as(); if (m_args.count(g_strYulOptimizations)) { @@ -1132,7 +1133,8 @@ General Information)").c_str(), m_args.count(g_strModelCheckerTargets) || m_args.count(g_strModelCheckerTimeout); m_options.output.experimentalViaIR = (m_args.count(g_strExperimentalViaIR) > 0); - m_options.optimizer.expectedExecutionsPerDeployment = m_args[g_strOptimizeRuns].as(); + if (!m_args[g_strOptimizeRuns].defaulted()) + m_options.optimizer.expectedExecutionsPerDeployment = m_args.at(g_strOptimizeRuns).as(); m_options.optimizer.enabled = (m_args.count(g_strOptimize) > 0); m_options.optimizer.noOptimizeYul = (m_args.count(g_strNoOptimizeYul) > 0); diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp index 8d24f97aa..02444ce5d 100644 --- a/test/solc/CommandLineParser.cpp +++ b/test/solc/CommandLineParser.cpp @@ -78,7 +78,6 @@ BOOST_AUTO_TEST_CASE(no_options) CommandLineOptions expectedOptions; expectedOptions.input.paths = {"contract.sol"}; - expectedOptions.optimizer.expectedExecutionsPerDeployment = 200; expectedOptions.modelChecker.initialize = true; expectedOptions.modelChecker.settings = { ModelCheckerContracts::Default(), @@ -334,8 +333,6 @@ BOOST_AUTO_TEST_CASE(assembly_mode_options) expectedOptions.optimizer.yulSteps = "agf"; expectedOptions.optimizer.expectedExecutionsPerDeployment = 1000; } - else - expectedOptions.optimizer.expectedExecutionsPerDeployment = OptimiserSettings{}.expectedExecutionsPerDeployment; stringstream sout, serr; optional parsedOptions = parseCommandLine(commandLine, sout, serr); From d203c80abd8b657b1c47d4cc8a448b4323bc7cf5 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Tue, 3 Aug 2021 15:50:38 +0200 Subject: [PATCH 91/98] Eliminate unnecessary m_source from Parser --- liblangutil/Scanner.h | 2 ++ libsolidity/parsing/Parser.cpp | 5 +---- libsolidity/parsing/Parser.h | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/liblangutil/Scanner.h b/liblangutil/Scanner.h index 3ef6f7a09..e9957850c 100644 --- a/liblangutil/Scanner.h +++ b/liblangutil/Scanner.h @@ -119,6 +119,8 @@ public: rescan(); } + CharStream const& charStream() const noexcept { return m_source; } + /// @returns the next token and advances input Token next(); diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index efb31545a..3f30a07c9 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -87,7 +87,6 @@ ASTPointer Parser::parse(CharStream& _charStream) try { m_recursionDepth = 0; - m_source = &_charStream; m_scanner = make_shared(_charStream); ASTNodeFactory nodeFactory(*this); @@ -2057,8 +2056,6 @@ bool Parser::variableDeclarationStart() optional Parser::findLicenseString(std::vector> const& _nodes) { - solAssert(!!m_source, ""); - // We circumvent the scanner here, because it skips non-docstring comments. static regex const licenseRegex("SPDX-License-Identifier:\\s*([a-zA-Z0-9 ()+.-]+)"); @@ -2066,7 +2063,7 @@ optional Parser::findLicenseString(std::vector> cons // This will leave e.g. "global comments". using iter = std::string::const_iterator; vector> sequencesToSearch; - string const& source = m_source->source(); + string const& source = m_scanner->charStream().source(); sequencesToSearch.emplace_back(source.begin(), source.end()); for (ASTPointer const& node: _nodes) if (node->location().hasText()) diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index cdfeddd63..5374966d7 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -211,7 +211,6 @@ private: /// Creates an empty ParameterList at the current location (used if parameters can be omitted). ASTPointer createEmptyParameterList(); - langutil::CharStream* m_source = nullptr; /// Flag that signifies whether '_' is parsed as a PlaceholderStatement or a regular identifier. bool m_insideModifier = false; langutil::EVMVersion m_evmVersion; From 685d7a8c991c3de211d040f285afb15b3dafa96b Mon Sep 17 00:00:00 2001 From: Leo Alt Date: Fri, 2 Jul 2021 14:42:53 +0200 Subject: [PATCH 92/98] Bundle all unproved targets in a single message and only show all if setting chooses that --- libsolidity/formal/BMC.cpp | 21 ++++++++++-- libsolidity/formal/BMC.h | 7 ++-- libsolidity/formal/CHC.cpp | 40 +++++++++++++++++++---- libsolidity/formal/CHC.h | 22 +++++++++---- libsolidity/formal/ModelChecker.cpp | 4 +-- libsolidity/formal/ModelCheckerSettings.h | 2 ++ test/tools/fuzzer_common.cpp | 2 ++ 7 files changed, 79 insertions(+), 19 deletions(-) diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index eee9988ed..2676dffaa 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -60,7 +60,7 @@ BMC::BMC( #endif } -void BMC::analyze(SourceUnit const& _source, map> _solvedTargets) +void BMC::analyze(SourceUnit const& _source, map, smt::EncodingContext::IdCompare> _solvedTargets) { if (m_interface->solvers() == 0) { @@ -84,8 +84,21 @@ void BMC::analyze(SourceUnit const& _source, map 0 && !m_settings.showUnproved) + m_errorReporter.warning( + 2788_error, + {}, + "BMC: " + + to_string(m_unprovedAmt) + + " verification condition(s) could not be proved." + + " Enable the model checker option \"show unproved\" to see all of them." + + " Consider choosing a specific contract to be verified in order to reduce the solving problems." + + " Consider increasing the timeout per query." + ); } // If this check is true, Z3 and CVC4 are not available @@ -961,8 +974,12 @@ void BMC::checkCondition( case smtutil::CheckResult::UNSATISFIABLE: break; case smtutil::CheckResult::UNKNOWN: - m_errorReporter.warning(_errorMightHappen, _location, "BMC: " + _description + " might happen here.", secondaryLocation); + { + ++m_unprovedAmt; + if (m_settings.showUnproved) + m_errorReporter.warning(_errorMightHappen, _location, "BMC: " + _description + " might happen here.", secondaryLocation); break; + } case smtutil::CheckResult::CONFLICTING: m_errorReporter.warning(1584_error, _location, "BMC: At least two SMT solvers provided conflicting answers. Results might not be sound."); break; diff --git a/libsolidity/formal/BMC.h b/libsolidity/formal/BMC.h index c1b89347c..a256dbf09 100644 --- a/libsolidity/formal/BMC.h +++ b/libsolidity/formal/BMC.h @@ -66,7 +66,7 @@ public: langutil::CharStreamProvider const& _charStreamProvider ); - void analyze(SourceUnit const& _sources, std::map> _solvedTargets); + void analyze(SourceUnit const& _sources, std::map, smt::EncodingContext::IdCompare> _solvedTargets); /// This is used if the SMT solver is not directly linked into this binary. /// @returns a list of inputs to the SMT solver that were not part of the argument to @@ -192,7 +192,10 @@ private: std::vector m_verificationTargets; /// Targets that were already proven. - std::map> m_solvedTargets; + std::map, smt::EncodingContext::IdCompare> m_solvedTargets; + + /// Number of verification conditions that could not be proved. + size_t m_unprovedAmt = 0; }; } diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index 00657922d..7cc963956 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -933,6 +933,7 @@ void CHC::resetSourceAnalysis() { m_safeTargets.clear(); m_unsafeTargets.clear(); + m_unprovedTargets.clear(); m_functionTargetIds.clear(); m_verificationTargets.clear(); m_queryPlaceholders.clear(); @@ -1594,6 +1595,32 @@ void CHC::checkVerificationTargets() checkedErrorIds.insert(target.errorId); } + auto toReport = m_unsafeTargets; + if (m_settings.showUnproved) + for (auto const& [node, targets]: m_unprovedTargets) + for (auto const& [target, info]: targets) + toReport[node].emplace(target, info); + + for (auto const& [node, targets]: toReport) + for (auto const& [target, info]: targets) + m_errorReporter.warning( + info.error, + info.location, + info.message + ); + + if (!m_settings.showUnproved && !m_unprovedTargets.empty()) + m_errorReporter.warning( + 5840_error, + {}, + "CHC: " + + to_string(m_unprovedTargets.size()) + + " verification condition(s) could not be proved." + + " Enable the model checker option \"show unproved\" to see all of them." + + " Consider choosing a specific contract to be verified in order to reduce the solving problems." + + " Consider increasing the timeout per query." + ); + // There can be targets in internal functions that are not reachable from the external interface. // These are safe by definition and are not even checked by the CHC engine, but this information // must still be reported safe by the BMC engine. @@ -1633,27 +1660,26 @@ void CHC::checkAndReportTarget( else if (result == CheckResult::SATISFIABLE) { solAssert(!_satMsg.empty(), ""); - m_unsafeTargets[_target.errorNode].insert(_target.type); auto cex = generateCounterexample(model, error().name); if (cex) - m_errorReporter.warning( + m_unsafeTargets[_target.errorNode][_target.type] = { _errorReporterId, location, "CHC: " + _satMsg + "\nCounterexample:\n" + *cex - ); + }; else - m_errorReporter.warning( + m_unsafeTargets[_target.errorNode][_target.type] = { _errorReporterId, location, "CHC: " + _satMsg - ); + }; } else if (!_unknownMsg.empty()) - m_errorReporter.warning( + m_unprovedTargets[_target.errorNode][_target.type] = { _errorReporterId, location, "CHC: " + _unknownMsg - ); + }; } /** diff --git a/libsolidity/formal/CHC.h b/libsolidity/formal/CHC.h index 3aab9268d..f2e8f62a1 100644 --- a/libsolidity/formal/CHC.h +++ b/libsolidity/formal/CHC.h @@ -39,6 +39,8 @@ #include +#include + #include #include @@ -62,8 +64,14 @@ public: void analyze(SourceUnit const& _sources); - std::map> const& safeTargets() const { return m_safeTargets; } - std::map> const& unsafeTargets() const { return m_unsafeTargets; } + struct ReportTargetInfo + { + langutil::ErrorId error; + langutil::SourceLocation location; + std::string message; + }; + std::map, smt::EncodingContext::IdCompare> const& safeTargets() const { return m_safeTargets; } + std::map, smt::EncodingContext::IdCompare> const& unsafeTargets() const { return m_unsafeTargets; } /// This is used if the Horn solver is not directly linked into this binary. /// @returns a list of inputs to the Horn solver that were not part of the argument to @@ -347,10 +355,12 @@ private: /// Helper mapping unique IDs to actual verification targets. std::map m_verificationTargets; - /// Targets proven safe. - std::map> m_safeTargets; - /// Targets proven unsafe. - std::map> m_unsafeTargets; + /// Targets proved safe. + std::map, smt::EncodingContext::IdCompare> m_safeTargets; + /// Targets proved unsafe. + std::map, smt::EncodingContext::IdCompare> m_unsafeTargets; + /// Targets not proved. + std::map, smt::EncodingContext::IdCompare> m_unprovedTargets; //@} /// Control-flow. diff --git a/libsolidity/formal/ModelChecker.cpp b/libsolidity/formal/ModelChecker.cpp index 1aac80248..9b304f8ea 100644 --- a/libsolidity/formal/ModelChecker.cpp +++ b/libsolidity/formal/ModelChecker.cpp @@ -120,8 +120,8 @@ void ModelChecker::analyze(SourceUnit const& _source) m_chc.analyze(_source); auto solvedTargets = m_chc.safeTargets(); - for (auto const& target: m_chc.unsafeTargets()) - solvedTargets[target.first] += target.second; + for (auto const& [node, targets]: m_chc.unsafeTargets()) + solvedTargets[node] += targets | ranges::views::keys; if (m_settings.engine.bmc) m_bmc.analyze(_source, solvedTargets); diff --git a/libsolidity/formal/ModelCheckerSettings.h b/libsolidity/formal/ModelCheckerSettings.h index edd8f0800..ff39ddc9f 100644 --- a/libsolidity/formal/ModelCheckerSettings.h +++ b/libsolidity/formal/ModelCheckerSettings.h @@ -113,6 +113,7 @@ struct ModelCheckerSettings { ModelCheckerContracts contracts = ModelCheckerContracts::Default(); ModelCheckerEngine engine = ModelCheckerEngine::None(); + bool showUnproved = false; smtutil::SMTSolverChoice solvers = smtutil::SMTSolverChoice::All(); ModelCheckerTargets targets = ModelCheckerTargets::Default(); std::optional timeout; @@ -123,6 +124,7 @@ struct ModelCheckerSettings return contracts == _other.contracts && engine == _other.engine && + showUnproved == _other.showUnproved && solvers == _other.solvers && targets == _other.targets && timeout == _other.timeout; diff --git a/test/tools/fuzzer_common.cpp b/test/tools/fuzzer_common.cpp index ef85288b0..65749a009 100644 --- a/test/tools/fuzzer_common.cpp +++ b/test/tools/fuzzer_common.cpp @@ -20,6 +20,7 @@ #include #include +#include #include @@ -104,6 +105,7 @@ void FuzzerUtil::testCompiler( compiler.setModelCheckerSettings({ frontend::ModelCheckerContracts::Default(), frontend::ModelCheckerEngine::All(), + /*showUnproved=*/false, smtutil::SMTSolverChoice::All(), frontend::ModelCheckerTargets::Default(), /*timeout=*/1 From 41087f3195be81312decb68ca1888b3468e9bf08 Mon Sep 17 00:00:00 2001 From: Leo Alt Date: Fri, 2 Jul 2021 14:43:20 +0200 Subject: [PATCH 93/98] Add CLI and JSON option to show unproved targets --- libsolidity/interface/StandardCompiler.cpp | 10 +++++++++- solc/CommandLineParser.cpp | 13 +++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 70ed10d27..56c71f776 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -442,7 +442,7 @@ std::optional checkSettingsKeys(Json::Value const& _input) std::optional checkModelCheckerSettingsKeys(Json::Value const& _input) { - static set keys{"contracts", "engine", "solvers", "targets", "timeout"}; + static set keys{"contracts", "engine", "showUnproved", "solvers", "targets", "timeout"}; return checkKeys(_input, keys, "modelChecker"); } @@ -951,6 +951,14 @@ std::variant StandardCompiler: ret.modelCheckerSettings.engine = *engine; } + if (modelCheckerSettings.isMember("showUnproved")) + { + auto const& showUnproved = modelCheckerSettings["showUnproved"]; + if (!showUnproved.isBool()) + return formatFatalError("JSONError", "settings.modelChecker.showUnproved must be a Boolean value."); + ret.modelCheckerSettings.showUnproved = showUnproved.asBool(); + } + if (modelCheckerSettings.isMember("solvers")) { auto const& solversArray = modelCheckerSettings["solvers"]; diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index a01aa59f5..3654bbfc8 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -87,6 +87,7 @@ static string const g_strMetadataHash = "metadata-hash"; static string const g_strMetadataLiteral = "metadata-literal"; static string const g_strModelCheckerContracts = "model-checker-contracts"; static string const g_strModelCheckerEngine = "model-checker-engine"; +static string const g_strModelCheckerShowUnproved = "model-checker-show-unproved"; static string const g_strModelCheckerSolvers = "model-checker-solvers"; static string const g_strModelCheckerTargets = "model-checker-targets"; static string const g_strModelCheckerTimeout = "model-checker-timeout"; @@ -724,6 +725,11 @@ General Information)").c_str(), po::value()->value_name("all,bmc,chc,none")->default_value("none"), "Select model checker engine." ) + ( + g_strModelCheckerShowUnproved.c_str(), + po::value()->value_name("false,true")->default_value(false), + "Select whether to show all unproved targets." + ) ( g_strModelCheckerSolvers.c_str(), po::value()->value_name("all,cvc4,z3,smtlib2")->default_value("all"), @@ -1098,6 +1104,12 @@ General Information)").c_str(), m_options.modelChecker.settings.engine = *engine; } + if (m_args.count(g_strModelCheckerShowUnproved)) + { + bool showUnproved = m_args[g_strModelCheckerShowUnproved].as(); + m_options.modelChecker.settings.showUnproved = showUnproved; + } + if (m_args.count(g_strModelCheckerSolvers)) { string solversStr = m_args[g_strModelCheckerSolvers].as(); @@ -1129,6 +1141,7 @@ General Information)").c_str(), m_options.modelChecker.initialize = m_args.count(g_strModelCheckerContracts) || m_args.count(g_strModelCheckerEngine) || + m_args.count(g_strModelCheckerShowUnproved) || m_args.count(g_strModelCheckerSolvers) || m_args.count(g_strModelCheckerTargets) || m_args.count(g_strModelCheckerTimeout); From 700fe3e5d47a645b3853f4b672d003c9b08edaad Mon Sep 17 00:00:00 2001 From: Leo Alt Date: Fri, 2 Jul 2021 14:43:37 +0200 Subject: [PATCH 94/98] CL tests --- .../args | 1 + .../err | 3 + .../input.sol | 12 +++ .../args | 1 + .../err | 1 + .../input.sol | 12 +++ .../args | 1 + .../err | 1 + .../input.sol | 12 +++ .../args | 1 + .../err | 3 + .../input.sol | 12 +++ .../args | 1 + .../model_checker_show_unproved_false_bmc/err | 1 + .../input.sol | 12 +++ .../args | 1 + .../model_checker_show_unproved_false_chc/err | 1 + .../input.sol | 12 +++ .../args | 1 + .../err | 12 +++ .../input.sol | 12 +++ .../model_checker_show_unproved_true_bmc/args | 1 + .../model_checker_show_unproved_true_bmc/err | 6 ++ .../input.sol | 12 +++ .../model_checker_show_unproved_true_chc/args | 1 + .../model_checker_show_unproved_true_chc/err | 5 + .../input.sol | 12 +++ .../args | 1 + .../err | 1 + .../exit | 1 + .../input.sol | 12 +++ .../model_checker_timeout_all/err | 13 +-- .../model_checker_timeout_bmc/err | 7 +- .../model_checker_timeout_chc/err | 6 +- .../input.json | 26 +++++ .../output.json | 90 +++++++++++++++++ .../input.json | 27 +++++ .../output.json | 90 +++++++++++++++++ .../input.json | 27 +++++ .../output.json | 88 +++++++++++++++++ .../input.json | 27 +++++ .../output.json | 3 + .../input.json | 27 +++++ .../output.json | 99 +++++++++++++++++++ .../input.json | 27 +++++ .../output.json | 93 +++++++++++++++++ .../input.json | 27 +++++ .../output.json | 7 ++ .../input.json | 27 +++++ .../output.json | 1 + .../output.json | 15 +-- .../output.json | 9 +- .../output.json | 8 +- 53 files changed, 862 insertions(+), 47 deletions(-) create mode 100644 test/cmdlineTests/model_checker_show_unproved_default_all_engines/args create mode 100644 test/cmdlineTests/model_checker_show_unproved_default_all_engines/err create mode 100644 test/cmdlineTests/model_checker_show_unproved_default_all_engines/input.sol create mode 100644 test/cmdlineTests/model_checker_show_unproved_default_bmc/args create mode 100644 test/cmdlineTests/model_checker_show_unproved_default_bmc/err create mode 100644 test/cmdlineTests/model_checker_show_unproved_default_bmc/input.sol create mode 100644 test/cmdlineTests/model_checker_show_unproved_default_chc/args create mode 100644 test/cmdlineTests/model_checker_show_unproved_default_chc/err create mode 100644 test/cmdlineTests/model_checker_show_unproved_default_chc/input.sol create mode 100644 test/cmdlineTests/model_checker_show_unproved_false_all_engines/args create mode 100644 test/cmdlineTests/model_checker_show_unproved_false_all_engines/err create mode 100644 test/cmdlineTests/model_checker_show_unproved_false_all_engines/input.sol create mode 100644 test/cmdlineTests/model_checker_show_unproved_false_bmc/args create mode 100644 test/cmdlineTests/model_checker_show_unproved_false_bmc/err create mode 100644 test/cmdlineTests/model_checker_show_unproved_false_bmc/input.sol create mode 100644 test/cmdlineTests/model_checker_show_unproved_false_chc/args create mode 100644 test/cmdlineTests/model_checker_show_unproved_false_chc/err create mode 100644 test/cmdlineTests/model_checker_show_unproved_false_chc/input.sol create mode 100644 test/cmdlineTests/model_checker_show_unproved_true_all_engines/args create mode 100644 test/cmdlineTests/model_checker_show_unproved_true_all_engines/err create mode 100644 test/cmdlineTests/model_checker_show_unproved_true_all_engines/input.sol create mode 100644 test/cmdlineTests/model_checker_show_unproved_true_bmc/args create mode 100644 test/cmdlineTests/model_checker_show_unproved_true_bmc/err create mode 100644 test/cmdlineTests/model_checker_show_unproved_true_bmc/input.sol create mode 100644 test/cmdlineTests/model_checker_show_unproved_true_chc/args create mode 100644 test/cmdlineTests/model_checker_show_unproved_true_chc/err create mode 100644 test/cmdlineTests/model_checker_show_unproved_true_chc/input.sol create mode 100644 test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/args create mode 100644 test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/err create mode 100644 test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/exit create mode 100644 test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/input.sol create mode 100644 test/cmdlineTests/standard_model_checker_show_unproved_default_all_engines/input.json create mode 100644 test/cmdlineTests/standard_model_checker_show_unproved_default_all_engines/output.json create mode 100644 test/cmdlineTests/standard_model_checker_show_unproved_false_all_engines/input.json create mode 100644 test/cmdlineTests/standard_model_checker_show_unproved_false_all_engines/output.json create mode 100644 test/cmdlineTests/standard_model_checker_show_unproved_false_bmc/input.json create mode 100644 test/cmdlineTests/standard_model_checker_show_unproved_false_bmc/output.json create mode 100644 test/cmdlineTests/standard_model_checker_show_unproved_false_chc/input.json create mode 100644 test/cmdlineTests/standard_model_checker_show_unproved_false_chc/output.json create mode 100644 test/cmdlineTests/standard_model_checker_show_unproved_true_all_engines/input.json create mode 100644 test/cmdlineTests/standard_model_checker_show_unproved_true_all_engines/output.json create mode 100644 test/cmdlineTests/standard_model_checker_show_unproved_true_bmc/input.json create mode 100644 test/cmdlineTests/standard_model_checker_show_unproved_true_bmc/output.json create mode 100644 test/cmdlineTests/standard_model_checker_show_unproved_true_chc/input.json create mode 100644 test/cmdlineTests/standard_model_checker_show_unproved_true_chc/output.json create mode 100644 test/cmdlineTests/standard_model_checker_show_unproved_wrong/input.json create mode 100644 test/cmdlineTests/standard_model_checker_show_unproved_wrong/output.json diff --git a/test/cmdlineTests/model_checker_show_unproved_default_all_engines/args b/test/cmdlineTests/model_checker_show_unproved_default_all_engines/args new file mode 100644 index 000000000..5aeb1490e --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_default_all_engines/args @@ -0,0 +1 @@ +--model-checker-engine all diff --git a/test/cmdlineTests/model_checker_show_unproved_default_all_engines/err b/test/cmdlineTests/model_checker_show_unproved_default_all_engines/err new file mode 100644 index 000000000..45c38ef35 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_default_all_engines/err @@ -0,0 +1,3 @@ +Warning: CHC: 1 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. + +Warning: BMC: 1 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. diff --git a/test/cmdlineTests/model_checker_show_unproved_default_all_engines/input.sol b/test/cmdlineTests/model_checker_show_unproved_default_all_engines/input.sol new file mode 100644 index 000000000..567c9cd2b --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_default_all_engines/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } +} diff --git a/test/cmdlineTests/model_checker_show_unproved_default_bmc/args b/test/cmdlineTests/model_checker_show_unproved_default_bmc/args new file mode 100644 index 000000000..549f20236 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_default_bmc/args @@ -0,0 +1 @@ +--model-checker-engine bmc diff --git a/test/cmdlineTests/model_checker_show_unproved_default_bmc/err b/test/cmdlineTests/model_checker_show_unproved_default_bmc/err new file mode 100644 index 000000000..f8d0af2f4 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_default_bmc/err @@ -0,0 +1 @@ +Warning: BMC: 1 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. diff --git a/test/cmdlineTests/model_checker_show_unproved_default_bmc/input.sol b/test/cmdlineTests/model_checker_show_unproved_default_bmc/input.sol new file mode 100644 index 000000000..567c9cd2b --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_default_bmc/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } +} diff --git a/test/cmdlineTests/model_checker_show_unproved_default_chc/args b/test/cmdlineTests/model_checker_show_unproved_default_chc/args new file mode 100644 index 000000000..7458a47d3 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_default_chc/args @@ -0,0 +1 @@ +--model-checker-engine chc diff --git a/test/cmdlineTests/model_checker_show_unproved_default_chc/err b/test/cmdlineTests/model_checker_show_unproved_default_chc/err new file mode 100644 index 000000000..77083185f --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_default_chc/err @@ -0,0 +1 @@ +Warning: CHC: 1 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. diff --git a/test/cmdlineTests/model_checker_show_unproved_default_chc/input.sol b/test/cmdlineTests/model_checker_show_unproved_default_chc/input.sol new file mode 100644 index 000000000..567c9cd2b --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_default_chc/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } +} diff --git a/test/cmdlineTests/model_checker_show_unproved_false_all_engines/args b/test/cmdlineTests/model_checker_show_unproved_false_all_engines/args new file mode 100644 index 000000000..5f6883c76 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_false_all_engines/args @@ -0,0 +1 @@ +--model-checker-engine all --model-checker-show-unproved false diff --git a/test/cmdlineTests/model_checker_show_unproved_false_all_engines/err b/test/cmdlineTests/model_checker_show_unproved_false_all_engines/err new file mode 100644 index 000000000..45c38ef35 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_false_all_engines/err @@ -0,0 +1,3 @@ +Warning: CHC: 1 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. + +Warning: BMC: 1 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. diff --git a/test/cmdlineTests/model_checker_show_unproved_false_all_engines/input.sol b/test/cmdlineTests/model_checker_show_unproved_false_all_engines/input.sol new file mode 100644 index 000000000..567c9cd2b --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_false_all_engines/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } +} diff --git a/test/cmdlineTests/model_checker_show_unproved_false_bmc/args b/test/cmdlineTests/model_checker_show_unproved_false_bmc/args new file mode 100644 index 000000000..40fcde62b --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_false_bmc/args @@ -0,0 +1 @@ +--model-checker-engine bmc --model-checker-show-unproved false diff --git a/test/cmdlineTests/model_checker_show_unproved_false_bmc/err b/test/cmdlineTests/model_checker_show_unproved_false_bmc/err new file mode 100644 index 000000000..f8d0af2f4 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_false_bmc/err @@ -0,0 +1 @@ +Warning: BMC: 1 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. diff --git a/test/cmdlineTests/model_checker_show_unproved_false_bmc/input.sol b/test/cmdlineTests/model_checker_show_unproved_false_bmc/input.sol new file mode 100644 index 000000000..567c9cd2b --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_false_bmc/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } +} diff --git a/test/cmdlineTests/model_checker_show_unproved_false_chc/args b/test/cmdlineTests/model_checker_show_unproved_false_chc/args new file mode 100644 index 000000000..c14333f44 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_false_chc/args @@ -0,0 +1 @@ +--model-checker-engine chc --model-checker-show-unproved false diff --git a/test/cmdlineTests/model_checker_show_unproved_false_chc/err b/test/cmdlineTests/model_checker_show_unproved_false_chc/err new file mode 100644 index 000000000..77083185f --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_false_chc/err @@ -0,0 +1 @@ +Warning: CHC: 1 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. diff --git a/test/cmdlineTests/model_checker_show_unproved_false_chc/input.sol b/test/cmdlineTests/model_checker_show_unproved_false_chc/input.sol new file mode 100644 index 000000000..567c9cd2b --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_false_chc/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } +} diff --git a/test/cmdlineTests/model_checker_show_unproved_true_all_engines/args b/test/cmdlineTests/model_checker_show_unproved_true_all_engines/args new file mode 100644 index 000000000..4ccfd2e96 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_true_all_engines/args @@ -0,0 +1 @@ +--model-checker-engine all --model-checker-show-unproved true diff --git a/test/cmdlineTests/model_checker_show_unproved_true_all_engines/err b/test/cmdlineTests/model_checker_show_unproved_true_all_engines/err new file mode 100644 index 000000000..83864075d --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_true_all_engines/err @@ -0,0 +1,12 @@ +Warning: CHC: Assertion violation might happen here. + --> model_checker_show_unproved_true_all_engines/input.sol:10:9: + | +10 | assert(s.x > 0); + | ^^^^^^^^^^^^^^^ + +Warning: BMC: Assertion violation might happen here. + --> model_checker_show_unproved_true_all_engines/input.sol:10:9: + | +10 | assert(s.x > 0); + | ^^^^^^^^^^^^^^^ +Note: diff --git a/test/cmdlineTests/model_checker_show_unproved_true_all_engines/input.sol b/test/cmdlineTests/model_checker_show_unproved_true_all_engines/input.sol new file mode 100644 index 000000000..567c9cd2b --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_true_all_engines/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } +} diff --git a/test/cmdlineTests/model_checker_show_unproved_true_bmc/args b/test/cmdlineTests/model_checker_show_unproved_true_bmc/args new file mode 100644 index 000000000..79cae9260 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_true_bmc/args @@ -0,0 +1 @@ +--model-checker-engine bmc --model-checker-show-unproved true diff --git a/test/cmdlineTests/model_checker_show_unproved_true_bmc/err b/test/cmdlineTests/model_checker_show_unproved_true_bmc/err new file mode 100644 index 000000000..84ada7873 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_true_bmc/err @@ -0,0 +1,6 @@ +Warning: BMC: Assertion violation might happen here. + --> model_checker_show_unproved_true_bmc/input.sol:10:9: + | +10 | assert(s.x > 0); + | ^^^^^^^^^^^^^^^ +Note: diff --git a/test/cmdlineTests/model_checker_show_unproved_true_bmc/input.sol b/test/cmdlineTests/model_checker_show_unproved_true_bmc/input.sol new file mode 100644 index 000000000..567c9cd2b --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_true_bmc/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } +} diff --git a/test/cmdlineTests/model_checker_show_unproved_true_chc/args b/test/cmdlineTests/model_checker_show_unproved_true_chc/args new file mode 100644 index 000000000..f992f4d0c --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_true_chc/args @@ -0,0 +1 @@ +--model-checker-engine chc --model-checker-show-unproved true diff --git a/test/cmdlineTests/model_checker_show_unproved_true_chc/err b/test/cmdlineTests/model_checker_show_unproved_true_chc/err new file mode 100644 index 000000000..8274f4b46 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_true_chc/err @@ -0,0 +1,5 @@ +Warning: CHC: Assertion violation might happen here. + --> model_checker_show_unproved_true_chc/input.sol:10:9: + | +10 | assert(s.x > 0); + | ^^^^^^^^^^^^^^^ diff --git a/test/cmdlineTests/model_checker_show_unproved_true_chc/input.sol b/test/cmdlineTests/model_checker_show_unproved_true_chc/input.sol new file mode 100644 index 000000000..567c9cd2b --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_true_chc/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } +} diff --git a/test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/args b/test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/args new file mode 100644 index 000000000..b20188349 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/args @@ -0,0 +1 @@ +--model-checker-engine all --model-checker-show-unproved aaa diff --git a/test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/err b/test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/err new file mode 100644 index 000000000..9ece92874 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/err @@ -0,0 +1 @@ +the argument ('aaa') for option '--model-checker-show-unproved' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false' diff --git a/test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/exit b/test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/input.sol b/test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/input.sol new file mode 100644 index 000000000..567c9cd2b --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } +} diff --git a/test/cmdlineTests/model_checker_timeout_all/err b/test/cmdlineTests/model_checker_timeout_all/err index 6b2d3392a..45c38ef35 100644 --- a/test/cmdlineTests/model_checker_timeout_all/err +++ b/test/cmdlineTests/model_checker_timeout_all/err @@ -1,12 +1,3 @@ -Warning: CHC: Assertion violation might happen here. - --> model_checker_timeout_all/input.sol:9:3: - | -9 | assert(r % k == 0); - | ^^^^^^^^^^^^^^^^^^ +Warning: CHC: 1 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. -Warning: BMC: Assertion violation might happen here. - --> model_checker_timeout_all/input.sol:9:3: - | -9 | assert(r % k == 0); - | ^^^^^^^^^^^^^^^^^^ -Note: +Warning: BMC: 1 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. diff --git a/test/cmdlineTests/model_checker_timeout_bmc/err b/test/cmdlineTests/model_checker_timeout_bmc/err index 9cce6ba0c..f8d0af2f4 100644 --- a/test/cmdlineTests/model_checker_timeout_bmc/err +++ b/test/cmdlineTests/model_checker_timeout_bmc/err @@ -1,6 +1 @@ -Warning: BMC: Assertion violation might happen here. - --> model_checker_timeout_bmc/input.sol:9:3: - | -9 | assert(r % k == 0); - | ^^^^^^^^^^^^^^^^^^ -Note: +Warning: BMC: 1 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. diff --git a/test/cmdlineTests/model_checker_timeout_chc/err b/test/cmdlineTests/model_checker_timeout_chc/err index 187ef937c..77083185f 100644 --- a/test/cmdlineTests/model_checker_timeout_chc/err +++ b/test/cmdlineTests/model_checker_timeout_chc/err @@ -1,5 +1 @@ -Warning: CHC: Assertion violation might happen here. - --> model_checker_timeout_chc/input.sol:9:3: - | -9 | assert(r % k == 0); - | ^^^^^^^^^^^^^^^^^^ +Warning: CHC: 1 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_default_all_engines/input.json b/test/cmdlineTests/standard_model_checker_show_unproved_default_all_engines/input.json new file mode 100644 index 000000000..18a8b21fc --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_default_all_engines/input.json @@ -0,0 +1,26 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\n\ncontract test { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } + }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "all" + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_default_all_engines/output.json b/test/cmdlineTests/standard_model_checker_show_unproved_default_all_engines/output.json new file mode 100644 index 000000000..cf4c00c61 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_default_all_engines/output.json @@ -0,0 +1,90 @@ +{"auxiliaryInputRequested":{"smtlib2queries":{"0x119e9d636624c5af8dc0d97ee8d2905551bfe9eea88d60c6d3793cfdc576a76b":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) +(declare-fun |expr_15_1| () Bool) +(declare-fun |expr_16_0| () Int) +(declare-fun |expr_17_0| () Int) +(declare-fun |expr_18_0| () Int) +(declare-fun |expr_12_0| () |struct test.S|) +(declare-fun |expr_14_1| () Int) +(declare-fun |expr_19_1| () Int) +(declare-fun |expr_12_1| () |struct test.S|) +(declare-fun |expr_12_2| () |struct test.S|) +(declare-fun |expr_14_2| () Int) +(declare-fun |s_7_2| () |struct test.S|) +(declare-fun |expr_12_3| () |struct test.S|) +(declare-fun |expr_22_0| () |struct test.S|) +(declare-fun |expr_23_1| () Int) +(declare-fun |expr_24_0| () Int) +(declare-fun |expr_25_1| () Bool) + +(assert (and (and (and true true) (and (= expr_25_1 (> expr_23_1 expr_24_0)) (and (=> (and true true) true) (and (= expr_24_0 0) (and (=> (and true true) (and (>= expr_23_1 0) (<= expr_23_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_23_1 (|struct test.S_accessor_x| expr_22_0)) (and (= expr_22_0 s_7_2) (and (= expr_12_3 s_7_2) (and (ite (and true true) (= s_7_2 expr_12_2) (= s_7_2 s_7_1)) (and (=> (and true true) (and (>= expr_14_2 0) (<= expr_14_2 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_14_2 (|struct test.S_accessor_x| expr_12_2)) (and (= (|struct test.S_accessor_x| expr_12_2) expr_19_1) (and (= expr_12_1 s_7_1) (and (=> (and true true) (and (>= expr_19_1 0) (<= expr_19_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_19_1 (bv2nat (bvor (ite (>= expr_14_1 0) ((_ int2bv 256) expr_14_1) (bvneg ((_ int2bv 256) (- expr_14_1)))) (ite (>= expr_18_0 0) ((_ int2bv 256) expr_18_0) (bvneg ((_ int2bv 256) (- expr_18_0))))))) (and (=> (and true true) (and (>= expr_14_1 0) (<= expr_14_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_14_1 (|struct test.S_accessor_x| expr_12_0)) (and (= expr_12_0 s_7_1) (and (=> (and true true) (and (>= expr_18_0 0) (<= expr_18_0 255))) (and (= expr_18_0 (ite expr_15_1 expr_16_0 expr_17_0)) (and (=> (and (and true true) (not expr_15_1)) true) (and (= expr_17_0 2) (and (=> (and (and true true) expr_15_1) true) (and (= expr_16_0 1) (and (= expr_15_1 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true)))))))))))))))))))))))))))) (not expr_25_1))) +(declare-const |EVALEXPR_0| Bool) +(assert (= |EVALEXPR_0| b_9_0)) +(check-sat) +(get-value (|EVALEXPR_0| )) +","0x4d368a1e0f051bee84d8e64e660d7d50d57486e66c037dcdb97b06447bbcfb8e":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) + +(assert (and (and (and true true) (and (= expr_15_0 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true))))) (not expr_15_0))) +(check-sat) +","0xab73091601c574bdace0ae9a7fc088a8f13ff47d4b78323c2df81da0281c9df4":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) + +(assert (and (and (and true true) (and (= expr_15_0 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true))))) expr_15_0)) +(check-sat) +"}},"errors":[{"component":"general","errorCode":"5840","formattedMessage":"Warning: CHC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. + +","message":"CHC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query.","severity":"warning","type":"Warning"},{"component":"general","errorCode":"2788","formattedMessage":"Warning: BMC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. + +","message":"BMC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query.","severity":"warning","type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_false_all_engines/input.json b/test/cmdlineTests/standard_model_checker_show_unproved_false_all_engines/input.json new file mode 100644 index 000000000..d7396dd98 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_false_all_engines/input.json @@ -0,0 +1,27 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\n\ncontract test { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } + }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "all", + "showUnproved": false + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_false_all_engines/output.json b/test/cmdlineTests/standard_model_checker_show_unproved_false_all_engines/output.json new file mode 100644 index 000000000..cf4c00c61 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_false_all_engines/output.json @@ -0,0 +1,90 @@ +{"auxiliaryInputRequested":{"smtlib2queries":{"0x119e9d636624c5af8dc0d97ee8d2905551bfe9eea88d60c6d3793cfdc576a76b":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) +(declare-fun |expr_15_1| () Bool) +(declare-fun |expr_16_0| () Int) +(declare-fun |expr_17_0| () Int) +(declare-fun |expr_18_0| () Int) +(declare-fun |expr_12_0| () |struct test.S|) +(declare-fun |expr_14_1| () Int) +(declare-fun |expr_19_1| () Int) +(declare-fun |expr_12_1| () |struct test.S|) +(declare-fun |expr_12_2| () |struct test.S|) +(declare-fun |expr_14_2| () Int) +(declare-fun |s_7_2| () |struct test.S|) +(declare-fun |expr_12_3| () |struct test.S|) +(declare-fun |expr_22_0| () |struct test.S|) +(declare-fun |expr_23_1| () Int) +(declare-fun |expr_24_0| () Int) +(declare-fun |expr_25_1| () Bool) + +(assert (and (and (and true true) (and (= expr_25_1 (> expr_23_1 expr_24_0)) (and (=> (and true true) true) (and (= expr_24_0 0) (and (=> (and true true) (and (>= expr_23_1 0) (<= expr_23_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_23_1 (|struct test.S_accessor_x| expr_22_0)) (and (= expr_22_0 s_7_2) (and (= expr_12_3 s_7_2) (and (ite (and true true) (= s_7_2 expr_12_2) (= s_7_2 s_7_1)) (and (=> (and true true) (and (>= expr_14_2 0) (<= expr_14_2 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_14_2 (|struct test.S_accessor_x| expr_12_2)) (and (= (|struct test.S_accessor_x| expr_12_2) expr_19_1) (and (= expr_12_1 s_7_1) (and (=> (and true true) (and (>= expr_19_1 0) (<= expr_19_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_19_1 (bv2nat (bvor (ite (>= expr_14_1 0) ((_ int2bv 256) expr_14_1) (bvneg ((_ int2bv 256) (- expr_14_1)))) (ite (>= expr_18_0 0) ((_ int2bv 256) expr_18_0) (bvneg ((_ int2bv 256) (- expr_18_0))))))) (and (=> (and true true) (and (>= expr_14_1 0) (<= expr_14_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_14_1 (|struct test.S_accessor_x| expr_12_0)) (and (= expr_12_0 s_7_1) (and (=> (and true true) (and (>= expr_18_0 0) (<= expr_18_0 255))) (and (= expr_18_0 (ite expr_15_1 expr_16_0 expr_17_0)) (and (=> (and (and true true) (not expr_15_1)) true) (and (= expr_17_0 2) (and (=> (and (and true true) expr_15_1) true) (and (= expr_16_0 1) (and (= expr_15_1 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true)))))))))))))))))))))))))))) (not expr_25_1))) +(declare-const |EVALEXPR_0| Bool) +(assert (= |EVALEXPR_0| b_9_0)) +(check-sat) +(get-value (|EVALEXPR_0| )) +","0x4d368a1e0f051bee84d8e64e660d7d50d57486e66c037dcdb97b06447bbcfb8e":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) + +(assert (and (and (and true true) (and (= expr_15_0 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true))))) (not expr_15_0))) +(check-sat) +","0xab73091601c574bdace0ae9a7fc088a8f13ff47d4b78323c2df81da0281c9df4":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) + +(assert (and (and (and true true) (and (= expr_15_0 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true))))) expr_15_0)) +(check-sat) +"}},"errors":[{"component":"general","errorCode":"5840","formattedMessage":"Warning: CHC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. + +","message":"CHC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query.","severity":"warning","type":"Warning"},{"component":"general","errorCode":"2788","formattedMessage":"Warning: BMC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. + +","message":"BMC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query.","severity":"warning","type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_false_bmc/input.json b/test/cmdlineTests/standard_model_checker_show_unproved_false_bmc/input.json new file mode 100644 index 000000000..c9ee3258e --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_false_bmc/input.json @@ -0,0 +1,27 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\n\ncontract test { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } + }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "bmc", + "showUnproved": false + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_false_bmc/output.json b/test/cmdlineTests/standard_model_checker_show_unproved_false_bmc/output.json new file mode 100644 index 000000000..6d9e16af8 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_false_bmc/output.json @@ -0,0 +1,88 @@ +{"auxiliaryInputRequested":{"smtlib2queries":{"0x119e9d636624c5af8dc0d97ee8d2905551bfe9eea88d60c6d3793cfdc576a76b":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) +(declare-fun |expr_15_1| () Bool) +(declare-fun |expr_16_0| () Int) +(declare-fun |expr_17_0| () Int) +(declare-fun |expr_18_0| () Int) +(declare-fun |expr_12_0| () |struct test.S|) +(declare-fun |expr_14_1| () Int) +(declare-fun |expr_19_1| () Int) +(declare-fun |expr_12_1| () |struct test.S|) +(declare-fun |expr_12_2| () |struct test.S|) +(declare-fun |expr_14_2| () Int) +(declare-fun |s_7_2| () |struct test.S|) +(declare-fun |expr_12_3| () |struct test.S|) +(declare-fun |expr_22_0| () |struct test.S|) +(declare-fun |expr_23_1| () Int) +(declare-fun |expr_24_0| () Int) +(declare-fun |expr_25_1| () Bool) + +(assert (and (and (and true true) (and (= expr_25_1 (> expr_23_1 expr_24_0)) (and (=> (and true true) true) (and (= expr_24_0 0) (and (=> (and true true) (and (>= expr_23_1 0) (<= expr_23_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_23_1 (|struct test.S_accessor_x| expr_22_0)) (and (= expr_22_0 s_7_2) (and (= expr_12_3 s_7_2) (and (ite (and true true) (= s_7_2 expr_12_2) (= s_7_2 s_7_1)) (and (=> (and true true) (and (>= expr_14_2 0) (<= expr_14_2 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_14_2 (|struct test.S_accessor_x| expr_12_2)) (and (= (|struct test.S_accessor_x| expr_12_2) expr_19_1) (and (= expr_12_1 s_7_1) (and (=> (and true true) (and (>= expr_19_1 0) (<= expr_19_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_19_1 (bv2nat (bvor (ite (>= expr_14_1 0) ((_ int2bv 256) expr_14_1) (bvneg ((_ int2bv 256) (- expr_14_1)))) (ite (>= expr_18_0 0) ((_ int2bv 256) expr_18_0) (bvneg ((_ int2bv 256) (- expr_18_0))))))) (and (=> (and true true) (and (>= expr_14_1 0) (<= expr_14_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_14_1 (|struct test.S_accessor_x| expr_12_0)) (and (= expr_12_0 s_7_1) (and (=> (and true true) (and (>= expr_18_0 0) (<= expr_18_0 255))) (and (= expr_18_0 (ite expr_15_1 expr_16_0 expr_17_0)) (and (=> (and (and true true) (not expr_15_1)) true) (and (= expr_17_0 2) (and (=> (and (and true true) expr_15_1) true) (and (= expr_16_0 1) (and (= expr_15_1 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true)))))))))))))))))))))))))))) (not expr_25_1))) +(declare-const |EVALEXPR_0| Bool) +(assert (= |EVALEXPR_0| b_9_0)) +(check-sat) +(get-value (|EVALEXPR_0| )) +","0x4d368a1e0f051bee84d8e64e660d7d50d57486e66c037dcdb97b06447bbcfb8e":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) + +(assert (and (and (and true true) (and (= expr_15_0 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true))))) (not expr_15_0))) +(check-sat) +","0xab73091601c574bdace0ae9a7fc088a8f13ff47d4b78323c2df81da0281c9df4":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) + +(assert (and (and (and true true) (and (= expr_15_0 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true))))) expr_15_0)) +(check-sat) +"}},"errors":[{"component":"general","errorCode":"2788","formattedMessage":"Warning: BMC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. + +","message":"BMC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query.","severity":"warning","type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_false_chc/input.json b/test/cmdlineTests/standard_model_checker_show_unproved_false_chc/input.json new file mode 100644 index 000000000..fe662e80c --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_false_chc/input.json @@ -0,0 +1,27 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\n\ncontract test { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } + }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "chc", + "showUnproved": false + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_false_chc/output.json b/test/cmdlineTests/standard_model_checker_show_unproved_false_chc/output.json new file mode 100644 index 000000000..5e66cb899 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_false_chc/output.json @@ -0,0 +1,3 @@ +{"errors":[{"component":"general","errorCode":"5840","formattedMessage":"Warning: CHC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. + +","message":"CHC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query.","severity":"warning","type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_true_all_engines/input.json b/test/cmdlineTests/standard_model_checker_show_unproved_true_all_engines/input.json new file mode 100644 index 000000000..3f9bb715e --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_true_all_engines/input.json @@ -0,0 +1,27 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\n\ncontract test { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } + }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "all", + "showUnproved": true + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_true_all_engines/output.json b/test/cmdlineTests/standard_model_checker_show_unproved_true_all_engines/output.json new file mode 100644 index 000000000..2e384c009 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_true_all_engines/output.json @@ -0,0 +1,99 @@ +{"auxiliaryInputRequested":{"smtlib2queries":{"0x119e9d636624c5af8dc0d97ee8d2905551bfe9eea88d60c6d3793cfdc576a76b":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) +(declare-fun |expr_15_1| () Bool) +(declare-fun |expr_16_0| () Int) +(declare-fun |expr_17_0| () Int) +(declare-fun |expr_18_0| () Int) +(declare-fun |expr_12_0| () |struct test.S|) +(declare-fun |expr_14_1| () Int) +(declare-fun |expr_19_1| () Int) +(declare-fun |expr_12_1| () |struct test.S|) +(declare-fun |expr_12_2| () |struct test.S|) +(declare-fun |expr_14_2| () Int) +(declare-fun |s_7_2| () |struct test.S|) +(declare-fun |expr_12_3| () |struct test.S|) +(declare-fun |expr_22_0| () |struct test.S|) +(declare-fun |expr_23_1| () Int) +(declare-fun |expr_24_0| () Int) +(declare-fun |expr_25_1| () Bool) + +(assert (and (and (and true true) (and (= expr_25_1 (> expr_23_1 expr_24_0)) (and (=> (and true true) true) (and (= expr_24_0 0) (and (=> (and true true) (and (>= expr_23_1 0) (<= expr_23_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_23_1 (|struct test.S_accessor_x| expr_22_0)) (and (= expr_22_0 s_7_2) (and (= expr_12_3 s_7_2) (and (ite (and true true) (= s_7_2 expr_12_2) (= s_7_2 s_7_1)) (and (=> (and true true) (and (>= expr_14_2 0) (<= expr_14_2 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_14_2 (|struct test.S_accessor_x| expr_12_2)) (and (= (|struct test.S_accessor_x| expr_12_2) expr_19_1) (and (= expr_12_1 s_7_1) (and (=> (and true true) (and (>= expr_19_1 0) (<= expr_19_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_19_1 (bv2nat (bvor (ite (>= expr_14_1 0) ((_ int2bv 256) expr_14_1) (bvneg ((_ int2bv 256) (- expr_14_1)))) (ite (>= expr_18_0 0) ((_ int2bv 256) expr_18_0) (bvneg ((_ int2bv 256) (- expr_18_0))))))) (and (=> (and true true) (and (>= expr_14_1 0) (<= expr_14_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_14_1 (|struct test.S_accessor_x| expr_12_0)) (and (= expr_12_0 s_7_1) (and (=> (and true true) (and (>= expr_18_0 0) (<= expr_18_0 255))) (and (= expr_18_0 (ite expr_15_1 expr_16_0 expr_17_0)) (and (=> (and (and true true) (not expr_15_1)) true) (and (= expr_17_0 2) (and (=> (and (and true true) expr_15_1) true) (and (= expr_16_0 1) (and (= expr_15_1 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true)))))))))))))))))))))))))))) (not expr_25_1))) +(declare-const |EVALEXPR_0| Bool) +(assert (= |EVALEXPR_0| b_9_0)) +(check-sat) +(get-value (|EVALEXPR_0| )) +","0x4d368a1e0f051bee84d8e64e660d7d50d57486e66c037dcdb97b06447bbcfb8e":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) + +(assert (and (and (and true true) (and (= expr_15_0 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true))))) (not expr_15_0))) +(check-sat) +","0xab73091601c574bdace0ae9a7fc088a8f13ff47d4b78323c2df81da0281c9df4":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) + +(assert (and (and (and true true) (and (= expr_15_0 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true))))) expr_15_0)) +(check-sat) +"}},"errors":[{"component":"general","errorCode":"6328","formattedMessage":"Warning: CHC: Assertion violation might happen here. + --> A:11:7: + | +11 | \t\t\t\t\t\tassert(s.x > 0); + | \t\t\t\t\t\t^^^^^^^^^^^^^^^ + +","message":"CHC: Assertion violation might happen here.","severity":"warning","sourceLocation":{"end":201,"file":"A","start":186},"type":"Warning"},{"component":"general","errorCode":"7812","formattedMessage":"Warning: BMC: Assertion violation might happen here. + --> A:11:7: + | +11 | \t\t\t\t\t\tassert(s.x > 0); + | \t\t\t\t\t\t^^^^^^^^^^^^^^^ +Note: + +","message":"BMC: Assertion violation might happen here.","secondarySourceLocations":[{"message":""}],"severity":"warning","sourceLocation":{"end":201,"file":"A","start":186},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_true_bmc/input.json b/test/cmdlineTests/standard_model_checker_show_unproved_true_bmc/input.json new file mode 100644 index 000000000..2f367ae80 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_true_bmc/input.json @@ -0,0 +1,27 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\n\ncontract test { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } + }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "bmc", + "showUnproved": true + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_true_bmc/output.json b/test/cmdlineTests/standard_model_checker_show_unproved_true_bmc/output.json new file mode 100644 index 000000000..d21389103 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_true_bmc/output.json @@ -0,0 +1,93 @@ +{"auxiliaryInputRequested":{"smtlib2queries":{"0x119e9d636624c5af8dc0d97ee8d2905551bfe9eea88d60c6d3793cfdc576a76b":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) +(declare-fun |expr_15_1| () Bool) +(declare-fun |expr_16_0| () Int) +(declare-fun |expr_17_0| () Int) +(declare-fun |expr_18_0| () Int) +(declare-fun |expr_12_0| () |struct test.S|) +(declare-fun |expr_14_1| () Int) +(declare-fun |expr_19_1| () Int) +(declare-fun |expr_12_1| () |struct test.S|) +(declare-fun |expr_12_2| () |struct test.S|) +(declare-fun |expr_14_2| () Int) +(declare-fun |s_7_2| () |struct test.S|) +(declare-fun |expr_12_3| () |struct test.S|) +(declare-fun |expr_22_0| () |struct test.S|) +(declare-fun |expr_23_1| () Int) +(declare-fun |expr_24_0| () Int) +(declare-fun |expr_25_1| () Bool) + +(assert (and (and (and true true) (and (= expr_25_1 (> expr_23_1 expr_24_0)) (and (=> (and true true) true) (and (= expr_24_0 0) (and (=> (and true true) (and (>= expr_23_1 0) (<= expr_23_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_23_1 (|struct test.S_accessor_x| expr_22_0)) (and (= expr_22_0 s_7_2) (and (= expr_12_3 s_7_2) (and (ite (and true true) (= s_7_2 expr_12_2) (= s_7_2 s_7_1)) (and (=> (and true true) (and (>= expr_14_2 0) (<= expr_14_2 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_14_2 (|struct test.S_accessor_x| expr_12_2)) (and (= (|struct test.S_accessor_x| expr_12_2) expr_19_1) (and (= expr_12_1 s_7_1) (and (=> (and true true) (and (>= expr_19_1 0) (<= expr_19_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_19_1 (bv2nat (bvor (ite (>= expr_14_1 0) ((_ int2bv 256) expr_14_1) (bvneg ((_ int2bv 256) (- expr_14_1)))) (ite (>= expr_18_0 0) ((_ int2bv 256) expr_18_0) (bvneg ((_ int2bv 256) (- expr_18_0))))))) (and (=> (and true true) (and (>= expr_14_1 0) (<= expr_14_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_14_1 (|struct test.S_accessor_x| expr_12_0)) (and (= expr_12_0 s_7_1) (and (=> (and true true) (and (>= expr_18_0 0) (<= expr_18_0 255))) (and (= expr_18_0 (ite expr_15_1 expr_16_0 expr_17_0)) (and (=> (and (and true true) (not expr_15_1)) true) (and (= expr_17_0 2) (and (=> (and (and true true) expr_15_1) true) (and (= expr_16_0 1) (and (= expr_15_1 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true)))))))))))))))))))))))))))) (not expr_25_1))) +(declare-const |EVALEXPR_0| Bool) +(assert (= |EVALEXPR_0| b_9_0)) +(check-sat) +(get-value (|EVALEXPR_0| )) +","0x4d368a1e0f051bee84d8e64e660d7d50d57486e66c037dcdb97b06447bbcfb8e":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) + +(assert (and (and (and true true) (and (= expr_15_0 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true))))) (not expr_15_0))) +(check-sat) +","0xab73091601c574bdace0ae9a7fc088a8f13ff47d4b78323c2df81da0281c9df4":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) + +(assert (and (and (and true true) (and (= expr_15_0 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true))))) expr_15_0)) +(check-sat) +"}},"errors":[{"component":"general","errorCode":"7812","formattedMessage":"Warning: BMC: Assertion violation might happen here. + --> A:11:7: + | +11 | \t\t\t\t\t\tassert(s.x > 0); + | \t\t\t\t\t\t^^^^^^^^^^^^^^^ +Note: + +","message":"BMC: Assertion violation might happen here.","secondarySourceLocations":[{"message":""}],"severity":"warning","sourceLocation":{"end":201,"file":"A","start":186},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_true_chc/input.json b/test/cmdlineTests/standard_model_checker_show_unproved_true_chc/input.json new file mode 100644 index 000000000..f81688b71 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_true_chc/input.json @@ -0,0 +1,27 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\n\ncontract test { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } + }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "chc", + "showUnproved": true + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_true_chc/output.json b/test/cmdlineTests/standard_model_checker_show_unproved_true_chc/output.json new file mode 100644 index 000000000..352b76d27 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_true_chc/output.json @@ -0,0 +1,7 @@ +{"errors":[{"component":"general","errorCode":"6328","formattedMessage":"Warning: CHC: Assertion violation might happen here. + --> A:11:7: + | +11 | \t\t\t\t\t\tassert(s.x > 0); + | \t\t\t\t\t\t^^^^^^^^^^^^^^^ + +","message":"CHC: Assertion violation might happen here.","severity":"warning","sourceLocation":{"end":201,"file":"A","start":186},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_wrong/input.json b/test/cmdlineTests/standard_model_checker_show_unproved_wrong/input.json new file mode 100644 index 000000000..bdaec9b3e --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_wrong/input.json @@ -0,0 +1,27 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\n\ncontract test { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } + }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "all", + "showUnproved": "aaa" + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_wrong/output.json b/test/cmdlineTests/standard_model_checker_show_unproved_wrong/output.json new file mode 100644 index 000000000..394dbf925 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_wrong/output.json @@ -0,0 +1 @@ +{"errors":[{"component":"general","formattedMessage":"settings.modelChecker.showUnproved must be a Boolean value.","message":"settings.modelChecker.showUnproved must be a Boolean value.","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_model_checker_timeout_all/output.json b/test/cmdlineTests/standard_model_checker_timeout_all/output.json index 15f7ad969..335a6f99e 100644 --- a/test/cmdlineTests/standard_model_checker_timeout_all/output.json +++ b/test/cmdlineTests/standard_model_checker_timeout_all/output.json @@ -254,17 +254,8 @@ (assert (= |EVALEXPR_3| r_33_1)) (check-sat) (get-value (|EVALEXPR_0| |EVALEXPR_1| |EVALEXPR_2| |EVALEXPR_3| )) -"}},"errors":[{"component":"general","errorCode":"6328","formattedMessage":"Warning: CHC: Assertion violation might happen here. - --> A:6:85: - | -6 | require(k > 0); require(x % k == 0); require(y % k == 0); uint r = mulmod(x, y, k); assert(r % k == 0);}} - | ^^^^^^^^^^^^^^^^^^ +"}},"errors":[{"component":"general","errorCode":"5840","formattedMessage":"Warning: CHC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. -","message":"CHC: Assertion violation might happen here.","severity":"warning","sourceLocation":{"end":227,"file":"A","start":209},"type":"Warning"},{"component":"general","errorCode":"7812","formattedMessage":"Warning: BMC: Assertion violation might happen here. - --> A:6:85: - | -6 | require(k > 0); require(x % k == 0); require(y % k == 0); uint r = mulmod(x, y, k); assert(r % k == 0);}} - | ^^^^^^^^^^^^^^^^^^ -Note: +","message":"CHC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query.","severity":"warning","type":"Warning"},{"component":"general","errorCode":"2788","formattedMessage":"Warning: BMC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. -","message":"BMC: Assertion violation might happen here.","secondarySourceLocations":[{"message":""}],"severity":"warning","sourceLocation":{"end":227,"file":"A","start":209},"type":"Warning"}],"sources":{"A":{"id":0}}} +","message":"BMC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query.","severity":"warning","type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_timeout_bmc/output.json b/test/cmdlineTests/standard_model_checker_timeout_bmc/output.json index 0c3de2823..c1631811f 100644 --- a/test/cmdlineTests/standard_model_checker_timeout_bmc/output.json +++ b/test/cmdlineTests/standard_model_checker_timeout_bmc/output.json @@ -518,11 +518,6 @@ (assert (and (and (and true true) (and (= expr_21_1 (= expr_19_1 expr_20_0)) (and (=> (and true true) true) (and (= expr_20_0 0) (and (=> (and true true) (and (>= expr_19_1 0) (<= expr_19_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_19_1 (ite (= expr_18_0 0) 0 r_div_mod_0_0)) (and (and (<= 0 r_div_mod_0_0) (or (= expr_18_0 0) (< r_div_mod_0_0 expr_18_0))) (and (= (+ (* d_div_mod_0_0 expr_18_0) r_div_mod_0_0) expr_17_0) (and (=> (and true true) (and (>= expr_18_0 0) (<= expr_18_0 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_18_0 k_7_0) (and (=> (and true true) (and (>= expr_17_0 0) (<= expr_17_0 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_17_0 x_3_0) (and (=> (and true true) expr_13_1) (and (= expr_13_1 (> expr_11_0 expr_12_0)) (and (=> (and true true) true) (and (= expr_12_0 0) (and (=> (and true true) (and (>= expr_11_0 0) (<= expr_11_0 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_11_0 k_7_0) (and (and (>= k_7_0 0) (<= k_7_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= y_5_0 0) (<= y_5_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= x_3_0 0) (<= x_3_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (= r_33_0 0) (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 3204897777)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 191)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 6)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 219)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 241)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true))))))))))))))))))))))) (not expr_21_1))) (check-sat) -"}},"errors":[{"component":"general","errorCode":"7812","formattedMessage":"Warning: BMC: Assertion violation might happen here. - --> A:6:85: - | -6 | require(k > 0); require(x % k == 0); require(y % k == 0); uint r = mulmod(x, y, k); assert(r % k == 0);}} - | ^^^^^^^^^^^^^^^^^^ -Note: +"}},"errors":[{"component":"general","errorCode":"2788","formattedMessage":"Warning: BMC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. -","message":"BMC: Assertion violation might happen here.","secondarySourceLocations":[{"message":""}],"severity":"warning","sourceLocation":{"end":227,"file":"A","start":209},"type":"Warning"}],"sources":{"A":{"id":0}}} +","message":"BMC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query.","severity":"warning","type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_timeout_chc/output.json b/test/cmdlineTests/standard_model_checker_timeout_chc/output.json index 34de85f8f..5e66cb899 100644 --- a/test/cmdlineTests/standard_model_checker_timeout_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_timeout_chc/output.json @@ -1,7 +1,3 @@ -{"errors":[{"component":"general","errorCode":"6328","formattedMessage":"Warning: CHC: Assertion violation might happen here. - --> A:6:85: - | -6 | require(k > 0); require(x % k == 0); require(y % k == 0); uint r = mulmod(x, y, k); assert(r % k == 0);}} - | ^^^^^^^^^^^^^^^^^^ +{"errors":[{"component":"general","errorCode":"5840","formattedMessage":"Warning: CHC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. -","message":"CHC: Assertion violation might happen here.","severity":"warning","sourceLocation":{"end":227,"file":"A","start":209},"type":"Warning"}],"sources":{"A":{"id":0}}} +","message":"CHC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query.","severity":"warning","type":"Warning"}],"sources":{"A":{"id":0}}} From 3c1f555f713ca6cf1b17af938a21dd71b6fff4cc Mon Sep 17 00:00:00 2001 From: Leo Alt Date: Fri, 2 Jul 2021 15:05:24 +0200 Subject: [PATCH 95/98] Tests --- libsolidity/interface/StandardCompiler.cpp | 2 +- test/libsolidity/SMTCheckerTest.cpp | 8 +++++ .../abi/abi_encode_packed_string_literal.sol | 8 ++--- ...code_packed_string_literal_no_unproved.sol | 35 +++++++++++++++++++ .../abi/abi_encode_string_literal.sol | 6 ++-- .../abi_encode_with_selector_array_slice.sol | 6 ++-- ...abi_encode_with_selector_array_slice_2.sol | 6 ++-- .../abi/abi_encode_with_selector_hash.sol | 2 +- ...bi_encode_with_selector_string_literal.sol | 8 ++--- .../abi/abi_encode_with_sig_array_slice.sol | 6 ++-- .../abi/abi_encode_with_sig_array_slice_2.sol | 6 ++-- .../abi/abi_encode_with_sig_hash.sol | 2 +- .../abi_encode_with_sig_string_literal.sol | 8 ++--- .../compound_bitwise_or_uint_3.sol | 15 ++++++++ ...same_input_over_state_same_output_fail.sol | 6 ++-- .../external_calls/external_inc.sol | 1 - .../external_reentrancy_crypto.sol | 1 - .../smtCheckerTests/file_level/recursion.sol | 1 - .../functions/constructor_hierarchy_3.sol | 2 +- .../functions/constructor_hierarchy_4.sol | 2 +- .../constructor_hierarchy_diamond_3.sol | 2 +- .../constructor_state_value_parameter.sol | 2 +- .../internal_call_with_assertion_1_fail.sol | 2 +- ...l_multiple_calls_with_assertion_1_fail.sol | 2 +- .../base_contract_assertion_fail_9.sol | 2 +- ...ctor_state_variable_init_chain_run_all.sol | 2 +- ...or_state_variable_init_chain_run_all_2.sol | 2 +- ...tor_state_variable_init_diamond_middle.sol | 2 +- ...or_loop_array_assignment_memory_memory.sol | 2 +- ...act_function_nondet_pow_no_abstraction.sol | 1 - .../overflow/signed_div_overflow.sol | 2 +- .../special/msg_value_inheritance_2.sol | 2 +- ...g_literal_to_fixed_bytes_function_call.sol | 2 +- .../types/array_branches_3d_show_unproved.sol | 25 +++++++++++++ test/solc/CommandLineParser.cpp | 5 +++ 35 files changed, 135 insertions(+), 51 deletions(-) create mode 100644 test/libsolidity/smtCheckerTests/abi/abi_encode_packed_string_literal_no_unproved.sol create mode 100644 test/libsolidity/smtCheckerTests/bmc_coverage/compound_bitwise_or_uint_3.sol create mode 100644 test/libsolidity/smtCheckerTests/types/array_branches_3d_show_unproved.sol diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 56c71f776..0a12059d5 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -958,7 +958,7 @@ std::variant StandardCompiler: return formatFatalError("JSONError", "settings.modelChecker.showUnproved must be a Boolean value."); ret.modelCheckerSettings.showUnproved = showUnproved.asBool(); } - + if (modelCheckerSettings.isMember("solvers")) { auto const& solversArray = modelCheckerSettings["solvers"]; diff --git a/test/libsolidity/SMTCheckerTest.cpp b/test/libsolidity/SMTCheckerTest.cpp index 961baab70..166aa2982 100644 --- a/test/libsolidity/SMTCheckerTest.cpp +++ b/test/libsolidity/SMTCheckerTest.cpp @@ -27,6 +27,14 @@ using namespace solidity::frontend::test; SMTCheckerTest::SMTCheckerTest(string const& _filename): SyntaxTest(_filename, EVMVersion{}) { + auto const& showUnproved = m_reader.stringSetting("SMTShowUnproved", "yes"); + if (showUnproved == "no") + m_modelCheckerSettings.showUnproved = false; + else if (showUnproved == "yes") + m_modelCheckerSettings.showUnproved = true; + else + BOOST_THROW_EXCEPTION(runtime_error("Invalid SMT \"show unproved\" choice.")); + auto const& choice = m_reader.stringSetting("SMTSolvers", "any"); if (choice == "any") m_modelCheckerSettings.solvers = smtutil::SMTSolverChoice::All(); diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_packed_string_literal.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_packed_string_literal.sol index 0598311ad..2bbaa2e1c 100644 --- a/test/libsolidity/smtCheckerTests/abi/abi_encode_packed_string_literal.sol +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_packed_string_literal.sol @@ -22,14 +22,14 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (226-256): CHC: Assertion violation happens here. // Warning 1218: (310-340): CHC: Error trying to invoke SMT solver. -// Warning 6328: (310-340): CHC: Assertion violation might happen here. // Warning 1218: (483-513): CHC: Error trying to invoke SMT solver. -// Warning 6328: (483-513): CHC: Assertion violation might happen here. // Warning 1218: (568-598): CHC: Error trying to invoke SMT solver. -// Warning 6328: (568-598): CHC: Assertion violation might happen here. // Warning 1218: (654-684): CHC: Error trying to invoke SMT solver. +// Warning 6328: (226-256): CHC: Assertion violation happens here. +// Warning 6328: (310-340): CHC: Assertion violation might happen here. +// Warning 6328: (483-513): CHC: Assertion violation might happen here. +// Warning 6328: (568-598): CHC: Assertion violation might happen here. // Warning 6328: (654-684): CHC: Assertion violation might happen here. // Warning 4661: (310-340): BMC: Assertion violation happens here. // Warning 4661: (483-513): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_packed_string_literal_no_unproved.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_packed_string_literal_no_unproved.sol new file mode 100644 index 000000000..ffeef4896 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_packed_string_literal_no_unproved.sol @@ -0,0 +1,35 @@ +contract C { + function abiencodePackedStringLiteral() public pure { + bytes memory b1 = abi.encodePacked(""); + bytes memory b2 = abi.encodePacked(""); + // should hold, but currently fails due to string literal abstraction + assert(b1.length == b2.length); + + bytes memory b3 = abi.encodePacked(bytes("")); + assert(b1.length == b3.length); // should fail + + bytes memory b4 = abi.encodePacked(bytes24("")); + // should hold, but currently fails due to string literal abstraction + assert(b1.length == b4.length); + + bytes memory b5 = abi.encodePacked(string("")); + assert(b1.length == b5.length); // should fail + + bytes memory b6 = abi.encode(""); + assert(b1.length == b6.length); // should fail + } +} +// ==== +// SMTEngine: all +// SMTShowUnproved: no +// ---- +// Warning 1218: (310-340): CHC: Error trying to invoke SMT solver. +// Warning 1218: (483-513): CHC: Error trying to invoke SMT solver. +// Warning 1218: (568-598): CHC: Error trying to invoke SMT solver. +// Warning 1218: (654-684): CHC: Error trying to invoke SMT solver. +// Warning 6328: (226-256): CHC: Assertion violation happens here. +// Warning 5840: CHC: 4 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. +// Warning 4661: (310-340): BMC: Assertion violation happens here. +// Warning 4661: (483-513): BMC: Assertion violation happens here. +// Warning 4661: (568-598): BMC: Assertion violation happens here. +// Warning 4661: (654-684): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_string_literal.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_string_literal.sol index 6eb3fff73..2a3ff2bb2 100644 --- a/test/libsolidity/smtCheckerTests/abi/abi_encode_string_literal.sol +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_string_literal.sol @@ -19,12 +19,12 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (208-238): CHC: Assertion violation happens here. // Warning 1218: (286-316): CHC: Error trying to invoke SMT solver. -// Warning 6328: (286-316): CHC: Assertion violation might happen here. // Warning 1218: (453-483): CHC: Error trying to invoke SMT solver. -// Warning 6328: (453-483): CHC: Assertion violation might happen here. // Warning 1218: (532-562): CHC: Error trying to invoke SMT solver. +// Warning 6328: (208-238): CHC: Assertion violation happens here. +// Warning 6328: (286-316): CHC: Assertion violation might happen here. +// Warning 6328: (453-483): CHC: Assertion violation might happen here. // Warning 6328: (532-562): CHC: Assertion violation might happen here. // Warning 4661: (286-316): BMC: Assertion violation happens here. // Warning 4661: (453-483): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_array_slice.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_array_slice.sol index 0365b9655..c9d9bb056 100644 --- a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_array_slice.sol +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_array_slice.sol @@ -25,13 +25,13 @@ contract C { // ==== // SMTEngine: all // ---- +// Warning 1218: (691-721): CHC: Error trying to invoke SMT solver. +// Warning 1218: (959-989): CHC: Error trying to invoke SMT solver. +// Warning 1218: (1079-1109): CHC: Error trying to invoke SMT solver. // Warning 6328: (325-355): CHC: Assertion violation happens here. // Warning 6328: (578-608): CHC: Assertion violation happens here. -// Warning 1218: (691-721): CHC: Error trying to invoke SMT solver. // Warning 6328: (691-721): CHC: Assertion violation might happen here. -// Warning 1218: (959-989): CHC: Error trying to invoke SMT solver. // Warning 6328: (959-989): CHC: Assertion violation might happen here. -// Warning 1218: (1079-1109): CHC: Error trying to invoke SMT solver. // Warning 6328: (1079-1109): CHC: Assertion violation might happen here. // Warning 4661: (691-721): BMC: Assertion violation happens here. // Warning 4661: (959-989): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_array_slice_2.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_array_slice_2.sol index b94977030..740411bf3 100644 --- a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_array_slice_2.sol +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_array_slice_2.sol @@ -25,13 +25,13 @@ contract C { // ==== // SMTEngine: all // ---- +// Warning 1218: (692-722): CHC: Error trying to invoke SMT solver. +// Warning 1218: (960-990): CHC: Error trying to invoke SMT solver. +// Warning 1218: (1080-1110): CHC: Error trying to invoke SMT solver. // Warning 6328: (326-356): CHC: Assertion violation happens here. // Warning 6328: (579-609): CHC: Assertion violation happens here. -// Warning 1218: (692-722): CHC: Error trying to invoke SMT solver. // Warning 6328: (692-722): CHC: Assertion violation might happen here. -// Warning 1218: (960-990): CHC: Error trying to invoke SMT solver. // Warning 6328: (960-990): CHC: Assertion violation might happen here. -// Warning 1218: (1080-1110): CHC: Error trying to invoke SMT solver. // Warning 6328: (1080-1110): CHC: Assertion violation might happen here. // Warning 4661: (692-722): BMC: Assertion violation happens here. // Warning 4661: (960-990): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_hash.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_hash.sol index 3360feb02..ca1b6b40a 100644 --- a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_hash.sol +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_hash.sol @@ -14,8 +14,8 @@ contract C { // SMTEngine: all // ---- // Warning 1218: (333-371): CHC: Error trying to invoke SMT solver. -// Warning 6328: (333-371): CHC: Assertion violation might happen here. // Warning 1218: (390-428): CHC: Error trying to invoke SMT solver. +// Warning 6328: (333-371): CHC: Assertion violation might happen here. // Warning 6328: (390-428): CHC: Assertion violation might happen here. // Warning 4661: (333-371): BMC: Assertion violation happens here. // Warning 4661: (390-428): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_string_literal.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_string_literal.sol index e824c57d1..839d00835 100644 --- a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_string_literal.sol +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_string_literal.sol @@ -22,14 +22,14 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (252-282): CHC: Assertion violation happens here. // Warning 1218: (347-377): CHC: Error trying to invoke SMT solver. -// Warning 6328: (347-377): CHC: Assertion violation might happen here. // Warning 1218: (531-561): CHC: Error trying to invoke SMT solver. -// Warning 6328: (531-561): CHC: Assertion violation might happen here. // Warning 1218: (627-657): CHC: Error trying to invoke SMT solver. -// Warning 6328: (627-657): CHC: Assertion violation might happen here. // Warning 1218: (746-776): CHC: Error trying to invoke SMT solver. +// Warning 6328: (252-282): CHC: Assertion violation happens here. +// Warning 6328: (347-377): CHC: Assertion violation might happen here. +// Warning 6328: (531-561): CHC: Assertion violation might happen here. +// Warning 6328: (627-657): CHC: Assertion violation might happen here. // Warning 6328: (746-776): CHC: Assertion violation might happen here. // Warning 4661: (347-377): BMC: Assertion violation happens here. // Warning 4661: (531-561): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_array_slice.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_array_slice.sol index 7530ea6c5..fd15ea27d 100644 --- a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_array_slice.sol +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_array_slice.sol @@ -25,13 +25,13 @@ contract C { // ==== // SMTEngine: all // ---- +// Warning 1218: (702-732): CHC: Error trying to invoke SMT solver. +// Warning 1218: (971-1001): CHC: Error trying to invoke SMT solver. +// Warning 1218: (1086-1116): CHC: Error trying to invoke SMT solver. // Warning 6328: (334-364): CHC: Assertion violation happens here. // Warning 6328: (588-618): CHC: Assertion violation happens here. -// Warning 1218: (702-732): CHC: Error trying to invoke SMT solver. // Warning 6328: (702-732): CHC: Assertion violation might happen here. -// Warning 1218: (971-1001): CHC: Error trying to invoke SMT solver. // Warning 6328: (971-1001): CHC: Assertion violation might happen here. -// Warning 1218: (1086-1116): CHC: Error trying to invoke SMT solver. // Warning 6328: (1086-1116): CHC: Assertion violation might happen here. // Warning 4661: (702-732): BMC: Assertion violation happens here. // Warning 4661: (971-1001): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_array_slice_2.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_array_slice_2.sol index f9da7be6b..cdb87233e 100644 --- a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_array_slice_2.sol +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_array_slice_2.sol @@ -25,13 +25,13 @@ contract C { // ==== // SMTEngine: all // ---- +// Warning 1218: (703-733): CHC: Error trying to invoke SMT solver. +// Warning 1218: (972-1002): CHC: Error trying to invoke SMT solver. +// Warning 1218: (1087-1117): CHC: Error trying to invoke SMT solver. // Warning 6328: (335-365): CHC: Assertion violation happens here. // Warning 6328: (589-619): CHC: Assertion violation happens here. -// Warning 1218: (703-733): CHC: Error trying to invoke SMT solver. // Warning 6328: (703-733): CHC: Assertion violation might happen here. -// Warning 1218: (972-1002): CHC: Error trying to invoke SMT solver. // Warning 6328: (972-1002): CHC: Assertion violation might happen here. -// Warning 1218: (1087-1117): CHC: Error trying to invoke SMT solver. // Warning 6328: (1087-1117): CHC: Assertion violation might happen here. // Warning 4661: (703-733): BMC: Assertion violation happens here. // Warning 4661: (972-1002): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_hash.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_hash.sol index a76f91f81..dbf423741 100644 --- a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_hash.sol +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_hash.sol @@ -14,8 +14,8 @@ contract C { // SMTEngine: all // ---- // Warning 1218: (337-375): CHC: Error trying to invoke SMT solver. -// Warning 6328: (337-375): CHC: Assertion violation might happen here. // Warning 1218: (394-432): CHC: Error trying to invoke SMT solver. +// Warning 6328: (337-375): CHC: Assertion violation might happen here. // Warning 6328: (394-432): CHC: Assertion violation might happen here. // Warning 4661: (337-375): BMC: Assertion violation happens here. // Warning 4661: (394-432): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_string_literal.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_string_literal.sol index 666fa23ba..2e64229bc 100644 --- a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_string_literal.sol +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_string_literal.sol @@ -22,14 +22,14 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (261-291): CHC: Assertion violation happens here. // Warning 1218: (357-387): CHC: Error trying to invoke SMT solver. -// Warning 6328: (357-387): CHC: Assertion violation might happen here. // Warning 1218: (542-572): CHC: Error trying to invoke SMT solver. -// Warning 6328: (542-572): CHC: Assertion violation might happen here. // Warning 1218: (639-669): CHC: Error trying to invoke SMT solver. -// Warning 6328: (639-669): CHC: Assertion violation might happen here. // Warning 1218: (753-783): CHC: Error trying to invoke SMT solver. +// Warning 6328: (261-291): CHC: Assertion violation happens here. +// Warning 6328: (357-387): CHC: Assertion violation might happen here. +// Warning 6328: (542-572): CHC: Assertion violation might happen here. +// Warning 6328: (639-669): CHC: Assertion violation might happen here. // Warning 6328: (753-783): CHC: Assertion violation might happen here. // Warning 4661: (357-387): BMC: Assertion violation happens here. // Warning 4661: (542-572): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/compound_bitwise_or_uint_3.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/compound_bitwise_or_uint_3.sol new file mode 100644 index 000000000..02d538d7d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/compound_bitwise_or_uint_3.sol @@ -0,0 +1,15 @@ +contract C { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } +} +// ==== +// SMTEngine: bmc +// SMTShowUnproved: no +// ---- +// Warning 2788: BMC: 1 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. diff --git a/test/libsolidity/smtCheckerTests/crypto/crypto_functions_same_input_over_state_same_output_fail.sol b/test/libsolidity/smtCheckerTests/crypto/crypto_functions_same_input_over_state_same_output_fail.sol index 865ec694b..fd3b55a8c 100644 --- a/test/libsolidity/smtCheckerTests/crypto/crypto_functions_same_input_over_state_same_output_fail.sol +++ b/test/libsolidity/smtCheckerTests/crypto/crypto_functions_same_input_over_state_same_output_fail.sol @@ -46,12 +46,12 @@ contract C { // SMTEngine: all // ---- // Warning 1218: (693-712): CHC: Error trying to invoke SMT solver. -// Warning 6328: (693-712): CHC: Assertion violation might happen here. // Warning 1218: (716-735): CHC: Error trying to invoke SMT solver. -// Warning 6328: (716-735): CHC: Assertion violation might happen here. // Warning 1218: (739-758): CHC: Error trying to invoke SMT solver. -// Warning 6328: (739-758): CHC: Assertion violation might happen here. // Warning 1218: (762-781): CHC: Error trying to invoke SMT solver. +// Warning 6328: (693-712): CHC: Assertion violation might happen here. +// Warning 6328: (716-735): CHC: Assertion violation might happen here. +// Warning 6328: (739-758): CHC: Assertion violation might happen here. // Warning 6328: (762-781): CHC: Assertion violation might happen here. // Warning 4661: (693-712): BMC: Assertion violation happens here. // Warning 4661: (716-735): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_inc.sol b/test/libsolidity/smtCheckerTests/external_calls/external_inc.sol index 3c1777d85..22a4551c8 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/external_inc.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/external_inc.sol @@ -20,6 +20,5 @@ contract C { // SMTIgnoreCex: yes // ---- // Warning 4984: (113-116): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. -// Warning 4984: (113-116): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. // Warning 6328: (156-170): CHC: Assertion violation happens here. // Warning 2661: (113-116): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_reentrancy_crypto.sol b/test/libsolidity/smtCheckerTests/external_calls/external_reentrancy_crypto.sol index 7c9ba2d5c..2437d9829 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/external_reentrancy_crypto.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/external_reentrancy_crypto.sol @@ -27,7 +27,6 @@ contract C { // SMTEngine: all // ---- // Warning 1218: (302-333): CHC: Error trying to invoke SMT solver. -// Warning 6328: (302-333): CHC: Assertion violation might happen here. // Warning 1218: (302-333): CHC: Error trying to invoke SMT solver. // Warning 6328: (302-333): CHC: Assertion violation might happen here. // Warning 4661: (302-333): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/file_level/recursion.sol b/test/libsolidity/smtCheckerTests/file_level/recursion.sol index f461861e9..34a26d326 100644 --- a/test/libsolidity/smtCheckerTests/file_level/recursion.sol +++ b/test/libsolidity/smtCheckerTests/file_level/recursion.sol @@ -25,4 +25,3 @@ contract C { } // ==== // SMTEngine: all -// ---- diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol index 067ac0218..84fdaf87d 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol @@ -21,5 +21,5 @@ contract A is B { // SMTEngine: all // SMTIgnoreCex: yes // ---- -// Warning 6328: (200-218): CHC: Assertion violation happens here. // Warning 4984: (171-176): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 6328: (200-218): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_4.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_4.sol index 165740218..b7cacc0f6 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_4.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_4.sol @@ -19,5 +19,5 @@ contract A is B { // ==== // SMTEngine: all // ---- -// Warning 4984: (175-180): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\na = 0\nx = 115792089237316195423570985008687907853269984665640564039457584007913129639935\n\nTransaction trace:\nA.constructor(115792089237316195423570985008687907853269984665640564039457584007913129639935) // Warning 4984: (166-171): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\na = 0\nx = 115792089237316195423570985008687907853269984665640564039457584007913129639934\n\nTransaction trace:\nA.constructor(115792089237316195423570985008687907853269984665640564039457584007913129639934) +// Warning 4984: (175-180): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\na = 0\nx = 115792089237316195423570985008687907853269984665640564039457584007913129639935\n\nTransaction trace:\nA.constructor(115792089237316195423570985008687907853269984665640564039457584007913129639935) diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol index 98da0a2a6..6a2b07919 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol @@ -29,6 +29,6 @@ contract A is B2, B1 { // SMTEngine: all // SMTIgnoreCex: yes // ---- -// Warning 4984: (209-214): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. // Warning 4984: (193-198): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 4984: (209-214): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. // Warning 6328: (302-318): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_state_value_parameter.sol b/test/libsolidity/smtCheckerTests/functions/constructor_state_value_parameter.sol index 414809209..8fd9bf9a2 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_state_value_parameter.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_state_value_parameter.sol @@ -14,5 +14,5 @@ contract C { // SMTEngine: all // SMTIgnoreCex: yes // ---- -// Warning 6328: (129-143): CHC: Assertion violation happens here. // Warning 4984: (82-87): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 6328: (129-143): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1_fail.sol b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1_fail.sol index cf31fa9e5..1fe537aad 100644 --- a/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1_fail.sol @@ -21,8 +21,8 @@ contract C{ // SMTEngine: all // ---- // Warning 5667: (37-43): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning 6328: (49-63): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\ny = 0\n\nTransaction trace:\nC.constructor(0) // Warning 6328: (105-119): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\nTransaction trace:\nC.constructor(0)\nState: x = 1\nC.f() // Warning 6328: (137-151): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\nTransaction trace:\nC.constructor(0)\nState: x = 1\nC.f()\n C.g() -- internal call // Warning 6328: (187-201): CHC: Assertion violation happens here.\nCounterexample:\nx = 2\n\nTransaction trace:\nC.constructor(0)\nState: x = 1\nC.f()\n C.g() -- internal call // Warning 6328: (212-226): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\nTransaction trace:\nC.constructor(0)\nState: x = 1\nC.f()\n C.g() -- internal call -// Warning 6328: (49-63): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\ny = 0\n\nTransaction trace:\nC.constructor(0) diff --git a/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1_fail.sol b/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1_fail.sol index 2e934a0f2..f03e1b4eb 100644 --- a/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1_fail.sol @@ -21,6 +21,6 @@ contract C{ // SMTEngine: all // ---- // Warning 5667: (37-43): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning 6328: (49-63): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\ny = 0\n\nTransaction trace:\nC.constructor(0) // Warning 6328: (105-119): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\nTransaction trace:\nC.constructor(0)\nState: x = 1\nC.f() // Warning 6328: (151-165): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\nTransaction trace:\nC.constructor(0)\nState: x = 1\nC.f()\n C.g() -- internal call\n C.g() -- internal call -// Warning 6328: (49-63): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\ny = 0\n\nTransaction trace:\nC.constructor(0) diff --git a/test/libsolidity/smtCheckerTests/inheritance/base_contract_assertion_fail_9.sol b/test/libsolidity/smtCheckerTests/inheritance/base_contract_assertion_fail_9.sol index 83f8e609c..f97bae5cd 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/base_contract_assertion_fail_9.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/base_contract_assertion_fail_9.sol @@ -28,5 +28,5 @@ contract C is B { // ==== // SMTEngine: all // ---- -// Warning 6328: (131-145): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\nTransaction trace:\nA.constructor()\nState: x = 0\nA.f()\n A.v() -- internal call // Warning 6328: (62-76): CHC: Assertion violation happens here.\nCounterexample:\nx = 2\n\nTransaction trace:\nC.constructor()\nState: x = 0\nB.f()\n A.f() -- internal call\n C.v() -- internal call +// Warning 6328: (131-145): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\nTransaction trace:\nA.constructor()\nState: x = 0\nA.f()\n A.v() -- internal call diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol index f3a6393fa..f02e416a4 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol @@ -23,6 +23,6 @@ contract A is B { // SMTEngine: all // SMTIgnoreCex: yes // ---- -// Warning 6328: (243-261): CHC: Assertion violation happens here. // Warning 4984: (125-130): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. // Warning 4984: (184-189): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 6328: (243-261): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol index d20358525..e223ad5f7 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol @@ -23,6 +23,6 @@ contract A is B { // SMTEngine: all // SMTIgnoreCex: yes // ---- -// Warning 6328: (241-259): CHC: Assertion violation happens here. // Warning 4984: (125-131): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. // Warning 4984: (185-190): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 6328: (241-259): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_diamond_middle.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_diamond_middle.sol index 940ff64b3..c7c63f00f 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_diamond_middle.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_diamond_middle.sol @@ -25,5 +25,5 @@ contract D is B, C { // ==== // SMTEngine: all // ---- -// Warning 6328: (223-237): CHC: Assertion violation happens here.\nCounterexample:\nx = 3\n\nTransaction trace:\nD.constructor() // Warning 6328: (134-148): CHC: Assertion violation happens here.\nCounterexample:\nx = 2\n\nTransaction trace:\nD.constructor() +// Warning 6328: (223-237): CHC: Assertion violation happens here.\nCounterexample:\nx = 3\n\nTransaction trace:\nD.constructor() diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_memory.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_memory.sol index 88fe7f51f..02a66575a 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_memory.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_memory.sol @@ -24,5 +24,5 @@ contract LoopFor2 { // ---- // Warning 2072: (202-217): Unused local variable. // Warning 6368: (354-358): CHC: Out of bounds access happens here. -// Warning 6368: (378-382): CHC: Out of bounds access happens here. // Warning 6368: (371-375): CHC: Out of bounds access happens here. +// Warning 6368: (378-382): CHC: Out of bounds access happens here. diff --git a/test/libsolidity/smtCheckerTests/natspec/abstract_function_nondet_pow_no_abstraction.sol b/test/libsolidity/smtCheckerTests/natspec/abstract_function_nondet_pow_no_abstraction.sol index dc4fc9ef1..2a2f16c47 100644 --- a/test/libsolidity/smtCheckerTests/natspec/abstract_function_nondet_pow_no_abstraction.sol +++ b/test/libsolidity/smtCheckerTests/natspec/abstract_function_nondet_pow_no_abstraction.sol @@ -35,4 +35,3 @@ contract C { } // ==== // SMTEngine: chc -// ---- diff --git a/test/libsolidity/smtCheckerTests/overflow/signed_div_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/signed_div_overflow.sol index e617e274e..d7a309405 100644 --- a/test/libsolidity/smtCheckerTests/overflow/signed_div_overflow.sol +++ b/test/libsolidity/smtCheckerTests/overflow/signed_div_overflow.sol @@ -7,5 +7,5 @@ contract C { // SMTEngine: all // SMTIgnoreCex: yes // ---- -// Warning 4281: (77-82): CHC: Division by zero happens here. // Warning 4984: (77-82): CHC: Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here. +// Warning 4281: (77-82): CHC: Division by zero happens here. diff --git a/test/libsolidity/smtCheckerTests/special/msg_value_inheritance_2.sol b/test/libsolidity/smtCheckerTests/special/msg_value_inheritance_2.sol index f1bcf66b6..efc59fcfc 100644 --- a/test/libsolidity/smtCheckerTests/special/msg_value_inheritance_2.sol +++ b/test/libsolidity/smtCheckerTests/special/msg_value_inheritance_2.sol @@ -14,5 +14,5 @@ contract C is A { // ==== // SMTEngine: all // ---- -// Warning 6328: (240-254): CHC: Assertion violation happens here.\nCounterexample:\nv = 1, x = 1\n\nTransaction trace:\nC.constructor(){ value: 1 } // Warning 6328: (60-74): CHC: Assertion violation happens here.\nCounterexample:\nv = 0, x = 1\n\nTransaction trace:\nC.constructor(){ value: 1 } +// Warning 6328: (240-254): CHC: Assertion violation happens here.\nCounterexample:\nv = 1, x = 1\n\nTransaction trace:\nC.constructor(){ value: 1 } diff --git a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol index 10fd28ee3..7ce444357 100644 --- a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol +++ b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol @@ -11,5 +11,5 @@ contract B { // SMTEngine: all // SMTIgnoreCex: yes // ---- -// Warning 6328: (130-152): CHC: Assertion violation happens here. // Warning 6328: (104-126): CHC: Assertion violation happens here. +// Warning 6328: (130-152): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/types/array_branches_3d_show_unproved.sol b/test/libsolidity/smtCheckerTests/types/array_branches_3d_show_unproved.sol new file mode 100644 index 000000000..6ec629c9f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/array_branches_3d_show_unproved.sol @@ -0,0 +1,25 @@ +contract C +{ + uint[][][] c; + constructor() { + c.push(); + c[0].push(); + c[0][0].push(); + } + function f(bool b) public { + c[0][0][0] = 0; + if (b) + c[0][0][0] = 1; + assert(c[0][0][0] < 2); + } +} +// ==== +// SMTEngine: all +// SMTShowUnproved: yes +// ---- +// Warning 6368: (124-131): CHC: Out of bounds access might happen here. +// Warning 6368: (124-134): CHC: Out of bounds access might happen here. +// Warning 6368: (152-159): CHC: Out of bounds access might happen here. +// Warning 6368: (152-162): CHC: Out of bounds access might happen here. +// Warning 6368: (177-184): CHC: Out of bounds access might happen here. +// Warning 6368: (177-187): CHC: Out of bounds access might happen here. diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp index 02444ce5d..bb427232c 100644 --- a/test/solc/CommandLineParser.cpp +++ b/test/solc/CommandLineParser.cpp @@ -82,6 +82,7 @@ BOOST_AUTO_TEST_CASE(no_options) expectedOptions.modelChecker.settings = { ModelCheckerContracts::Default(), ModelCheckerEngine::None(), + false, smtutil::SMTSolverChoice::All(), ModelCheckerTargets::Default(), nullopt, @@ -151,6 +152,7 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) "--yul-optimizations=agf", "--model-checker-contracts=contract1.yul:A,contract2.yul:B", "--model-checker-engine=bmc", + "--model-checker-show-unproved=true", "--model-checker-solvers=z3,smtlib2", "--model-checker-targets=underflow,divByZero", "--model-checker-timeout=5", @@ -209,6 +211,7 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) expectedOptions.modelChecker.settings = { {{{"contract1.yul", {"A"}}, {"contract2.yul", {"B"}}}}, {true, false}, + true, {false, true, true}, {{VerificationTargetType::Underflow, VerificationTargetType::DivByZero}}, 5, @@ -279,6 +282,7 @@ BOOST_AUTO_TEST_CASE(assembly_mode_options) "contract1.yul:A," "contract2.yul:B", "--model-checker-engine=bmc", // Ignored in assembly mode + "--model-checker-show-unproved=true", // Ignored in assembly mode "--model-checker-solvers=z3,smtlib2", // Ignored in assembly mode "--model-checker-targets=" // Ignored in assembly mode "underflow," @@ -377,6 +381,7 @@ BOOST_AUTO_TEST_CASE(standard_json_mode_options) "contract1.yul:A," "contract2.yul:B", "--model-checker-engine=bmc", // Ignored in Standard JSON mode + "--model-checker-show-unproved=true", // Ignored in Standard JSON mode "--model-checker-solvers=z3,smtlib2", // Ignored in Standard JSON mode "--model-checker-targets=" // Ignored in Standard JSON mode "underflow," From 847dd5cf926b6e9e30a9140ee83ab0e34280729f Mon Sep 17 00:00:00 2001 From: Leo Alt Date: Wed, 28 Jul 2021 18:58:03 +0200 Subject: [PATCH 96/98] Docs --- docs/smtchecker.rst | 8 ++++++++ docs/using-the-compiler.rst | 2 ++ 2 files changed, 10 insertions(+) diff --git a/docs/smtchecker.rst b/docs/smtchecker.rst index 685f5e9fc..e2b61cf35 100644 --- a/docs/smtchecker.rst +++ b/docs/smtchecker.rst @@ -474,6 +474,14 @@ A common subset of targets might be, for example: There is no precise heuristic on how and when to split verification targets, but it can be useful especially when dealing with large contracts. +Unproved Targets +================ + +If there are any unproved targets, the SMTChecker issues one warning stating +how many unproved targets there are. If the user wishes to see all the specific +unproved targets, the CLI option ``--model-checker-show-unproved true`` and +the JSON option ``settings.modelChecker.showUnproved = true`` can be used. + Verified Contracts ================== diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 9ae6b15c9..381f150cd 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -402,6 +402,8 @@ Input Description }, // Choose which model checker engine to use: all (default), bmc, chc, none. "engine": "chc", + // Choose whether to output all unproved targets. The default is `false`. + "showUnproved": true, // Choose which targets should be checked: constantCondition, // underflow, overflow, divByZero, balance, assert, popEmptyArray, outOfBounds. // If the option is not given all targets are checked by default. From 01c01ea421c0675c2dbfef6eebc47cfb34599771 Mon Sep 17 00:00:00 2001 From: Leo Alt Date: Wed, 28 Jul 2021 18:59:35 +0200 Subject: [PATCH 97/98] Changelog --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 3546a754f..09ef7db60 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,7 @@ Compiler Features: * Yul EVM Code Transform: Also pop unused argument slots for functions without return variables (under the same restrictions as for functions with return variables). * Yul Optimizer: Move function arguments and return variables to memory with the experimental Stack Limit Evader (which is not enabled by default). * Commandline Interface: option ``--pretty-json`` works also with ``--standard--json``. + * SMTChecker: Unproved targets are hidden by default, and the SMTChecker only states how many unproved targets there are. They can be listed using the command line option ``--model-checker-show-unproved`` or the JSON option ``settings.modelChecker.showUnproved``. Bugfixes: From dea201800fd4e26b9a70671721e98fb9e0222e06 Mon Sep 17 00:00:00 2001 From: Marenz Date: Mon, 12 Jul 2021 16:31:29 +0200 Subject: [PATCH 98/98] Provide a better error reporting for failed cmdline tests. --- scripts/common_cmdline.sh | 61 +++++++++++++++++----------- scripts/docs_version_pragma_check.sh | 4 +- test/cmdlineTests.sh | 8 ++-- 3 files changed, 44 insertions(+), 29 deletions(-) diff --git a/scripts/common_cmdline.sh b/scripts/common_cmdline.sh index 7b452d857..59c334a4b 100644 --- a/scripts/common_cmdline.sh +++ b/scripts/common_cmdline.sh @@ -25,20 +25,24 @@ OLDARGS=(--optimize --combined-json "abi,asm,ast,bin,bin-runtime,devdoc,interfac function compileFull() { local expected_exit_code=0 - local expect_output=0 - if [[ $1 = '-e' ]]; then - expected_exit_code=1 - expect_output=1 - shift; - fi - if [[ $1 = '-w' ]]; then - expect_output=1 - shift; - fi - if [[ $1 = '-o' ]]; then - expect_output=2 - shift; - fi + local expect_output='none' + + case "$1" in + '--expect-errors') + expected_exit_code=1 + expect_output='warnings-or-errors' + shift; + ;; + '--expect-warnings') + expect_output='warnings-or-errors' + shift; + ;; + '--ignore-warnings') + expect_output='any' + shift; + ;; + esac + local args=("${FULLARGS[@]}") if [[ $1 = '-v' ]]; then if (echo "$2" | grep -Po '(?<=0.4.)\d+' >/dev/null); then @@ -71,20 +75,31 @@ function compileFull() set -e rm "$stderr_path" - if [[ \ - ("$exit_code" -ne "$expected_exit_code" || \ - ( $expect_output -eq 0 && -n "$errors" ) || \ - ( $expect_output -ne 0 && $expected_exit_code -eq 0 && $expect_output -ne 2 && -z "$errors" )) + if [[ + $exit_code != "$expected_exit_code" || + $errors != "" && $expect_output == 'none' || + $errors == "" && $expect_output != 'none' && $expect_output != 'any' && $expected_exit_code == 0 ]] then - printError "Unexpected compilation result:" - printError "Expected failure: $expected_exit_code - Expected warning / error output: $expect_output" - printError "Was failure: $exit_code" + printError "TEST FAILURE" + printError "Actual exit code: $exit_code" + printError "Expected exit code: $expected_exit_code" + printError "==== Output ====" echo "$errors" + printError "== Output end ==" + printError "" + case "$expect_output" in + 'none') printError "No output was expected." ;; + 'warnings-or-errors') printError "Expected warnings or errors." ;; + esac + + printError "" printError "While calling:" - echo "\"$SOLC\" ${args[*]} ${files[*]}" + echo "\"$SOLC\" ${args[*]} ${files[*]}" printError "Inside directory:" - pwd + echo " $(pwd)" + printError "Input was:" + cat -- "${files[@]}" false fi } diff --git a/scripts/docs_version_pragma_check.sh b/scripts/docs_version_pragma_check.sh index d7ca3f2ad..d16645da8 100755 --- a/scripts/docs_version_pragma_check.sh +++ b/scripts/docs_version_pragma_check.sh @@ -156,11 +156,11 @@ SOLTMPDIR=$(mktemp -d) if ( ! grep -E "This will not compile after" "$f" >/dev/null && \ grep -E "This will not compile|import \"" "$f" >/dev/null ) then - opts=(-e) + opts=(--expect-errors) fi # ignore warnings in this case - opts+=(-o) + opts+=(--ignore-warnings) findMinimalVersion "$f" if [[ "$version" == "" ]] diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index 37a538328..582a7acde 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -369,7 +369,7 @@ printTask "Compiling various other contracts and libraries..." do echo " - $dir" cd "$dir" - compileFull -w ./*.sol ./*/*.sol + compileFull --expect-warnings ./*.sol ./*/*.sol cd .. done ) @@ -397,15 +397,15 @@ SOLTMPDIR=$(mktemp -d) # are used (in the style guide) if grep -E "This will not compile|import \"" "$f" >/dev/null then - opts=(-e) + opts=(--expect-errors) fi if grep "This will report a warning" "$f" >/dev/null then - opts+=(-w) + opts+=(--expect-warnings) fi if grep "This may report a warning" "$f" >/dev/null then - opts+=(-o) + opts+=(--ignore-warnings) fi # Disable the version pragma in code snippets that only work with the current development version.