From cc49cc6276191bc5fa3ea846a2c8e475711ac3c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 12:48:17 +0400 Subject: [PATCH 01/24] chore(deps-dev): bump knip from 5.34.2 to 5.36.2 (#1016) Bumps [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip) from 5.34.2 to 5.36.2. - [Release notes](https://github.com/webpro-nl/knip/releases) - [Changelog](https://github.com/webpro-nl/knip/blob/main/packages/knip/.release-it.json) - [Commits](https://github.com/webpro-nl/knip/commits/5.36.2/packages/knip) --- updated-dependencies: - dependency-name: knip dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/yarn.lock b/yarn.lock index aa0621530..537ceffce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3890,10 +3890,10 @@ jest@^29.3.1: import-local "^3.0.2" jest-cli "^29.7.0" -jiti@^2.3.3: - version "2.3.3" - resolved "https://registry.npmjs.org/jiti/-/jiti-2.3.3.tgz#39c66fc77476b92a694e65dfe04b294070e2e096" - integrity sha512-EX4oNDwcXSivPrw2qKH2LB5PoFxEvgtv2JgwW0bU858HoLQ+kutSvjLMUqBd0PeJYEinLWhoI9Ol0eYMqj/wNQ== +jiti@^2.4.0: + version "2.4.0" + resolved "https://registry.npmjs.org/jiti/-/jiti-2.4.0.tgz#393d595fb6031a11d11171b5e4fc0b989ba3e053" + integrity sha512-H5UpaUI+aHOqZXlYOaFP/8AzKsg+guWu+Pr3Y8i7+Y3zr1aXAvCvTAQ1RxSc6oVD8R8c7brgNtTVP91E7upH/g== js-tokens@^4.0.0: version "4.0.0" @@ -3996,19 +3996,19 @@ kleur@^3.0.3: integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== knip@^5.24.1: - version "5.34.2" - resolved "https://registry.npmjs.org/knip/-/knip-5.34.2.tgz#7e3cd9b0cb36dc5a41d53be74ef549708aeac042" - integrity sha512-quSohta9fKJUWTXIqHenA4NYW24rmoP3g/Naw5Lq/Xb/LjAQYSUK4aIyU1wizDLC4EnMicGA5g/H33vJ3ovz5Q== + version "5.36.2" + resolved "https://registry.npmjs.org/knip/-/knip-5.36.2.tgz#346ce5eb464bdf34329cdcf3a6d41d0a41dce647" + integrity sha512-MudNTKBSqThAFAV29GuRPSKSebByZeQCFeNgXVRVSd+sXcubehTgQHTGqqiwlXGCt4WBP7vuVekp0ZehfZtHuw== dependencies: "@nodelib/fs.walk" "1.2.8" "@snyk/github-codeowners" "1.1.0" easy-table "1.2.0" enhanced-resolve "^5.17.1" fast-glob "^3.3.2" - jiti "^2.3.3" + jiti "^2.4.0" js-yaml "^4.1.0" minimist "^1.2.8" - picocolors "^1.0.0" + picocolors "^1.1.0" picomatch "^4.0.1" pretty-ms "^9.0.0" smol-toml "^1.3.0" @@ -4646,10 +4646,10 @@ path-type@^5.0.0: resolved "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz#14b01ed7aea7ddf9c7c3f46181d4d04f9c785bb8" integrity sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg== -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picocolors@^1.0.0, picocolors@^1.1.0: + version "1.1.1" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" From 756f84e886b09849a05a495edcc4132bd65781a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 12:49:51 +0400 Subject: [PATCH 02/24] chore(deps-dev): bump cspell from 8.15.4 to 8.15.7 (#1015) Bumps [cspell](https://github.com/streetsidesoftware/cspell/tree/HEAD/packages/cspell) from 8.15.4 to 8.15.7. - [Release notes](https://github.com/streetsidesoftware/cspell/releases) - [Changelog](https://github.com/streetsidesoftware/cspell/blob/main/packages/cspell/CHANGELOG.md) - [Commits](https://github.com/streetsidesoftware/cspell/commits/v8.15.7/packages/cspell) --- updated-dependencies: - dependency-name: cspell dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 342 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 177 insertions(+), 165 deletions(-) diff --git a/yarn.lock b/yarn.lock index 537ceffce..11dcb2d93 100644 --- a/yarn.lock +++ b/yarn.lock @@ -308,16 +308,17 @@ resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@cspell/cspell-bundled-dicts@8.15.4": - version "8.15.4" - resolved "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-8.15.4.tgz#dcba969630b8ce132e649721027a2e67ff483174" - integrity sha512-t5b2JwGeUmzmjl319mCuaeKGxTvmzLLRmrpdHr+ZZGRO4nf7L48Lbe9A6uwNUvsZe0cXohiNXsrrsuzRVXswVA== +"@cspell/cspell-bundled-dicts@8.15.7": + version "8.15.7" + resolved "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-8.15.7.tgz#beeb2af34b9691febd489c915211bc2a919e0e90" + integrity sha512-lNbrlHhDnOWCJh/vNCliZJz4X1KMEZqWJZpTgmdPrEGS9ZyfEiCmZyoQzw+fauC5Xo7dwd2KdS9VMHftAJqMPQ== dependencies: "@cspell/dict-ada" "^4.0.5" + "@cspell/dict-al" "^1.0.3" "@cspell/dict-aws" "^4.0.7" "@cspell/dict-bash" "^4.1.8" "@cspell/dict-companies" "^3.1.7" - "@cspell/dict-cpp" "^5.1.22" + "@cspell/dict-cpp" "^6.0.0" "@cspell/dict-cryptocurrencies" "^5.0.3" "@cspell/dict-csharp" "^4.0.5" "@cspell/dict-css" "^4.0.16" @@ -329,7 +330,7 @@ "@cspell/dict-en-common-misspellings" "^2.0.7" "@cspell/dict-en-gb" "1.1.33" "@cspell/dict-en_us" "^4.3.26" - "@cspell/dict-filetypes" "^3.0.7" + "@cspell/dict-filetypes" "^3.0.8" "@cspell/dict-flutter" "^1.0.3" "@cspell/dict-fonts" "^4.0.3" "@cspell/dict-fsharp" "^1.0.4" @@ -339,7 +340,7 @@ "@cspell/dict-golang" "^6.0.16" "@cspell/dict-google" "^1.0.4" "@cspell/dict-haskell" "^4.0.4" - "@cspell/dict-html" "^4.0.9" + "@cspell/dict-html" "^4.0.10" "@cspell/dict-html-symbol-entities" "^4.0.3" "@cspell/dict-java" "^5.0.10" "@cspell/dict-julia" "^1.0.4" @@ -348,9 +349,10 @@ "@cspell/dict-lorem-ipsum" "^4.0.3" "@cspell/dict-lua" "^4.0.6" "@cspell/dict-makefile" "^1.0.3" + "@cspell/dict-markdown" "^2.0.7" "@cspell/dict-monkeyc" "^1.0.9" "@cspell/dict-node" "^5.0.4" - "@cspell/dict-npm" "^5.1.8" + "@cspell/dict-npm" "^5.1.9" "@cspell/dict-php" "^4.0.13" "@cspell/dict-powershell" "^5.0.13" "@cspell/dict-public-licenses" "^2.0.11" @@ -359,48 +361,53 @@ "@cspell/dict-ruby" "^5.0.7" "@cspell/dict-rust" "^4.0.9" "@cspell/dict-scala" "^5.0.6" - "@cspell/dict-software-terms" "^4.1.11" + "@cspell/dict-software-terms" "^4.1.12" "@cspell/dict-sql" "^2.1.8" "@cspell/dict-svelte" "^1.0.5" "@cspell/dict-swift" "^2.0.4" - "@cspell/dict-terraform" "^1.0.5" - "@cspell/dict-typescript" "^3.1.10" + "@cspell/dict-terraform" "^1.0.6" + "@cspell/dict-typescript" "^3.1.11" "@cspell/dict-vue" "^3.0.3" -"@cspell/cspell-json-reporter@8.15.4": - version "8.15.4" - resolved "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-8.15.4.tgz#f8902234bd000040d40ae3574860d221c991aff9" - integrity sha512-solraYhZG4l++NeVCOtpc8DMvwHc46TmJt8DQbgvKtk6wOjTEcFrwKfA6Ei9YKbvyebJlpWMenO3goOll0loYg== +"@cspell/cspell-json-reporter@8.15.7": + version "8.15.7" + resolved "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-8.15.7.tgz#3c87ba514ce9917c8ee3e6819748dab3b06b2a34" + integrity sha512-kOcJaThztX1A8jkALBiAyp8dWrHPMuDOXki8Q5ZG1dL25wKsAnPyqflXhXg6Up4VGVIkkgy1S3T0Q/i+G5f12w== dependencies: - "@cspell/cspell-types" "8.15.4" + "@cspell/cspell-types" "8.15.7" -"@cspell/cspell-pipe@8.15.4": - version "8.15.4" - resolved "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-8.15.4.tgz#c3c958d3b900ccdcc4f2a8610fe464882e3f1759" - integrity sha512-WfCmZVFC6mX6vYlf02hWwelcSBTbDQgc5YqY+1miuMk+OHSUAHSACjZId6/a4IAID94xScvFfj7jgrdejUVvIQ== +"@cspell/cspell-pipe@8.15.7": + version "8.15.7" + resolved "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-8.15.7.tgz#2971c1d27daff0cb127fa51d1bf24e41b0be829c" + integrity sha512-EyVSJPqJFrDA9Sj4Nzx13vytloMS0V3HaevhqMFLHJ53QNz/ZP7vuECbXApRAJwLonuToJBvY3b9xzB6eEhU/A== -"@cspell/cspell-resolver@8.15.4": - version "8.15.4" - resolved "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-8.15.4.tgz#83ade61a1e11b88be756a805138ec8b31191e0d0" - integrity sha512-Zr428o+uUTqywrdKyjluJVnDPVAJEqZ1chQLKIrHggUah1cgs5aQ7rZ+0Rv5euYMlC2idZnP7IL6TDaIib80oA== +"@cspell/cspell-resolver@8.15.7": + version "8.15.7" + resolved "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-8.15.7.tgz#f513b84862c84ef61e88fed548fcf120bb7ae112" + integrity sha512-9RPZ5VwjYPYLTLWkoPGHqV3Kuai5QwTWgZJW3Yk2GgJkxss/LDsXME+9CalPcPBQpnCIBEOtE2DjDQbFjAiMnA== dependencies: global-directory "^4.0.1" -"@cspell/cspell-service-bus@8.15.4": - version "8.15.4" - resolved "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-8.15.4.tgz#76a0c380e0102a4521ce96a64ade580331ca8404" - integrity sha512-pXYofnV/V9Y3LZdfFGbmhdxPX/ABjiD3wFjGHt5YhIU9hjVVuvjFlgY7pH2AvRjs4F8xKXv1ReWl44BJOL9gLA== +"@cspell/cspell-service-bus@8.15.7": + version "8.15.7" + resolved "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-8.15.7.tgz#fcc343d6c0d8e56736cc58c9a839950d219d8744" + integrity sha512-kL+1+K4VApdwZccGlg7Vjmh4CzzjoT+G556/gErdESQFPY0y9/4OPPVKLrFkbEDODtp9Py7aTRHdhl6w1xxCCw== -"@cspell/cspell-types@8.15.4": - version "8.15.4" - resolved "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-8.15.4.tgz#307db2055ff669d99487c1772edb393243bb5098" - integrity sha512-1hDtgYDQVW11zgtrr12EmGW45Deoi7IjZOhzPFLb+3WkhZ46ggWdbrRalWwBolQPDDo6+B2Q6WXz5hdND+Tpwg== +"@cspell/cspell-types@8.15.7": + version "8.15.7" + resolved "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-8.15.7.tgz#a9e629aae2d81531300af4ea226395ece7f62e7f" + integrity sha512-QMbGJsUXTdV8/V9Gsc/kBgdkBwm4hvcChhQf6KE9yeg3CZlbd95NkFJuSiqp1phJOWMTzHCZ0Ro7v7P/LGKdVA== "@cspell/dict-ada@^4.0.5": version "4.0.5" resolved "https://registry.npmjs.org/@cspell/dict-ada/-/dict-ada-4.0.5.tgz#c14aae2faaecbad2d99f0d701e4700a48c68ef60" integrity sha512-6/RtZ/a+lhFVmrx/B7bfP7rzC4yjEYe8o74EybXcvu4Oue6J4Ey2WSYj96iuodloj1LWrkNCQyX5h4Pmcj0Iag== +"@cspell/dict-al@^1.0.3": + version "1.0.3" + resolved "https://registry.npmjs.org/@cspell/dict-al/-/dict-al-1.0.3.tgz#09e288b5ab56b126dce895d3301faf7c0dd732d6" + integrity sha512-V1HClwlfU/qwSq2Kt+MkqRAsonNu3mxjSCDyGRecdLGIHmh7yeEeaxqRiO/VZ4KP+eVSiSIlbwrb5YNFfxYZbw== + "@cspell/dict-aws@^4.0.7": version "4.0.7" resolved "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-4.0.7.tgz#f96f3b70cd52a25b895eb08e297de5a5cc3fc5b6" @@ -416,10 +423,10 @@ resolved "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-3.1.7.tgz#c9abd6f5293f103062f54dde01f2bee939189f79" integrity sha512-ncVs/efuAkP1/tLDhWbXukBjgZ5xOUfe03neHMWsE8zvXXc5+Lw6TX5jaJXZLOoES/f4j4AhRE20jsPCF5pm+A== -"@cspell/dict-cpp@^5.1.22": - version "5.1.22" - resolved "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-5.1.22.tgz#ee14d2b193c0a25dc58c609979f1500cd2f6e870" - integrity sha512-g1/8P5/Q+xnIc8Js4UtBg3XOhcFrFlFbG3UWVtyEx49YTf0r9eyDtDt1qMMDBZT91pyCwLcAEbwS+4i5PIfNZw== +"@cspell/dict-cpp@^6.0.0": + version "6.0.1" + resolved "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-6.0.1.tgz#f6e7f04c5e8ad23f45a322feef34bc782a17ca88" + integrity sha512-AxMC1KVu/9JSme1eG1SPQQTSLQbGUpoICMdKjQEEaB4RyrEev2V6fcVnqH38lzs+zN5Dffh04B2bTW0pT4lr9g== "@cspell/dict-cryptocurrencies@^5.0.3": version "5.0.3" @@ -481,10 +488,10 @@ resolved "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-4.3.26.tgz#f0d2c9492715e85b60a78f62f03918525639aa48" integrity sha512-hDbHYJsi3UgU1J++B0WLiYhWQdsmve3CH53FIaMRAdhrWOHcuw7h1dYkQXHFEP5lOjaq53KUHp/oh5su6VkIZg== -"@cspell/dict-filetypes@^3.0.7": - version "3.0.7" - resolved "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-3.0.7.tgz#bd79078e35f01ee6d86bb9c5ebe811cb7ee1c0d9" - integrity sha512-/DN0Ujp9/EXvpTcgih9JmBaE8n+G0wtsspyNdvHT5luRfpfol1xm/CIQb6xloCXCiLkWX+EMPeLSiVIZq+24dA== +"@cspell/dict-filetypes@^3.0.8": + version "3.0.8" + resolved "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-3.0.8.tgz#016d523ca2c34dea972ea0ca931255868348d81a" + integrity sha512-D3N8sm/iptzfVwsib/jvpX+K/++rM8SRpLDFUaM4jxm8EyGmSIYRbKZvdIv5BkAWmMlTWoRqlLn7Yb1b11jKJg== "@cspell/dict-flutter@^1.0.3": version "1.0.3" @@ -536,10 +543,10 @@ resolved "https://registry.npmjs.org/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-4.0.3.tgz#bf2887020ca4774413d8b1f27c9b6824ba89e9ef" integrity sha512-aABXX7dMLNFdSE8aY844X4+hvfK7977sOWgZXo4MTGAmOzR8524fjbJPswIBK7GaD3+SgFZ2yP2o0CFvXDGF+A== -"@cspell/dict-html@^4.0.9": - version "4.0.9" - resolved "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.9.tgz#b400a777e347e9437105f088d48f44eb268a5a9f" - integrity sha512-BNp7w3m910K4qIVyOBOZxHuFNbVojUY6ES8Y8r7YjYgJkm2lCuQoVwwhPjurnomJ7BPmZTb+3LLJ58XIkgF7JQ== +"@cspell/dict-html@^4.0.10": + version "4.0.10" + resolved "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.10.tgz#7b536b2adca4b58ed92752c9d3c7ffc724dd5991" + integrity sha512-I9uRAcdtHbh0wEtYZlgF0TTcgH0xaw1B54G2CW+tx4vHUwlde/+JBOfIzird4+WcMv4smZOfw+qHf7puFUbI5g== "@cspell/dict-java@^5.0.10": version "5.0.10" @@ -576,6 +583,11 @@ resolved "https://registry.npmjs.org/@cspell/dict-makefile/-/dict-makefile-1.0.3.tgz#08d3349bf7cbd8f5dacf8641f3d35092ca0b8b38" integrity sha512-R3U0DSpvTs6qdqfyBATnePj9Q/pypkje0Nj26mQJ8TOBQutCRAJbr2ZFAeDjgRx5EAJU/+8txiyVF97fbVRViw== +"@cspell/dict-markdown@^2.0.7": + version "2.0.7" + resolved "https://registry.npmjs.org/@cspell/dict-markdown/-/dict-markdown-2.0.7.tgz#15d6f9eed6bd1b33921b4332426ff387961163f1" + integrity sha512-F9SGsSOokFn976DV4u/1eL4FtKQDSgJHSZ3+haPRU5ki6OEqojxKa8hhj4AUrtNFpmBaJx/WJ4YaEzWqG7hgqg== + "@cspell/dict-monkeyc@^1.0.9": version "1.0.9" resolved "https://registry.npmjs.org/@cspell/dict-monkeyc/-/dict-monkeyc-1.0.9.tgz#58b5f6f15fc7c11ce0eeffd0742fba4b39fc0b8b" @@ -586,10 +598,10 @@ resolved "https://registry.npmjs.org/@cspell/dict-node/-/dict-node-5.0.4.tgz#dfe1f159a1ffb1c4f389ec43b15f705123113658" integrity sha512-Hz5hiuOvZTd7Cp1IBqUZ7/ChwJeQpD5BJuwCaDn4mPNq4iMcQ1iWBYMThvNVqCEDgKv63X52nT8RAWacss98qg== -"@cspell/dict-npm@^5.1.8": - version "5.1.8" - resolved "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.1.8.tgz#d179e555d351aecd4c7e5a87756f3b1192701ee8" - integrity sha512-AJELYXeB4fQdIoNfmuaQxB1Hli3cX6XPsQCjfBxlu0QYXhrjB/IrCLLQAjWIywDqJiWyGUFTz4DqaANm8C/r9Q== +"@cspell/dict-npm@^5.1.9": + version "5.1.10" + resolved "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.1.10.tgz#274d5eb3a44d1017771fe7768aabeaf7e46764d5" + integrity sha512-YmOLvM3ERk/yrdk0s0HhMI7Ws4epLRycGQH5uWyvWg5F64C31mbV557+jfxjrn6Ewq3UdT4ILCS9EyCHVyirig== "@cspell/dict-php@^4.0.13": version "4.0.13" @@ -633,10 +645,10 @@ resolved "https://registry.npmjs.org/@cspell/dict-scala/-/dict-scala-5.0.6.tgz#5e925def2fe6dc27ee2ad1c452941c3d6790fb6d" integrity sha512-tl0YWAfjUVb4LyyE4JIMVE8DlLzb1ecHRmIWc4eT6nkyDqQgHKzdHsnusxFEFMVLIQomgSg0Zz6hJ5S1E4W4ww== -"@cspell/dict-software-terms@^4.1.11": - version "4.1.11" - resolved "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-4.1.11.tgz#54e1cfcda53f308135215f7163797d7ed8f69ee4" - integrity sha512-77CTHxWFTVw6tVoMN8WBMrlNW2F2FbgATwD/6vcOuiyrJUmh8klN5ZK3m+yyK3ZzsnaW2Bduoc0fw2Ckcm/riQ== +"@cspell/dict-software-terms@^4.1.12": + version "4.1.13" + resolved "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-4.1.13.tgz#7c9116e155d69eb7e732de2bc484d9496bbffd6d" + integrity sha512-S8TLDjY+piV8nmzn4/acwKjDbUtOqfJ9Cb3gZ9egjU/Fm8DBaQ7ziR1S2wntxlGLud9cly+LoWnEoWJYDZFveQ== "@cspell/dict-sql@^2.1.8": version "2.1.8" @@ -653,42 +665,42 @@ resolved "https://registry.npmjs.org/@cspell/dict-swift/-/dict-swift-2.0.4.tgz#bc19522418ed68cf914736b612c4e4febbf07e8d" integrity sha512-CsFF0IFAbRtYNg0yZcdaYbADF5F3DsM8C4wHnZefQy8YcHP/qjAF/GdGfBFBLx+XSthYuBlo2b2XQVdz3cJZBw== -"@cspell/dict-terraform@^1.0.5": - version "1.0.5" - resolved "https://registry.npmjs.org/@cspell/dict-terraform/-/dict-terraform-1.0.5.tgz#14c427ae47d2f78b4acd6fe9ed12bcae53aec441" - integrity sha512-qH3epPB2d6d5w1l4hR2OsnN8qDQ4P0z6oDB7+YiNH+BoECXv4Z38MIV1H8cxIzD2wkzkt2JTcFYaVW72MDZAlg== +"@cspell/dict-terraform@^1.0.6": + version "1.0.6" + resolved "https://registry.npmjs.org/@cspell/dict-terraform/-/dict-terraform-1.0.6.tgz#f67b7363d0cf08c820818980bbe8c927332ad0b8" + integrity sha512-Sqm5vGbXuI9hCFcr4w6xWf4Y25J9SdleE/IqfM6RySPnk8lISEmVdax4k6+Kinv9qaxyvnIbUUN4WFLWcBPQAg== -"@cspell/dict-typescript@^3.1.10": - version "3.1.10" - resolved "https://registry.npmjs.org/@cspell/dict-typescript/-/dict-typescript-3.1.10.tgz#aa297c3f910cde8e644eaf9f711598a85763aaf8" - integrity sha512-7Zek3w4Rh3ZYyhihJ34FdnUBwP3OmRldnEq3hZ+FgQ0PyYZjXv5ztEViRBBxXjiFx1nHozr6pLi74TxToD8xsg== +"@cspell/dict-typescript@^3.1.11": + version "3.1.11" + resolved "https://registry.npmjs.org/@cspell/dict-typescript/-/dict-typescript-3.1.11.tgz#40586f13b0337bd9cba958e0661b35888580b249" + integrity sha512-FwvK5sKbwrVpdw0e9+1lVTl8FPoHYvfHRuQRQz2Ql5XkC0gwPPkpoyD1zYImjIyZRoYXk3yp9j8ss4iz7A7zoQ== "@cspell/dict-vue@^3.0.3": version "3.0.3" resolved "https://registry.npmjs.org/@cspell/dict-vue/-/dict-vue-3.0.3.tgz#295c288f6fd363879898223202ec3be048663b98" integrity sha512-akmYbrgAGumqk1xXALtDJcEcOMYBYMnkjpmGzH13Ozhq1mkPF4VgllFQlm1xYde+BUKNnzMgPEzxrL2qZllgYA== -"@cspell/dynamic-import@8.15.4": - version "8.15.4" - resolved "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-8.15.4.tgz#6aee2c81a3a45d3ec6c3f225fbe24557d46e0f85" - integrity sha512-tr0F6EYN6qtniNvt1Uib+PgYQHeo4dQHXE2Optap+hYTOoQ2VoQ+SwBVjZ+Q2bmSAB0fmOyf0AvgsUtnWIpavw== +"@cspell/dynamic-import@8.15.7": + version "8.15.7" + resolved "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-8.15.7.tgz#285e1860e6b8c911f75d97163fa37b90d97b288b" + integrity sha512-qFlVisdP2lvFcS4Kre4Dl+f4Y7U9w/Y7IQAS+XXl5KlInImMaYhNUDEru8DoUPQHYsXKAPJsu/Y2JloHNE502Q== dependencies: import-meta-resolve "^4.1.0" -"@cspell/filetypes@8.15.4": - version "8.15.4" - resolved "https://registry.npmjs.org/@cspell/filetypes/-/filetypes-8.15.4.tgz#cdc64f21f5b1b824490aa003d53b9a07703c5818" - integrity sha512-sNl6jr3ym/4151EY76qlI/00HHsiLZBqW7Vb1tqCzsgSg3EpL30ddjr74So6Sg2PN26Yf09hvxGTJzXn1R4aYw== +"@cspell/filetypes@8.15.7": + version "8.15.7" + resolved "https://registry.npmjs.org/@cspell/filetypes/-/filetypes-8.15.7.tgz#b74866c84c4d08c63b5fae8c62142f0c0f82fb91" + integrity sha512-MeP6gh8Om9vHSxYoYey2BFCib4m+vEyMLQCSub1Gk+uXiJjj1l/S5MFWM9zHhjGBBNNdvuopuUKP6Gcgcw+3Cw== -"@cspell/strong-weak-map@8.15.4": - version "8.15.4" - resolved "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-8.15.4.tgz#0e5419862e0a8634e2db20a9e660b827321a6436" - integrity sha512-m5DeQksbhJFqcSYF8Q0Af/WXmXCMAJocCUShkzOXK+uZNXnvhBZN7VyQ9hL+GRzX8JTPEPdVcz2lFyVE5p+LzQ== +"@cspell/strong-weak-map@8.15.7": + version "8.15.7" + resolved "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-8.15.7.tgz#656b3a311a0ace90b4f77505db7d2ca4a2f16700" + integrity sha512-SGlkhRnHXoBzLY2SxVppMsREhyaDHpyXQrPDUfsCnyG5DC8UVmXnTVQp9c2kqhAZw6g6g6V7uoqTLqJQmrWOFQ== -"@cspell/url@8.15.4": - version "8.15.4" - resolved "https://registry.npmjs.org/@cspell/url/-/url-8.15.4.tgz#759a9832e9fd6f9fd022c4eb3031c1b28c272faa" - integrity sha512-K2oZu/oLQPs5suRpLS8uu04O3YMUioSlEU1D66fRoOxzI5NzLt7i7yMg3HQHjChGa09N5bzqmrVdhmQrRZXwGg== +"@cspell/url@8.15.7": + version "8.15.7" + resolved "https://registry.npmjs.org/@cspell/url/-/url-8.15.7.tgz#152aa1caf47e6995427c6f1052f4b019a1f4b6c9" + integrity sha512-IzBsrl54TyO5Ezbyr25ZOUZA3Sg2UbSWDZZar9jSRAsoikcsoy1ivgSumcYJYOH8HAtanfr8YGN0+8UF/kbYqg== "@cspotcode/source-map-support@^0.8.0": version "0.8.1" @@ -2250,80 +2262,80 @@ cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -cspell-config-lib@8.15.4: - version "8.15.4" - resolved "https://registry.npmjs.org/cspell-config-lib/-/cspell-config-lib-8.15.4.tgz#c9530304e072cbc8d9d4cd76fdb3abcf45dab8af" - integrity sha512-vUgikQTRkRMTdkZqSs7F2cTdPpX61cTjr/9L/VCkXkbW38ObCr4650ioiF1Wq3zDF3Gy2bc4ECTpD2PZUXX5SA== +cspell-config-lib@8.15.7: + version "8.15.7" + resolved "https://registry.npmjs.org/cspell-config-lib/-/cspell-config-lib-8.15.7.tgz#1e7f7820327f58585c1dd77a57eb08447b066094" + integrity sha512-orxPKLMLQjk+Px1wlZdMElsHlKFGiwlXhQcG/36hODFKsle9DnGqVefOjH6aWFO5DyDF+Z678leiO2F30wtXEQ== dependencies: - "@cspell/cspell-types" "8.15.4" + "@cspell/cspell-types" "8.15.7" comment-json "^4.2.5" yaml "^2.6.0" -cspell-dictionary@8.15.4: - version "8.15.4" - resolved "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-8.15.4.tgz#be701af741a0be1a8680674bcbd3dadd7bc5c1fe" - integrity sha512-8+p/l9Saac7qyCbqtELneDoT7CwHu9gYmnI8uXMu34/lPGjhVhy10ZeI0+t1djaO2YyASK400YFKq5uP/5KulA== +cspell-dictionary@8.15.7: + version "8.15.7" + resolved "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-8.15.7.tgz#9aaf8c132cbbcb25a8e7170582caf0b7d470e1e3" + integrity sha512-jmOk9kJ/bsVFg0/ObnNMUHA3wPgHb4eGFx6yF+Lx28eYx9j2XkLZuXXicbNyOWqJ9AzP0CavPmHwAS6bJrxD3Q== dependencies: - "@cspell/cspell-pipe" "8.15.4" - "@cspell/cspell-types" "8.15.4" - cspell-trie-lib "8.15.4" + "@cspell/cspell-pipe" "8.15.7" + "@cspell/cspell-types" "8.15.7" + cspell-trie-lib "8.15.7" fast-equals "^5.0.1" -cspell-gitignore@8.15.4: - version "8.15.4" - resolved "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-8.15.4.tgz#79bcfcc343466cc483c1805baf0c53f6bdef1753" - integrity sha512-9n5PpQ8gEf8YcvEtoZGZ2Ma6wnqSFkD2GrmyjISy39DfIX/jNLN7GX2wJm6OD2P4FjXer95ypmIb/JWTlfmbTw== +cspell-gitignore@8.15.7: + version "8.15.7" + resolved "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-8.15.7.tgz#cbff91ec234f502db9e5c88b094b0267e8078840" + integrity sha512-LxX/PS3z6UqXHUqw3wIB1OJwZrexxKn/EUertYcLce/K2M2wLsUA+uneU5EvUqzkM6vwMHvdv/hl/tROFQJIbw== dependencies: - "@cspell/url" "8.15.4" - cspell-glob "8.15.4" - cspell-io "8.15.4" + "@cspell/url" "8.15.7" + cspell-glob "8.15.7" + cspell-io "8.15.7" find-up-simple "^1.0.0" -cspell-glob@8.15.4: - version "8.15.4" - resolved "https://registry.npmjs.org/cspell-glob/-/cspell-glob-8.15.4.tgz#24934982f3ecf664a32618f0fdf3965de63f05da" - integrity sha512-TTfRRHRAN+PN9drIz4MAEgKKYnPThBOlPMdFddyuisvU33Do1sPAnqkkOjTEFdi3jAA5KwnSva68SVH6IzzMBQ== +cspell-glob@8.15.7: + version "8.15.7" + resolved "https://registry.npmjs.org/cspell-glob/-/cspell-glob-8.15.7.tgz#a0ff17bfc93108b5ef45cc0515730aa7fe363e14" + integrity sha512-BI0mF0IWqVxEGpRkH2kBgT9Ey7lAMlEhvY/zKCy3JQY5PSn/qI3RhlsXrsTDt2RJxhicuzJIe3KiNdUKXQM0Ig== dependencies: - "@cspell/url" "8.15.4" + "@cspell/url" "8.15.7" micromatch "^4.0.8" -cspell-grammar@8.15.4: - version "8.15.4" - resolved "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-8.15.4.tgz#28619d564f4c128618fcecb1d71e31e30b69d213" - integrity sha512-MKiKyYi05mRtXOxPoTv3Ksi0GwYLiK84Uq0C+5PaMrnIjXeed0bsddSFXCT+7ywFJc7PdjhTtz0M/9WWK3UgbA== - dependencies: - "@cspell/cspell-pipe" "8.15.4" - "@cspell/cspell-types" "8.15.4" - -cspell-io@8.15.4: - version "8.15.4" - resolved "https://registry.npmjs.org/cspell-io/-/cspell-io-8.15.4.tgz#5d55471cbcff9f2d061a6f3504cd525152da754e" - integrity sha512-rXIEREPTFV9dwwg4EKfvzqlCNOvT6910AYED5YrSt8Y68usRJ9lbqdx0BrDndVCd33bp1o+9JBfHuRiFIQC81g== - dependencies: - "@cspell/cspell-service-bus" "8.15.4" - "@cspell/url" "8.15.4" - -cspell-lib@8.15.4: - version "8.15.4" - resolved "https://registry.npmjs.org/cspell-lib/-/cspell-lib-8.15.4.tgz#ebab39a5e9f74d2a50c588cd8397f0a2f5f7c4d0" - integrity sha512-iLp/625fvCyFFxSyZYLMgqHIKcrhN4hT7Hw5+ySa38Bp/OfA81ANqWHpsDQ0bGsALTRn/DHBpQYj4xCW/aN9tw== - dependencies: - "@cspell/cspell-bundled-dicts" "8.15.4" - "@cspell/cspell-pipe" "8.15.4" - "@cspell/cspell-resolver" "8.15.4" - "@cspell/cspell-types" "8.15.4" - "@cspell/dynamic-import" "8.15.4" - "@cspell/filetypes" "8.15.4" - "@cspell/strong-weak-map" "8.15.4" - "@cspell/url" "8.15.4" +cspell-grammar@8.15.7: + version "8.15.7" + resolved "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-8.15.7.tgz#6904ca220d2ad15c0e656508a1599fed28a01aef" + integrity sha512-g7ocpFG9Gam4+b2bHrqhmXVaFNV4BjFjVnaEKS3RoqcMjJuQUa9wD5HWO6AvBJeJf/auvQS7CgmumQqSo9xxCw== + dependencies: + "@cspell/cspell-pipe" "8.15.7" + "@cspell/cspell-types" "8.15.7" + +cspell-io@8.15.7: + version "8.15.7" + resolved "https://registry.npmjs.org/cspell-io/-/cspell-io-8.15.7.tgz#709aeae1061087120d6c2030971db2b431816a0f" + integrity sha512-GEnMPu+xyyHTal2QdCbuRrPUEpjCYo0mF/Tz/YkcZNJdn0sj6MylH2EA0A+d6WzheRpw9ijd1dRvq3h5jJgmuQ== + dependencies: + "@cspell/cspell-service-bus" "8.15.7" + "@cspell/url" "8.15.7" + +cspell-lib@8.15.7: + version "8.15.7" + resolved "https://registry.npmjs.org/cspell-lib/-/cspell-lib-8.15.7.tgz#afe0e0815b912ae9124912c63793ff3dc9138f03" + integrity sha512-RxxPEymTENc76f8ny1LN+aPyR4Efwyah8m5c20xOwxD/lAhBrNs2PPE1taEMPkI7WTXWjKm4D0DJpZatD+W6pg== + dependencies: + "@cspell/cspell-bundled-dicts" "8.15.7" + "@cspell/cspell-pipe" "8.15.7" + "@cspell/cspell-resolver" "8.15.7" + "@cspell/cspell-types" "8.15.7" + "@cspell/dynamic-import" "8.15.7" + "@cspell/filetypes" "8.15.7" + "@cspell/strong-weak-map" "8.15.7" + "@cspell/url" "8.15.7" clear-module "^4.1.2" comment-json "^4.2.5" - cspell-config-lib "8.15.4" - cspell-dictionary "8.15.4" - cspell-glob "8.15.4" - cspell-grammar "8.15.4" - cspell-io "8.15.4" - cspell-trie-lib "8.15.4" + cspell-config-lib "8.15.7" + cspell-dictionary "8.15.7" + cspell-glob "8.15.7" + cspell-grammar "8.15.7" + cspell-io "8.15.7" + cspell-trie-lib "8.15.7" env-paths "^3.0.0" fast-equals "^5.0.1" gensequence "^7.0.0" @@ -2333,38 +2345,38 @@ cspell-lib@8.15.4: vscode-uri "^3.0.8" xdg-basedir "^5.1.0" -cspell-trie-lib@8.15.4: - version "8.15.4" - resolved "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-8.15.4.tgz#48027e9bddc311f4b578b47286fc5d530c6da14a" - integrity sha512-sg9klsNHyrfos0Boiio+qy5d6fI9cCNjBqFYrNxvpKpwZ4gEzDzjgEKdZY1C76RD2KoBQ8I1NF5YcGc0+hhhCw== +cspell-trie-lib@8.15.7: + version "8.15.7" + resolved "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-8.15.7.tgz#241452831da22503379576f674aa6bfa9002ef85" + integrity sha512-b8WWWOx5wfhT72K43fk3dMoE4H2c1UpbCEVB2JXJ5scub7mjqoT/CRiZlw1IDfQT6BmUtJuD7sZ8NRFanF9hQA== dependencies: - "@cspell/cspell-pipe" "8.15.4" - "@cspell/cspell-types" "8.15.4" + "@cspell/cspell-pipe" "8.15.7" + "@cspell/cspell-types" "8.15.7" gensequence "^7.0.0" cspell@^8.8.3: - version "8.15.4" - resolved "https://registry.npmjs.org/cspell/-/cspell-8.15.4.tgz#8292a6ece4ef05f0602c4ba6fb9ef8e962cb0433" - integrity sha512-hUOxcwmNWuHzVeGHyN5v/T9MkyCE5gi0mvatxsM794B2wOuR1ZORgjZH62P2HY1uBkXe/x5C6ITWrSyh0WgAcg== - dependencies: - "@cspell/cspell-json-reporter" "8.15.4" - "@cspell/cspell-pipe" "8.15.4" - "@cspell/cspell-types" "8.15.4" - "@cspell/dynamic-import" "8.15.4" - "@cspell/url" "8.15.4" + version "8.15.7" + resolved "https://registry.npmjs.org/cspell/-/cspell-8.15.7.tgz#367a04ac579e6b67fc5eb451bf466f9f134345dc" + integrity sha512-68Bs/brr31M0W6tljNCgHcz09xdfDnRobyyRQJ8z0ZrovfTHHj9gSQldJJt5Fq3AMlCeYbECnKPsY9DkzIP1sQ== + dependencies: + "@cspell/cspell-json-reporter" "8.15.7" + "@cspell/cspell-pipe" "8.15.7" + "@cspell/cspell-types" "8.15.7" + "@cspell/dynamic-import" "8.15.7" + "@cspell/url" "8.15.7" chalk "^5.3.0" chalk-template "^1.1.0" commander "^12.1.0" - cspell-dictionary "8.15.4" - cspell-gitignore "8.15.4" - cspell-glob "8.15.4" - cspell-io "8.15.4" - cspell-lib "8.15.4" + cspell-dictionary "8.15.7" + cspell-gitignore "8.15.7" + cspell-glob "8.15.7" + cspell-io "8.15.7" + cspell-lib "8.15.7" fast-json-stable-stringify "^2.1.0" file-entry-cache "^9.1.0" get-stdin "^9.0.0" semver "^7.6.3" - tinyglobby "^0.2.9" + tinyglobby "^0.2.10" data-uri-to-buffer@^6.0.2: version "6.0.2" @@ -2795,10 +2807,10 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" -fdir@^6.4.0: - version "6.4.0" - resolved "https://registry.npmjs.org/fdir/-/fdir-6.4.0.tgz#8e80ab4b18a2ac24beebf9d20d71e1bc2627dbae" - integrity sha512-3oB133prH1o4j/L5lLW7uOCF1PlD+/It2L0eL/iAqWMB91RBbqTewABqxhj0ibBd90EEmWZq7ntIWzVaWcXTGQ== +fdir@^6.4.2: + version "6.4.2" + resolved "https://registry.npmjs.org/fdir/-/fdir-6.4.2.tgz#ddaa7ce1831b161bc3657bb99cb36e1622702689" + integrity sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ== file-entry-cache@^6.0.1: version "6.0.1" @@ -5281,12 +5293,12 @@ text-table@^0.2.0: resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== -tinyglobby@^0.2.9: - version "0.2.9" - resolved "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.9.tgz#6baddd1b0fe416403efb0dd40442c7d7c03c1c66" - integrity sha512-8or1+BGEdk1Zkkw2ii16qSS7uVrQJPre5A9o/XkWPATkk23FZh/15BKFxPnlTy6vkljZxLqYCzzBMj30ZrSvjw== +tinyglobby@^0.2.10: + version "0.2.10" + resolved "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.10.tgz#e712cf2dc9b95a1f5c5bbd159720e15833977a0f" + integrity sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew== dependencies: - fdir "^6.4.0" + fdir "^6.4.2" picomatch "^4.0.2" tmp@^0.0.33: From de4461665a315b5fcfe1264644bf208b21333420 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 12:50:15 +0400 Subject: [PATCH 03/24] chore(deps-dev): bump @types/node from 22.8.2 to 22.8.7 (#1014) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.8.2 to 22.8.7. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 11dcb2d93..227432a95 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1454,9 +1454,9 @@ integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== "@types/node@*", "@types/node@>=13.7.0", "@types/node@^22.5.0": - version "22.8.2" - resolved "https://registry.npmjs.org/@types/node/-/node-22.8.2.tgz#8e82bb8201c0caf751dcdc61b0a262d2002d438b" - integrity sha512-NzaRNFV+FZkvK/KLCsNdTvID0SThyrs5SHB6tsD/lajr22FGC73N2QeDPM2wHtVde8mgcXuSsHQkH5cX1pbPLw== + version "22.8.7" + resolved "https://registry.npmjs.org/@types/node/-/node-22.8.7.tgz#04ab7a073d95b4a6ee899f235d43f3c320a976f4" + integrity sha512-LidcG+2UeYIWcMuMUpBKOnryBWG/rnmOHQR5apjn8myTQcx3rinFRn7DcIFhMnS0PPFSC6OafdIKEad0lj6U0Q== dependencies: undici-types "~6.19.8" From 14b7c89a503a7fea920a7a7e89c86530301fcdd4 Mon Sep 17 00:00:00 2001 From: Daniil Sedov Date: Tue, 5 Nov 2024 12:04:34 +0300 Subject: [PATCH 04/24] fix: `as coins` map value serialization type now generates correct code (#987) * fix: use `BigVarUint` TS function for coins map value serialization type * fix: `coins` value type func codegen --- CHANGELOG.md | 1 + cspell.json | 1 + src/abi/map.ts | 16 + src/bindings/typescript/serializers.ts | 10 +- .../writeSerialization.spec.ts.snap | 1254 +++++++++++++++-- src/generator/writers/writeStdlib.ts | 77 +- src/test/e2e-emulated/contracts/maps.tact | 141 ++ src/test/e2e-emulated/map.spec.ts | 18 + 8 files changed, 1404 insertions(+), 114 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a2183371..86ea8216a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Prevent inline code snippets from changing their background color: PR [#935](https://github.com/tact-lang/tact/pull/935) - Docs: correctly handle next and previous page links at the bottom of the pages when there's a separator item in the sidebar: PR [#949](https://github.com/tact-lang/tact/pull/949) - Docs: compilation of examples in `data-structures.mdx` and across Cookbook: PR [#917](https://github.com/tact-lang/tact/pull/917) +- `as coins` map value serialization type is now handled correctly: PR [#987](https://github.com/tact-lang/tact/pull/987) ### Release contributors diff --git a/cspell.json b/cspell.json index d43b9ea1f..90d2f5036 100644 --- a/cspell.json +++ b/cspell.json @@ -114,6 +114,7 @@ "uintptr", "uninit", "unixfs", + "varuint", "workchain", "xffff", "привет" diff --git a/src/abi/map.ts b/src/abi/map.ts index bc44095cc..23370bcb1 100644 --- a/src/abi/map.ts +++ b/src/abi/map.ts @@ -160,6 +160,10 @@ export const MapFunctions: Map = new Map([ } else if (self.valueAs?.startsWith("uint")) { vBits = parseInt(self.valueAs.slice(4), 10); vKind = "uint"; + } else if (self.valueAs?.startsWith("coins")) { + vKind = "coins"; + ctx.used(`__tact_dict_set_${kind}_${vKind}`); + return `${resolved[0]}~__tact_dict_set_${kind}_${vKind}(${bits}, ${resolved[1]}, ${resolved[2]})`; } ctx.used(`__tact_dict_set_${kind}_${vKind}`); return `${resolved[0]}~__tact_dict_set_${kind}_${vKind}(${bits}, ${resolved[1]}, ${resolved[2]}, ${vBits})`; @@ -226,6 +230,10 @@ export const MapFunctions: Map = new Map([ } else if (self.valueAs?.startsWith("uint")) { vBits = parseInt(self.valueAs.slice(4), 10); vKind = "uint"; + } else if (self.valueAs?.startsWith("coins")) { + vKind = "coins"; + ctx.used(`__tact_dict_get_${kind}_${vKind}`); + return `__tact_dict_get_${kind}_${vKind}(${resolved[0]}, ${bits}, ${resolved[1]})`; } ctx.used(`__tact_dict_get_${kind}_${vKind}`); return `__tact_dict_get_${kind}_${vKind}(${resolved[0]}, ${bits}, ${resolved[1]}, ${vBits})`; @@ -571,6 +579,10 @@ export const MapFunctions: Map = new Map([ } else if (self.valueAs?.startsWith("uint")) { vBits = parseInt(self.valueAs.slice(4), 10); vKind = "uint"; + } else if (self.valueAs?.startsWith("coins")) { + vKind = "coins"; + ctx.used(`__tact_dict_replace_${kind}_${vKind}`); + return `${resolved[0]}~__tact_dict_replace_${kind}_${vKind}(${bits}, ${resolved[1]}, ${resolved[2]})`; } ctx.used(`__tact_dict_replace_${kind}_${vKind}`); return `${resolved[0]}~__tact_dict_replace_${kind}_${vKind}(${bits}, ${resolved[1]}, ${resolved[2]}, ${vBits})`; @@ -649,6 +661,10 @@ export const MapFunctions: Map = new Map([ } else if (self.valueAs?.startsWith("uint")) { vBits = parseInt(self.valueAs.slice(4), 10); vKind = "uint"; + } else if (self.valueAs?.startsWith("coins")) { + vKind = "coins"; + ctx.used(`__tact_dict_replaceget_${kind}_${vKind}`); + return `${resolved[0]}~__tact_dict_replaceget_${kind}_${vKind}(${bits}, ${resolved[1]}, ${resolved[2]})`; } ctx.used(`__tact_dict_replaceget_${kind}_${vKind}`); return `${resolved[0]}~__tact_dict_replaceget_${kind}_${vKind}(${bits}, ${resolved[1]}, ${resolved[2]}, ${vBits})`; diff --git a/src/bindings/typescript/serializers.ts b/src/bindings/typescript/serializers.ts index ef9c46311..2f23a8583 100644 --- a/src/bindings/typescript/serializers.ts +++ b/src/bindings/typescript/serializers.ts @@ -620,6 +620,7 @@ type MapSerializerDescrKey = | { kind: "address" }; type MapSerializerDescrValue = | { kind: "int" | "uint"; bits: number } + | { kind: "varuint"; length: number } | { kind: "boolean" } | { kind: "address" } | { kind: "cell" } @@ -665,6 +666,9 @@ function getValueParser(src: MapSerializerDescrValue) { return `Dictionary.Values.BigUint(${src.bits})`; } } + case "varuint": { + return `Dictionary.Values.BigVarUint(${src.length})`; + } case "address": { return "Dictionary.Values.Address()"; } @@ -739,7 +743,7 @@ const map: Serializer = { ) { value = { kind: "uint", bits: 256 }; } else if (src.valueFormat === "coins") { - value = { kind: "uint", bits: 124 }; + value = { kind: "varuint", length: 4 }; } } if (src.value === "address") { @@ -809,6 +813,10 @@ const map: Serializer = { } } break; + case "varuint": { + valueT = `bigint`; + break; + } case "boolean": { valueT = `boolean`; diff --git a/src/generator/writers/__snapshots__/writeSerialization.spec.ts.snap b/src/generator/writers/__snapshots__/writeSerialization.spec.ts.snap index fbf26704e..30db3674f 100644 --- a/src/generator/writers/__snapshots__/writeSerialization.spec.ts.snap +++ b/src/generator/writers/__snapshots__/writeSerialization.spec.ts.snap @@ -2243,6 +2243,132 @@ if (ok) { "name": "__tact_dict_replaceget_slice_cell", "signature": "(cell, (cell)) __tact_dict_replaceget_slice_cell(cell d, int kl, slice k, cell v)", }, + { + "code": { + "code": "var (r, ok) = __tact_dict_get(d, kl, k); +if (ok) { + return r~load_coins(); +} else { + return null(); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_get", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_get_slice_coins", + "signature": "int __tact_dict_get_slice_coins(cell d, int kl, slice k)", + }, + { + "code": { + "code": "var (key, value, flag) = __tact_dict_min(d, kl); +if (flag) { + return (key, value~load_coins(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_min", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_min_slice_coins", + "signature": "(slice, int, int) __tact_dict_min_slice_coins(cell d, int kl)", + }, + { + "code": { + "code": "var (key, value, flag) = __tact_dict_next(d, kl, pivot); +if (flag) { + return (key, value~load_coins(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_next", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_next_slice_coins", + "signature": "(slice, int, int) __tact_dict_next_slice_coins(cell d, int kl, slice pivot)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = __tact_dict_delete(d, kl, k); + return (r, ()); +} else { + return (dict_set_builder(d, kl, k, begin_cell().store_coins(v)), ()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_delete", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_set_slice_coins", + "signature": "(cell, ()) __tact_dict_set_slice_coins(cell d, int kl, slice k, int v)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = __tact_dict_delete(d, kl, k); + return (r, (ok)); +} else { + return dict_replace_builder?(d, kl, k, begin_cell().store_coins(v)); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_delete", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replace_slice_coins", + "signature": "(cell, (int)) __tact_dict_replace_slice_coins(cell d, int kl, slice k, int v)", + }, + { + "code": { + "code": "var (old, ok) = null?(v) ? d~__tact_dict_delete_get(kl, k) : d~dict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse()); +if (ok) { + return (d, old~load_coins()); +} else { + return (d, null()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_delete_get", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replaceget_slice_coins", + "signature": "(cell, (int)) __tact_dict_replaceget_slice_coins(cell d, int kl, slice k, int v)", + }, { "code": { "code": "var (r, ok) = udict_get?(d, kl, k); @@ -2699,6 +2825,120 @@ if (ok) { "name": "__tact_dict_replaceget_uint_cell", "signature": "(cell, (cell)) __tact_dict_replaceget_uint_cell(cell d, int kl, int k, cell v)", }, + { + "code": { + "code": "var (r, ok) = udict_get?(d, kl, k); +if (ok) { + return r~load_coins(); +} else { + return null(); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_get_uint_coins", + "signature": "int __tact_dict_get_uint_coins(cell d, int kl, int k)", + }, + { + "code": { + "code": "var (key, value, flag) = udict_get_min?(d, kl); +if (flag) { + return (key, value~load_coins(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_min_uint_coins", + "signature": "(int, int, int) __tact_dict_min_uint_coins(cell d, int kl)", + }, + { + "code": { + "code": "var (key, value, flag) = udict_get_next?(d, kl, pivot); +if (flag) { + return (key, value~load_coins(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_next_uint_coins", + "signature": "(int, int, int) __tact_dict_next_uint_coins(cell d, int kl, int pivot)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = udict_delete?(d, kl, k); + return (r, ()); +} else { + return (udict_set_builder(d, kl, k, begin_cell().store_coins(v)), ()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_set_uint_coins", + "signature": "(cell, ()) __tact_dict_set_uint_coins(cell d, int kl, int k, int v)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = udict_delete?(d, kl, k); + return (r, (ok)); +} else { + return udict_replace_builder?(d, kl, k, begin_cell().store_coins(v)); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replace_uint_coins", + "signature": "(cell, (int)) __tact_dict_replace_uint_coins(cell d, int kl, int k, int v)", + }, + { + "code": { + "code": "var (old, ok) = null?(v) ? d~udict_delete_get?(kl, k) : d~udict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse()); +if (ok) { + return (d, old~load_coins()); +} else { + return (d, null()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replaceget_uint_coins", + "signature": "(cell, (int)) __tact_dict_replaceget_uint_coins(cell d, int kl, int k, int v)", + }, { "code": { "code": "var (r, ok) = idict_get?(d, kl, k); @@ -3019,14 +3259,128 @@ if (flag) { "flags": Set { "inline", }, - "name": "__tact_dict_replace_int_uint", - "signature": "(cell, (int)) __tact_dict_replace_int_uint(cell d, int kl, int k, int v, int vl)", + "name": "__tact_dict_replace_int_uint", + "signature": "(cell, (int)) __tact_dict_replace_int_uint(cell d, int kl, int k, int v, int vl)", + }, + { + "code": { + "code": "var (old, ok) = null?(v) ? d~idict_delete_get?(kl, k) : d~idict_replaceget?(kl, k, begin_cell().store_uint(v, vl).end_cell().begin_parse()); +if (ok) { + return (d, old~load_uint(vl)); +} else { + return (d, null()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replaceget_int_uint", + "signature": "(cell, (int)) __tact_dict_replaceget_int_uint(cell d, int kl, int k, int v, int vl)", + }, + { + "code": { + "code": "var (r, ok) = idict_get_ref?(d, kl, k); +if (ok) { + return r; +} else { + return null(); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_get_int_cell", + "signature": "cell __tact_dict_get_int_cell(cell d, int kl, int k)", + }, + { + "code": { + "code": "var (key, value, flag) = idict_get_min_ref?(d, kl); +if (flag) { + return (key, value, flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_min_int_cell", + "signature": "(int, cell, int) __tact_dict_min_int_cell(cell d, int kl)", + }, + { + "code": { + "code": "var (key, value, flag) = idict_get_next?(d, kl, pivot); +if (flag) { + return (key, value~load_ref(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_next_int_cell", + "signature": "(int, cell, int) __tact_dict_next_int_cell(cell d, int kl, int pivot)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = idict_delete?(d, kl, k); + return (r, ()); +} else { + return (idict_set_ref(d, kl, k, v), ()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_set_int_cell", + "signature": "(cell, ()) __tact_dict_set_int_cell(cell d, int kl, int k, cell v)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = idict_delete?(d, kl, k); + return (r, (ok)); +} else { + return idict_replace_ref?(d, kl, k, v); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replace_int_cell", + "signature": "(cell, (int)) __tact_dict_replace_int_cell(cell d, int kl, int k, cell v)", }, { "code": { - "code": "var (old, ok) = null?(v) ? d~idict_delete_get?(kl, k) : d~idict_replaceget?(kl, k, begin_cell().store_uint(v, vl).end_cell().begin_parse()); + "code": "var (old, ok) = null?(v) ? d~idict_delete_get_ref?(kl, k) : d~idict_replaceget_ref?(kl, k, v); if (ok) { - return (d, old~load_uint(vl)); + return (d, old); } else { return (d, null()); }", @@ -3038,14 +3392,14 @@ if (ok) { "flags": Set { "inline", }, - "name": "__tact_dict_replaceget_int_uint", - "signature": "(cell, (int)) __tact_dict_replaceget_int_uint(cell d, int kl, int k, int v, int vl)", + "name": "__tact_dict_replaceget_int_cell", + "signature": "(cell, (cell)) __tact_dict_replaceget_int_cell(cell d, int kl, int k, cell v)", }, { "code": { - "code": "var (r, ok) = idict_get_ref?(d, kl, k); + "code": "var (r, ok) = idict_get?(d, kl, k); if (ok) { - return r; + return r~load_coins(); } else { return null(); }", @@ -3057,14 +3411,14 @@ if (ok) { "flags": Set { "inline", }, - "name": "__tact_dict_get_int_cell", - "signature": "cell __tact_dict_get_int_cell(cell d, int kl, int k)", + "name": "__tact_dict_get_int_coins", + "signature": "int __tact_dict_get_int_coins(cell d, int kl, int k)", }, { "code": { - "code": "var (key, value, flag) = idict_get_min_ref?(d, kl); + "code": "var (key, value, flag) = idict_get_min?(d, kl); if (flag) { - return (key, value, flag); + return (key, value~load_coins(), flag); } else { return (null(), null(), flag); }", @@ -3076,14 +3430,14 @@ if (flag) { "flags": Set { "inline", }, - "name": "__tact_dict_min_int_cell", - "signature": "(int, cell, int) __tact_dict_min_int_cell(cell d, int kl)", + "name": "__tact_dict_min_int_coins", + "signature": "(int, int, int) __tact_dict_min_int_coins(cell d, int kl)", }, { "code": { "code": "var (key, value, flag) = idict_get_next?(d, kl, pivot); if (flag) { - return (key, value~load_ref(), flag); + return (key, value~load_coins(), flag); } else { return (null(), null(), flag); }", @@ -3095,8 +3449,8 @@ if (flag) { "flags": Set { "inline", }, - "name": "__tact_dict_next_int_cell", - "signature": "(int, cell, int) __tact_dict_next_int_cell(cell d, int kl, int pivot)", + "name": "__tact_dict_next_int_coins", + "signature": "(int, int, int) __tact_dict_next_int_coins(cell d, int kl, int pivot)", }, { "code": { @@ -3104,7 +3458,7 @@ if (flag) { var (r, ok) = idict_delete?(d, kl, k); return (r, ()); } else { - return (idict_set_ref(d, kl, k, v), ()); + return (idict_set_builder(d, kl, k, begin_cell().store_coins(v)), ()); }", "kind": "generic", }, @@ -3114,8 +3468,8 @@ if (flag) { "flags": Set { "inline", }, - "name": "__tact_dict_set_int_cell", - "signature": "(cell, ()) __tact_dict_set_int_cell(cell d, int kl, int k, cell v)", + "name": "__tact_dict_set_int_coins", + "signature": "(cell, ()) __tact_dict_set_int_coins(cell d, int kl, int k, int v)", }, { "code": { @@ -3123,7 +3477,7 @@ if (flag) { var (r, ok) = idict_delete?(d, kl, k); return (r, (ok)); } else { - return idict_replace_ref?(d, kl, k, v); + return idict_replace_builder?(d, kl, k, begin_cell().store_coins(v)); }", "kind": "generic", }, @@ -3133,14 +3487,14 @@ if (flag) { "flags": Set { "inline", }, - "name": "__tact_dict_replace_int_cell", - "signature": "(cell, (int)) __tact_dict_replace_int_cell(cell d, int kl, int k, cell v)", + "name": "__tact_dict_replace_int_coins", + "signature": "(cell, (int)) __tact_dict_replace_int_coins(cell d, int kl, int k, int v)", }, { "code": { - "code": "var (old, ok) = null?(v) ? d~idict_delete_get_ref?(kl, k) : d~idict_replaceget_ref?(kl, k, v); + "code": "var (old, ok) = null?(v) ? d~idict_delete_get?(kl, k) : d~idict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse()); if (ok) { - return (d, old); + return (d, old~load_coins()); } else { return (d, null()); }", @@ -3152,8 +3506,8 @@ if (ok) { "flags": Set { "inline", }, - "name": "__tact_dict_replaceget_int_cell", - "signature": "(cell, (cell)) __tact_dict_replaceget_int_cell(cell d, int kl, int k, cell v)", + "name": "__tact_dict_replaceget_int_coins", + "signature": "(cell, (int)) __tact_dict_replaceget_int_coins(cell d, int kl, int k, int v)", }, { "code": { @@ -6315,6 +6669,132 @@ if (ok) { "name": "__tact_dict_replaceget_slice_cell", "signature": "(cell, (cell)) __tact_dict_replaceget_slice_cell(cell d, int kl, slice k, cell v)", }, + { + "code": { + "code": "var (r, ok) = __tact_dict_get(d, kl, k); +if (ok) { + return r~load_coins(); +} else { + return null(); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_get", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_get_slice_coins", + "signature": "int __tact_dict_get_slice_coins(cell d, int kl, slice k)", + }, + { + "code": { + "code": "var (key, value, flag) = __tact_dict_min(d, kl); +if (flag) { + return (key, value~load_coins(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_min", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_min_slice_coins", + "signature": "(slice, int, int) __tact_dict_min_slice_coins(cell d, int kl)", + }, + { + "code": { + "code": "var (key, value, flag) = __tact_dict_next(d, kl, pivot); +if (flag) { + return (key, value~load_coins(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_next", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_next_slice_coins", + "signature": "(slice, int, int) __tact_dict_next_slice_coins(cell d, int kl, slice pivot)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = __tact_dict_delete(d, kl, k); + return (r, ()); +} else { + return (dict_set_builder(d, kl, k, begin_cell().store_coins(v)), ()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_delete", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_set_slice_coins", + "signature": "(cell, ()) __tact_dict_set_slice_coins(cell d, int kl, slice k, int v)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = __tact_dict_delete(d, kl, k); + return (r, (ok)); +} else { + return dict_replace_builder?(d, kl, k, begin_cell().store_coins(v)); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_delete", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replace_slice_coins", + "signature": "(cell, (int)) __tact_dict_replace_slice_coins(cell d, int kl, slice k, int v)", + }, + { + "code": { + "code": "var (old, ok) = null?(v) ? d~__tact_dict_delete_get(kl, k) : d~dict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse()); +if (ok) { + return (d, old~load_coins()); +} else { + return (d, null()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_delete_get", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replaceget_slice_coins", + "signature": "(cell, (int)) __tact_dict_replaceget_slice_coins(cell d, int kl, slice k, int v)", + }, { "code": { "code": "var (r, ok) = udict_get?(d, kl, k); @@ -6625,7 +7105,121 @@ if (flag) { var (r, ok) = udict_delete?(d, kl, k); return (r, (ok)); } else { - return udict_replace_builder?(d, kl, k, begin_cell().store_uint(v, vl)); + return udict_replace_builder?(d, kl, k, begin_cell().store_uint(v, vl)); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replace_uint_uint", + "signature": "(cell, (int)) __tact_dict_replace_uint_uint(cell d, int kl, int k, int v, int vl)", + }, + { + "code": { + "code": "var (old, ok) = null?(v) ? d~udict_delete_get?(kl, k) : d~udict_replaceget?(kl, k, begin_cell().store_uint(v, vl).end_cell().begin_parse()); +if (ok) { + return (d, old~load_uint(vl)); +} else { + return (d, null()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replaceget_uint_uint", + "signature": "(cell, (int)) __tact_dict_replaceget_uint_uint(cell d, int kl, int k, int v, int vl)", + }, + { + "code": { + "code": "var (r, ok) = udict_get_ref?(d, kl, k); +if (ok) { + return r; +} else { + return null(); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_get_uint_cell", + "signature": "cell __tact_dict_get_uint_cell(cell d, int kl, int k)", + }, + { + "code": { + "code": "var (key, value, flag) = udict_get_min_ref?(d, kl); +if (flag) { + return (key, value, flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_min_uint_cell", + "signature": "(int, cell, int) __tact_dict_min_uint_cell(cell d, int kl)", + }, + { + "code": { + "code": "var (key, value, flag) = udict_get_next?(d, kl, pivot); +if (flag) { + return (key, value~load_ref(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_next_uint_cell", + "signature": "(int, cell, int) __tact_dict_next_uint_cell(cell d, int kl, int pivot)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = udict_delete?(d, kl, k); + return (r, ()); +} else { + return (udict_set_ref(d, kl, k, v), ()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_set_uint_cell", + "signature": "(cell, ()) __tact_dict_set_uint_cell(cell d, int kl, int k, cell v)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = udict_delete?(d, kl, k); + return (r, (ok)); +} else { + return udict_replace_ref?(d, kl, k, v); }", "kind": "generic", }, @@ -6635,14 +7229,14 @@ if (flag) { "flags": Set { "inline", }, - "name": "__tact_dict_replace_uint_uint", - "signature": "(cell, (int)) __tact_dict_replace_uint_uint(cell d, int kl, int k, int v, int vl)", + "name": "__tact_dict_replace_uint_cell", + "signature": "(cell, (int)) __tact_dict_replace_uint_cell(cell d, int kl, int k, cell v)", }, { "code": { - "code": "var (old, ok) = null?(v) ? d~udict_delete_get?(kl, k) : d~udict_replaceget?(kl, k, begin_cell().store_uint(v, vl).end_cell().begin_parse()); + "code": "var (old, ok) = null?(v) ? d~udict_delete_get_ref?(kl, k) : d~udict_replaceget_ref?(kl, k, v); if (ok) { - return (d, old~load_uint(vl)); + return (d, old); } else { return (d, null()); }", @@ -6654,14 +7248,14 @@ if (ok) { "flags": Set { "inline", }, - "name": "__tact_dict_replaceget_uint_uint", - "signature": "(cell, (int)) __tact_dict_replaceget_uint_uint(cell d, int kl, int k, int v, int vl)", + "name": "__tact_dict_replaceget_uint_cell", + "signature": "(cell, (cell)) __tact_dict_replaceget_uint_cell(cell d, int kl, int k, cell v)", }, { "code": { - "code": "var (r, ok) = udict_get_ref?(d, kl, k); + "code": "var (r, ok) = udict_get?(d, kl, k); if (ok) { - return r; + return r~load_coins(); } else { return null(); }", @@ -6673,14 +7267,14 @@ if (ok) { "flags": Set { "inline", }, - "name": "__tact_dict_get_uint_cell", - "signature": "cell __tact_dict_get_uint_cell(cell d, int kl, int k)", + "name": "__tact_dict_get_uint_coins", + "signature": "int __tact_dict_get_uint_coins(cell d, int kl, int k)", }, { "code": { - "code": "var (key, value, flag) = udict_get_min_ref?(d, kl); + "code": "var (key, value, flag) = udict_get_min?(d, kl); if (flag) { - return (key, value, flag); + return (key, value~load_coins(), flag); } else { return (null(), null(), flag); }", @@ -6692,14 +7286,14 @@ if (flag) { "flags": Set { "inline", }, - "name": "__tact_dict_min_uint_cell", - "signature": "(int, cell, int) __tact_dict_min_uint_cell(cell d, int kl)", + "name": "__tact_dict_min_uint_coins", + "signature": "(int, int, int) __tact_dict_min_uint_coins(cell d, int kl)", }, { "code": { "code": "var (key, value, flag) = udict_get_next?(d, kl, pivot); if (flag) { - return (key, value~load_ref(), flag); + return (key, value~load_coins(), flag); } else { return (null(), null(), flag); }", @@ -6711,8 +7305,8 @@ if (flag) { "flags": Set { "inline", }, - "name": "__tact_dict_next_uint_cell", - "signature": "(int, cell, int) __tact_dict_next_uint_cell(cell d, int kl, int pivot)", + "name": "__tact_dict_next_uint_coins", + "signature": "(int, int, int) __tact_dict_next_uint_coins(cell d, int kl, int pivot)", }, { "code": { @@ -6720,7 +7314,7 @@ if (flag) { var (r, ok) = udict_delete?(d, kl, k); return (r, ()); } else { - return (udict_set_ref(d, kl, k, v), ()); + return (udict_set_builder(d, kl, k, begin_cell().store_coins(v)), ()); }", "kind": "generic", }, @@ -6730,8 +7324,8 @@ if (flag) { "flags": Set { "inline", }, - "name": "__tact_dict_set_uint_cell", - "signature": "(cell, ()) __tact_dict_set_uint_cell(cell d, int kl, int k, cell v)", + "name": "__tact_dict_set_uint_coins", + "signature": "(cell, ()) __tact_dict_set_uint_coins(cell d, int kl, int k, int v)", }, { "code": { @@ -6739,7 +7333,7 @@ if (flag) { var (r, ok) = udict_delete?(d, kl, k); return (r, (ok)); } else { - return udict_replace_ref?(d, kl, k, v); + return udict_replace_builder?(d, kl, k, begin_cell().store_coins(v)); }", "kind": "generic", }, @@ -6749,14 +7343,14 @@ if (flag) { "flags": Set { "inline", }, - "name": "__tact_dict_replace_uint_cell", - "signature": "(cell, (int)) __tact_dict_replace_uint_cell(cell d, int kl, int k, cell v)", + "name": "__tact_dict_replace_uint_coins", + "signature": "(cell, (int)) __tact_dict_replace_uint_coins(cell d, int kl, int k, int v)", }, { "code": { - "code": "var (old, ok) = null?(v) ? d~udict_delete_get_ref?(kl, k) : d~udict_replaceget_ref?(kl, k, v); + "code": "var (old, ok) = null?(v) ? d~udict_delete_get?(kl, k) : d~udict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse()); if (ok) { - return (d, old); + return (d, old~load_coins()); } else { return (d, null()); }", @@ -6768,8 +7362,8 @@ if (ok) { "flags": Set { "inline", }, - "name": "__tact_dict_replaceget_uint_cell", - "signature": "(cell, (cell)) __tact_dict_replaceget_uint_cell(cell d, int kl, int k, cell v)", + "name": "__tact_dict_replaceget_uint_coins", + "signature": "(cell, (int)) __tact_dict_replaceget_uint_coins(cell d, int kl, int k, int v)", }, { "code": { @@ -7227,6 +7821,120 @@ if (ok) { "name": "__tact_dict_replaceget_int_cell", "signature": "(cell, (cell)) __tact_dict_replaceget_int_cell(cell d, int kl, int k, cell v)", }, + { + "code": { + "code": "var (r, ok) = idict_get?(d, kl, k); +if (ok) { + return r~load_coins(); +} else { + return null(); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_get_int_coins", + "signature": "int __tact_dict_get_int_coins(cell d, int kl, int k)", + }, + { + "code": { + "code": "var (key, value, flag) = idict_get_min?(d, kl); +if (flag) { + return (key, value~load_coins(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_min_int_coins", + "signature": "(int, int, int) __tact_dict_min_int_coins(cell d, int kl)", + }, + { + "code": { + "code": "var (key, value, flag) = idict_get_next?(d, kl, pivot); +if (flag) { + return (key, value~load_coins(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_next_int_coins", + "signature": "(int, int, int) __tact_dict_next_int_coins(cell d, int kl, int pivot)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = idict_delete?(d, kl, k); + return (r, ()); +} else { + return (idict_set_builder(d, kl, k, begin_cell().store_coins(v)), ()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_set_int_coins", + "signature": "(cell, ()) __tact_dict_set_int_coins(cell d, int kl, int k, int v)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = idict_delete?(d, kl, k); + return (r, (ok)); +} else { + return idict_replace_builder?(d, kl, k, begin_cell().store_coins(v)); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replace_int_coins", + "signature": "(cell, (int)) __tact_dict_replace_int_coins(cell d, int kl, int k, int v)", + }, + { + "code": { + "code": "var (old, ok) = null?(v) ? d~idict_delete_get?(kl, k) : d~idict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse()); +if (ok) { + return (d, old~load_coins()); +} else { + return (d, null()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replaceget_int_coins", + "signature": "(cell, (int)) __tact_dict_replaceget_int_coins(cell d, int kl, int k, int v)", + }, { "code": { "code": "var (r, ok) = __tact_dict_get(d, kl, k); @@ -10178,7 +10886,133 @@ if (flag) { "code": { "code": "var (key, value, flag) = __tact_dict_next(d, kl, pivot); if (flag) { - return (key, value~load_uint(vl), flag); + return (key, value~load_uint(vl), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_next", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_next_slice_uint", + "signature": "(slice, int, int) __tact_dict_next_slice_uint(cell d, int kl, slice pivot, int vl)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = __tact_dict_delete(d, kl, k); + return (r, ()); +} else { + return (dict_set_builder(d, kl, k, begin_cell().store_uint(v, vl)), ()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_delete", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_set_slice_uint", + "signature": "(cell, ()) __tact_dict_set_slice_uint(cell d, int kl, slice k, int v, int vl)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = __tact_dict_delete(d, kl, k); + return (r, (ok)); +} else { + return dict_replace_builder?(d, kl, k, begin_cell().store_uint(v, vl)); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_delete", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replace_slice_uint", + "signature": "(cell, (int)) __tact_dict_replace_slice_uint(cell d, int kl, slice k, int v, int vl)", + }, + { + "code": { + "code": "var (old, ok) = null?(v) ? d~__tact_dict_delete_get(kl, k) : d~dict_replaceget?(kl, k, begin_cell().store_uint(v, vl).end_cell().begin_parse()); +if (ok) { + return (d, old~load_uint(vl)); +} else { + return (d, null()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_delete_get", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replaceget_slice_uint", + "signature": "(cell, (int)) __tact_dict_replaceget_slice_uint(cell d, int kl, slice k, int v, int vl)", + }, + { + "code": { + "code": "var (r, ok) = __tact_dict_get_ref(d, kl, k); +if (ok) { + return r; +} else { + return null(); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_get_ref", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_get_slice_cell", + "signature": "cell __tact_dict_get_slice_cell(cell d, int kl, slice k)", + }, + { + "code": { + "code": "var (key, value, flag) = __tact_dict_min_ref(d, kl); +if (flag) { + return (key, value, flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_min_ref", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_min_slice_cell", + "signature": "(slice, cell, int) __tact_dict_min_slice_cell(cell d, int kl)", + }, + { + "code": { + "code": "var (key, value, flag) = __tact_dict_next(d, kl, pivot); +if (flag) { + return (key, value~load_ref(), flag); } else { return (null(), null(), flag); }", @@ -10192,8 +11026,8 @@ if (flag) { "flags": Set { "inline", }, - "name": "__tact_dict_next_slice_uint", - "signature": "(slice, int, int) __tact_dict_next_slice_uint(cell d, int kl, slice pivot, int vl)", + "name": "__tact_dict_next_slice_cell", + "signature": "(slice, cell, int) __tact_dict_next_slice_cell(cell d, int kl, slice pivot)", }, { "code": { @@ -10201,7 +11035,7 @@ if (flag) { var (r, ok) = __tact_dict_delete(d, kl, k); return (r, ()); } else { - return (dict_set_builder(d, kl, k, begin_cell().store_uint(v, vl)), ()); + return __tact_dict_set_ref(d, kl, k, v); }", "kind": "generic", }, @@ -10209,12 +11043,13 @@ if (flag) { "context": "stdlib", "depends": Set { "__tact_dict_delete", + "__tact_dict_set_ref", }, "flags": Set { "inline", }, - "name": "__tact_dict_set_slice_uint", - "signature": "(cell, ()) __tact_dict_set_slice_uint(cell d, int kl, slice k, int v, int vl)", + "name": "__tact_dict_set_slice_cell", + "signature": "(cell, ()) __tact_dict_set_slice_cell(cell d, int kl, slice k, cell v)", }, { "code": { @@ -10222,7 +11057,7 @@ if (flag) { var (r, ok) = __tact_dict_delete(d, kl, k); return (r, (ok)); } else { - return dict_replace_builder?(d, kl, k, begin_cell().store_uint(v, vl)); + return __tact_dict_replace_ref(d, kl, k, v); }", "kind": "generic", }, @@ -10230,18 +11065,19 @@ if (flag) { "context": "stdlib", "depends": Set { "__tact_dict_delete", + "__tact_dict_replace_ref", }, "flags": Set { "inline", }, - "name": "__tact_dict_replace_slice_uint", - "signature": "(cell, (int)) __tact_dict_replace_slice_uint(cell d, int kl, slice k, int v, int vl)", + "name": "__tact_dict_replace_slice_cell", + "signature": "(cell, (int)) __tact_dict_replace_slice_cell(cell d, int kl, slice k, cell v)", }, { "code": { - "code": "var (old, ok) = null?(v) ? d~__tact_dict_delete_get(kl, k) : d~dict_replaceget?(kl, k, begin_cell().store_uint(v, vl).end_cell().begin_parse()); + "code": "var (old, ok) = null?(v) ? d~__tact_dict_delete_get_ref(kl, k) : d~__tact_dict_replaceget_ref(kl, k, v); if (ok) { - return (d, old~load_uint(vl)); + return (d, old); } else { return (d, null()); }", @@ -10250,19 +11086,20 @@ if (ok) { "comment": null, "context": "stdlib", "depends": Set { - "__tact_dict_delete_get", + "__tact_dict_delete_get_ref", + "__tact_dict_replaceget_ref", }, "flags": Set { "inline", }, - "name": "__tact_dict_replaceget_slice_uint", - "signature": "(cell, (int)) __tact_dict_replaceget_slice_uint(cell d, int kl, slice k, int v, int vl)", + "name": "__tact_dict_replaceget_slice_cell", + "signature": "(cell, (cell)) __tact_dict_replaceget_slice_cell(cell d, int kl, slice k, cell v)", }, { "code": { - "code": "var (r, ok) = __tact_dict_get_ref(d, kl, k); + "code": "var (r, ok) = __tact_dict_get(d, kl, k); if (ok) { - return r; + return r~load_coins(); } else { return null(); }", @@ -10271,19 +11108,19 @@ if (ok) { "comment": null, "context": "stdlib", "depends": Set { - "__tact_dict_get_ref", + "__tact_dict_get", }, "flags": Set { "inline", }, - "name": "__tact_dict_get_slice_cell", - "signature": "cell __tact_dict_get_slice_cell(cell d, int kl, slice k)", + "name": "__tact_dict_get_slice_coins", + "signature": "int __tact_dict_get_slice_coins(cell d, int kl, slice k)", }, { "code": { - "code": "var (key, value, flag) = __tact_dict_min_ref(d, kl); + "code": "var (key, value, flag) = __tact_dict_min(d, kl); if (flag) { - return (key, value, flag); + return (key, value~load_coins(), flag); } else { return (null(), null(), flag); }", @@ -10292,19 +11129,19 @@ if (flag) { "comment": null, "context": "stdlib", "depends": Set { - "__tact_dict_min_ref", + "__tact_dict_min", }, "flags": Set { "inline", }, - "name": "__tact_dict_min_slice_cell", - "signature": "(slice, cell, int) __tact_dict_min_slice_cell(cell d, int kl)", + "name": "__tact_dict_min_slice_coins", + "signature": "(slice, int, int) __tact_dict_min_slice_coins(cell d, int kl)", }, { "code": { "code": "var (key, value, flag) = __tact_dict_next(d, kl, pivot); if (flag) { - return (key, value~load_ref(), flag); + return (key, value~load_coins(), flag); } else { return (null(), null(), flag); }", @@ -10318,8 +11155,8 @@ if (flag) { "flags": Set { "inline", }, - "name": "__tact_dict_next_slice_cell", - "signature": "(slice, cell, int) __tact_dict_next_slice_cell(cell d, int kl, slice pivot)", + "name": "__tact_dict_next_slice_coins", + "signature": "(slice, int, int) __tact_dict_next_slice_coins(cell d, int kl, slice pivot)", }, { "code": { @@ -10327,7 +11164,7 @@ if (flag) { var (r, ok) = __tact_dict_delete(d, kl, k); return (r, ()); } else { - return __tact_dict_set_ref(d, kl, k, v); + return (dict_set_builder(d, kl, k, begin_cell().store_coins(v)), ()); }", "kind": "generic", }, @@ -10335,13 +11172,12 @@ if (flag) { "context": "stdlib", "depends": Set { "__tact_dict_delete", - "__tact_dict_set_ref", }, "flags": Set { "inline", }, - "name": "__tact_dict_set_slice_cell", - "signature": "(cell, ()) __tact_dict_set_slice_cell(cell d, int kl, slice k, cell v)", + "name": "__tact_dict_set_slice_coins", + "signature": "(cell, ()) __tact_dict_set_slice_coins(cell d, int kl, slice k, int v)", }, { "code": { @@ -10349,7 +11185,7 @@ if (flag) { var (r, ok) = __tact_dict_delete(d, kl, k); return (r, (ok)); } else { - return __tact_dict_replace_ref(d, kl, k, v); + return dict_replace_builder?(d, kl, k, begin_cell().store_coins(v)); }", "kind": "generic", }, @@ -10357,19 +11193,18 @@ if (flag) { "context": "stdlib", "depends": Set { "__tact_dict_delete", - "__tact_dict_replace_ref", }, "flags": Set { "inline", }, - "name": "__tact_dict_replace_slice_cell", - "signature": "(cell, (int)) __tact_dict_replace_slice_cell(cell d, int kl, slice k, cell v)", + "name": "__tact_dict_replace_slice_coins", + "signature": "(cell, (int)) __tact_dict_replace_slice_coins(cell d, int kl, slice k, int v)", }, { "code": { - "code": "var (old, ok) = null?(v) ? d~__tact_dict_delete_get_ref(kl, k) : d~__tact_dict_replaceget_ref(kl, k, v); + "code": "var (old, ok) = null?(v) ? d~__tact_dict_delete_get(kl, k) : d~dict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse()); if (ok) { - return (d, old); + return (d, old~load_coins()); } else { return (d, null()); }", @@ -10378,14 +11213,13 @@ if (ok) { "comment": null, "context": "stdlib", "depends": Set { - "__tact_dict_delete_get_ref", - "__tact_dict_replaceget_ref", + "__tact_dict_delete_get", }, "flags": Set { "inline", }, - "name": "__tact_dict_replaceget_slice_cell", - "signature": "(cell, (cell)) __tact_dict_replaceget_slice_cell(cell d, int kl, slice k, cell v)", + "name": "__tact_dict_replaceget_slice_coins", + "signature": "(cell, (int)) __tact_dict_replaceget_slice_coins(cell d, int kl, slice k, int v)", }, { "code": { @@ -10843,6 +11677,120 @@ if (ok) { "name": "__tact_dict_replaceget_uint_cell", "signature": "(cell, (cell)) __tact_dict_replaceget_uint_cell(cell d, int kl, int k, cell v)", }, + { + "code": { + "code": "var (r, ok) = udict_get?(d, kl, k); +if (ok) { + return r~load_coins(); +} else { + return null(); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_get_uint_coins", + "signature": "int __tact_dict_get_uint_coins(cell d, int kl, int k)", + }, + { + "code": { + "code": "var (key, value, flag) = udict_get_min?(d, kl); +if (flag) { + return (key, value~load_coins(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_min_uint_coins", + "signature": "(int, int, int) __tact_dict_min_uint_coins(cell d, int kl)", + }, + { + "code": { + "code": "var (key, value, flag) = udict_get_next?(d, kl, pivot); +if (flag) { + return (key, value~load_coins(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_next_uint_coins", + "signature": "(int, int, int) __tact_dict_next_uint_coins(cell d, int kl, int pivot)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = udict_delete?(d, kl, k); + return (r, ()); +} else { + return (udict_set_builder(d, kl, k, begin_cell().store_coins(v)), ()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_set_uint_coins", + "signature": "(cell, ()) __tact_dict_set_uint_coins(cell d, int kl, int k, int v)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = udict_delete?(d, kl, k); + return (r, (ok)); +} else { + return udict_replace_builder?(d, kl, k, begin_cell().store_coins(v)); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replace_uint_coins", + "signature": "(cell, (int)) __tact_dict_replace_uint_coins(cell d, int kl, int k, int v)", + }, + { + "code": { + "code": "var (old, ok) = null?(v) ? d~udict_delete_get?(kl, k) : d~udict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse()); +if (ok) { + return (d, old~load_coins()); +} else { + return (d, null()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replaceget_uint_coins", + "signature": "(cell, (int)) __tact_dict_replaceget_uint_coins(cell d, int kl, int k, int v)", + }, { "code": { "code": "var (r, ok) = idict_get?(d, kl, k); @@ -11299,6 +12247,120 @@ if (ok) { "name": "__tact_dict_replaceget_int_cell", "signature": "(cell, (cell)) __tact_dict_replaceget_int_cell(cell d, int kl, int k, cell v)", }, + { + "code": { + "code": "var (r, ok) = idict_get?(d, kl, k); +if (ok) { + return r~load_coins(); +} else { + return null(); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_get_int_coins", + "signature": "int __tact_dict_get_int_coins(cell d, int kl, int k)", + }, + { + "code": { + "code": "var (key, value, flag) = idict_get_min?(d, kl); +if (flag) { + return (key, value~load_coins(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_min_int_coins", + "signature": "(int, int, int) __tact_dict_min_int_coins(cell d, int kl)", + }, + { + "code": { + "code": "var (key, value, flag) = idict_get_next?(d, kl, pivot); +if (flag) { + return (key, value~load_coins(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_next_int_coins", + "signature": "(int, int, int) __tact_dict_next_int_coins(cell d, int kl, int pivot)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = idict_delete?(d, kl, k); + return (r, ()); +} else { + return (idict_set_builder(d, kl, k, begin_cell().store_coins(v)), ()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_set_int_coins", + "signature": "(cell, ()) __tact_dict_set_int_coins(cell d, int kl, int k, int v)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = idict_delete?(d, kl, k); + return (r, (ok)); +} else { + return idict_replace_builder?(d, kl, k, begin_cell().store_coins(v)); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replace_int_coins", + "signature": "(cell, (int)) __tact_dict_replace_int_coins(cell d, int kl, int k, int v)", + }, + { + "code": { + "code": "var (old, ok) = null?(v) ? d~idict_delete_get?(kl, k) : d~idict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse()); +if (ok) { + return (d, old~load_coins()); +} else { + return (d, null()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replaceget_int_coins", + "signature": "(cell, (int)) __tact_dict_replaceget_int_coins(cell d, int kl, int k, int v)", + }, { "code": { "code": "var (r, ok) = __tact_dict_get(d, kl, k); diff --git a/src/generator/writers/writeStdlib.ts b/src/generator/writers/writeStdlib.ts index 5ca630c6d..790a4e5f1 100644 --- a/src/generator/writers/writeStdlib.ts +++ b/src/generator/writers/writeStdlib.ts @@ -1152,16 +1152,24 @@ export function writeStdlib(ctx: WriterContext): void { const keyTypes = ["slice", "uint", "int"] as const; type KeyType = (typeof keyTypes)[number]; -const valTypes = ["slice", "int", "uint", "cell"] as const; +const valTypes = ["slice", "int", "uint", "cell", "coins"] as const; type ValType = (typeof valTypes)[number]; +function getSignatureKeyType(key: KeyType): KeyType { + return key === "uint" ? "int" : key; +} + +function getSignatureValueType(value: ValType): ValType { + return value === "uint" || value === "coins" ? "int" : value; +} + function genTactDictGet( ctx: WriterContext, key: KeyType, value: ValType, ): void { - const signatureKeyType = key === "uint" ? "int" : key; - const signatureRetType = value === "uint" ? "int" : value; + const signatureKeyType = getSignatureKeyType(key); + const signatureValueType = getSignatureValueType(value); const dictGet = () => { const cellSuffix = value === "cell" ? "_ref" : ""; switch (key) { @@ -1182,12 +1190,15 @@ function genTactDictGet( return "r~load_uint(vl)"; case "int": return "r~load_int(vl)"; + case "coins": + return "r~load_coins()"; } }; const valBitsArg = () => { switch (value) { case "slice": case "cell": + case "coins": return ""; case "uint": case "int": @@ -1196,7 +1207,7 @@ function genTactDictGet( }; ctx.fun(`__tact_dict_get_${key}_${value}`, () => { ctx.signature( - `${signatureRetType} __tact_dict_get_${key}_${value}(cell d, int kl, ${signatureKeyType} k${valBitsArg()})`, + `${signatureValueType} __tact_dict_get_${key}_${value}(cell d, int kl, ${signatureKeyType} k${valBitsArg()})`, ); ctx.flag("inline"); ctx.context("stdlib"); @@ -1214,7 +1225,7 @@ function genTactDictGet( } function genTactDictExists(ctx: WriterContext, key: KeyType): void { - const signatureKeyType = key === "uint" ? "int" : key; + const signatureKeyType = getSignatureKeyType(key); const dictGet = () => { switch (key) { case "slice": @@ -1245,12 +1256,13 @@ function genTactDictSet( key: KeyType, value: ValType, ): void { - const signatureKeyType = key === "uint" ? "int" : key; - const signatureValueType = value === "uint" ? "int" : value; + const signatureKeyType = getSignatureKeyType(key); + const signatureValueType = getSignatureValueType(value); const valBitsArg = () => { switch (value) { case "slice": case "cell": + case "coins": return ""; case "uint": case "int": @@ -1273,14 +1285,20 @@ function genTactDictSet( return "(idict_set_builder(d, kl, k, begin_cell().store_int(v, vl)), ())"; case "int:uint": return "(idict_set_builder(d, kl, k, begin_cell().store_uint(v, vl)), ())"; + case "int:coins": + return "(idict_set_builder(d, kl, k, begin_cell().store_coins(v)), ())"; case "uint:int": return "(udict_set_builder(d, kl, k, begin_cell().store_int(v, vl)), ())"; case "uint:uint": return "(udict_set_builder(d, kl, k, begin_cell().store_uint(v, vl)), ())"; + case "uint:coins": + return "(udict_set_builder(d, kl, k, begin_cell().store_coins(v)), ())"; case "slice:int": return "(dict_set_builder(d, kl, k, begin_cell().store_int(v, vl)), ())"; case "slice:uint": return "(dict_set_builder(d, kl, k, begin_cell().store_uint(v, vl)), ())"; + case "slice:coins": + return "(dict_set_builder(d, kl, k, begin_cell().store_coins(v)), ())"; case "int:cell": return "(idict_set_ref(d, kl, k, v), ())"; case "uint:cell": @@ -1323,12 +1341,13 @@ function genTactDictGetMin( key: KeyType, value: ValType, ): void { - const signatureKeyType = key === "uint" ? "int" : key; - const signatureValType = value === "uint" ? "int" : value; + const signatureKeyType = getSignatureKeyType(key); + const signatureValueType = getSignatureValueType(value); const valBitsArg = () => { switch (value) { case "slice": case "cell": + case "coins": return ""; case "uint": case "int": @@ -1339,14 +1358,17 @@ function genTactDictGetMin( switch (`${key}:${value}`) { case "int:int": case "int:uint": + case "int:coins": case "int:slice": return "idict_get_min?"; case "uint:int": case "uint:uint": + case "uint:coins": case "uint:slice": return "udict_get_min?"; case "slice:int": case "slice:uint": + case "slice:coins": case "slice:slice": return ctx.used("__tact_dict_min"); case "int:cell": @@ -1367,6 +1389,8 @@ function genTactDictGetMin( return "value~load_int(vl)"; case "uint": return "value~load_uint(vl)"; + case "coins": + return "value~load_coins()"; case "slice": case "cell": return "value"; @@ -1374,7 +1398,7 @@ function genTactDictGetMin( }; ctx.fun(`__tact_dict_min_${key}_${value}`, () => { ctx.signature( - `(${signatureKeyType}, ${signatureValType}, int) __tact_dict_min_${key}_${value}(cell d, int kl${valBitsArg()})`, + `(${signatureKeyType}, ${signatureValueType}, int) __tact_dict_min_${key}_${value}(cell d, int kl${valBitsArg()})`, ); ctx.flag("inline"); ctx.context("stdlib"); @@ -1396,12 +1420,13 @@ function genTactDictGetNext( key: KeyType, value: ValType, ): void { - const signatureKeyType = key === "uint" ? "int" : key; - const signatureValType = value === "uint" ? "int" : value; + const signatureKeyType = getSignatureKeyType(key); + const signatureValueType = getSignatureValueType(value); const valBitsArg = () => { switch (value) { case "slice": case "cell": + case "coins": return ""; case "uint": case "int": @@ -1424,6 +1449,8 @@ function genTactDictGetNext( return "value~load_int(vl)"; case "uint": return "value~load_uint(vl)"; + case "coins": + return "value~load_coins()"; case "slice": return "value"; case "cell": @@ -1432,7 +1459,7 @@ function genTactDictGetNext( }; ctx.fun(`__tact_dict_next_${key}_${value}`, () => { ctx.signature( - `(${signatureKeyType}, ${signatureValType}, int) __tact_dict_next_${key}_${value}(cell d, int kl, ${signatureKeyType} pivot${valBitsArg()})`, + `(${signatureKeyType}, ${signatureValueType}, int) __tact_dict_next_${key}_${value}(cell d, int kl, ${signatureKeyType} pivot${valBitsArg()})`, ); ctx.flag("inline"); ctx.context("stdlib"); @@ -1460,12 +1487,13 @@ function genTactDictReplace( key: KeyType, value: ValType, ): void { - const signatureKeyType = key === "uint" ? "int" : key; - const signatureValueType = value === "uint" ? "int" : value; + const signatureKeyType = getSignatureKeyType(key); + const signatureValueType = getSignatureValueType(value); const valBitsArg = () => { switch (value) { case "slice": case "cell": + case "coins": return ""; case "uint": case "int": @@ -1488,14 +1516,20 @@ function genTactDictReplace( return "idict_replace_builder?(d, kl, k, begin_cell().store_int(v, vl))"; case "int:uint": return "idict_replace_builder?(d, kl, k, begin_cell().store_uint(v, vl))"; + case "int:coins": + return "idict_replace_builder?(d, kl, k, begin_cell().store_coins(v))"; case "uint:int": return "udict_replace_builder?(d, kl, k, begin_cell().store_int(v, vl))"; case "uint:uint": return "udict_replace_builder?(d, kl, k, begin_cell().store_uint(v, vl))"; + case "uint:coins": + return "udict_replace_builder?(d, kl, k, begin_cell().store_coins(v))"; case "slice:int": return "dict_replace_builder?(d, kl, k, begin_cell().store_int(v, vl))"; case "slice:uint": return "dict_replace_builder?(d, kl, k, begin_cell().store_uint(v, vl))"; + case "slice:coins": + return "dict_replace_builder?(d, kl, k, begin_cell().store_coins(v))"; case "int:cell": return "idict_replace_ref?(d, kl, k, v)"; case "uint:cell": @@ -1538,12 +1572,13 @@ function genTactDictReplaceGet( key: KeyType, value: ValType, ): void { - const signatureKeyType = key === "uint" ? "int" : key; - const signatureValueType = value === "uint" ? "int" : value; + const signatureKeyType = getSignatureKeyType(key); + const signatureValueType = getSignatureValueType(value); const valBitsArg = () => { switch (value) { case "slice": case "cell": + case "coins": return ""; case "uint": case "int": @@ -1567,14 +1602,20 @@ function genTactDictReplaceGet( return "d~idict_replaceget?(kl, k, begin_cell().store_int(v, vl).end_cell().begin_parse())"; case "int:uint": return "d~idict_replaceget?(kl, k, begin_cell().store_uint(v, vl).end_cell().begin_parse())"; + case "int:coins": + return "d~idict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse())"; case "uint:int": return "d~udict_replaceget?(kl, k, begin_cell().store_int(v, vl).end_cell().begin_parse())"; case "uint:uint": return "d~udict_replaceget?(kl, k, begin_cell().store_uint(v, vl).end_cell().begin_parse())"; + case "uint:coins": + return "d~udict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse())"; case "slice:int": return "d~dict_replaceget?(kl, k, begin_cell().store_int(v, vl).end_cell().begin_parse())"; case "slice:uint": return "d~dict_replaceget?(kl, k, begin_cell().store_uint(v, vl).end_cell().begin_parse())"; + case "slice:coins": + return "d~dict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse())"; case "int:cell": return "d~idict_replaceget_ref?(kl, k, v)"; case "uint:cell": @@ -1602,6 +1643,8 @@ function genTactDictReplaceGet( return "old~load_uint(vl)"; case "int": return "old~load_int(vl)"; + case "coins": + return "old~load_coins()"; } }; ctx.fun(`__tact_dict_replaceget_${key}_${value}`, () => { diff --git a/src/test/e2e-emulated/contracts/maps.tact b/src/test/e2e-emulated/contracts/maps.tact index 64ddf2f8e..2fb55bddc 100644 --- a/src/test/e2e-emulated/contracts/maps.tact +++ b/src/test/e2e-emulated/contracts/maps.tact @@ -20,6 +20,7 @@ struct GetAllMapsResult { int_uint8: Int?; int_uint42: Int?; int_uint256: Int?; + int_coins: Int?; int_bool: Bool?; int_cell: Cell?; int_address: Address?; @@ -32,6 +33,7 @@ struct GetAllMapsResult { int8_uint8: Int?; int8_uint42: Int?; int8_uint256: Int?; + int8_coins: Int?; int8_bool: Bool?; int8_cell: Cell?; int8_address: Address?; @@ -44,6 +46,7 @@ struct GetAllMapsResult { int42_uint8: Int?; int42_uint42: Int?; int42_uint256: Int?; + int42_coins: Int?; int42_bool: Bool?; int42_cell: Cell?; int42_address: Address?; @@ -56,6 +59,7 @@ struct GetAllMapsResult { int256_uint8: Int?; int256_uint42: Int?; int256_uint256: Int?; + int256_coins: Int?; int256_bool: Bool?; int256_cell: Cell?; int256_address: Address?; @@ -68,6 +72,7 @@ struct GetAllMapsResult { uint8_uint8: Int?; uint8_uint42: Int?; uint8_uint256: Int?; + uint8_coins: Int?; uint8_bool: Bool?; uint8_cell: Cell?; uint8_address: Address?; @@ -80,6 +85,7 @@ struct GetAllMapsResult { uint42_uint8: Int?; uint42_uint42: Int?; uint42_uint256: Int?; + uint42_coins: Int?; uint42_bool: Bool?; uint42_cell: Cell?; uint42_address: Address?; @@ -92,6 +98,7 @@ struct GetAllMapsResult { uint256_uint8: Int?; uint256_uint42: Int?; uint256_uint256: Int?; + uint256_coins: Int?; uint256_bool: Bool?; uint256_cell: Cell?; uint256_address: Address?; @@ -105,6 +112,7 @@ struct GetAllMapsResult { address_uint8: Int?; address_uint42: Int?; address_uint256: Int?; + address_coins: Int?; address_bool: Bool?; address_cell: Cell?; address_address: Address?; @@ -120,6 +128,7 @@ struct ReplaceAllMapsResult { int_uint8: Bool; int_uint42: Bool; int_uint256: Bool; + int_coins: Bool; int_bool: Bool; int_cell: Bool; int_address: Bool; @@ -132,6 +141,7 @@ struct ReplaceAllMapsResult { int8_uint8: Bool; int8_uint42: Bool; int8_uint256: Bool; + int8_coins: Bool; int8_bool: Bool; int8_cell: Bool; int8_address: Bool; @@ -144,6 +154,7 @@ struct ReplaceAllMapsResult { int42_uint8: Bool; int42_uint42: Bool; int42_uint256: Bool; + int42_coins: Bool; int42_bool: Bool; int42_cell: Bool; int42_address: Bool; @@ -156,6 +167,7 @@ struct ReplaceAllMapsResult { int256_uint8: Bool; int256_uint42: Bool; int256_uint256: Bool; + int256_coins: Bool; int256_bool: Bool; int256_cell: Bool; int256_address: Bool; @@ -168,6 +180,7 @@ struct ReplaceAllMapsResult { uint8_uint8: Bool; uint8_uint42: Bool; uint8_uint256: Bool; + uint8_coins: Bool; uint8_bool: Bool; uint8_cell: Bool; uint8_address: Bool; @@ -180,6 +193,7 @@ struct ReplaceAllMapsResult { uint42_uint8: Bool; uint42_uint42: Bool; uint42_uint256: Bool; + uint42_coins: Bool; uint42_bool: Bool; uint42_cell: Bool; uint42_address: Bool; @@ -192,6 +206,7 @@ struct ReplaceAllMapsResult { uint256_uint8: Bool; uint256_uint42: Bool; uint256_uint256: Bool; + uint256_coins: Bool; uint256_bool: Bool; uint256_cell: Bool; uint256_address: Bool; @@ -205,6 +220,7 @@ struct ReplaceAllMapsResult { address_uint8: Bool; address_uint42: Bool; address_uint256: Bool; + address_coins: Bool; address_bool: Bool; address_cell: Bool; address_address: Bool; @@ -220,6 +236,7 @@ struct ReplaceGetAllMapsResult { int_uint8: Int?; int_uint42: Int?; int_uint256: Int?; + int_coins: Int?; int_bool: Bool?; int_cell: Cell?; int_address: Address?; @@ -232,6 +249,7 @@ struct ReplaceGetAllMapsResult { int8_uint8: Int?; int8_uint42: Int?; int8_uint256: Int?; + int8_coins: Int?; int8_bool: Bool?; int8_cell: Cell?; int8_address: Address?; @@ -244,6 +262,7 @@ struct ReplaceGetAllMapsResult { int42_uint8: Int?; int42_uint42: Int?; int42_uint256: Int?; + int42_coins: Int?; int42_bool: Bool?; int42_cell: Cell?; int42_address: Address?; @@ -256,6 +275,7 @@ struct ReplaceGetAllMapsResult { int256_uint8: Int?; int256_uint42: Int?; int256_uint256: Int?; + int256_coins: Int?; int256_bool: Bool?; int256_cell: Cell?; int256_address: Address?; @@ -268,6 +288,7 @@ struct ReplaceGetAllMapsResult { uint8_uint8: Int?; uint8_uint42: Int?; uint8_uint256: Int?; + uint8_coins: Int?; uint8_bool: Bool?; uint8_cell: Cell?; uint8_address: Address?; @@ -280,6 +301,7 @@ struct ReplaceGetAllMapsResult { uint42_uint8: Int?; uint42_uint42: Int?; uint42_uint256: Int?; + uint42_coins: Int?; uint42_bool: Bool?; uint42_cell: Cell?; uint42_address: Address?; @@ -292,6 +314,7 @@ struct ReplaceGetAllMapsResult { uint256_uint8: Int?; uint256_uint42: Int?; uint256_uint256: Int?; + uint256_coins: Int?; uint256_bool: Bool?; uint256_cell: Cell?; uint256_address: Address?; @@ -305,6 +328,7 @@ struct ReplaceGetAllMapsResult { address_uint8: Int?; address_uint42: Int?; address_uint256: Int?; + address_coins: Int?; address_bool: Bool?; address_cell: Cell?; address_address: Address?; @@ -320,6 +344,7 @@ struct ExistsAllMapsResult { int_uint8: Bool; int_uint42: Bool; int_uint256: Bool; + int_coins: Bool; int_bool: Bool; int_cell: Bool; int_address: Bool; @@ -332,6 +357,7 @@ struct ExistsAllMapsResult { int8_uint8: Bool; int8_uint42: Bool; int8_uint256: Bool; + int8_coins: Bool; int8_bool: Bool; int8_cell: Bool; int8_address: Bool; @@ -344,6 +370,7 @@ struct ExistsAllMapsResult { int42_uint8: Bool; int42_uint42: Bool; int42_uint256: Bool; + int42_coins: Bool; int42_bool: Bool; int42_cell: Bool; int42_address: Bool; @@ -356,6 +383,7 @@ struct ExistsAllMapsResult { int256_uint8: Bool; int256_uint42: Bool; int256_uint256: Bool; + int256_coins: Bool; int256_bool: Bool; int256_cell: Bool; int256_address: Bool; @@ -368,6 +396,7 @@ struct ExistsAllMapsResult { uint8_uint8: Bool; uint8_uint42: Bool; uint8_uint256: Bool; + uint8_coins: Bool; uint8_bool: Bool; uint8_cell: Bool; uint8_address: Bool; @@ -380,6 +409,7 @@ struct ExistsAllMapsResult { uint42_uint8: Bool; uint42_uint42: Bool; uint42_uint256: Bool; + uint42_coins: Bool; uint42_bool: Bool; uint42_cell: Bool; uint42_address: Bool; @@ -392,6 +422,7 @@ struct ExistsAllMapsResult { uint256_uint8: Bool; uint256_uint42: Bool; uint256_uint256: Bool; + uint256_coins: Bool; uint256_bool: Bool; uint256_cell: Bool; uint256_address: Bool; @@ -405,6 +436,7 @@ struct ExistsAllMapsResult { address_uint8: Bool; address_uint42: Bool; address_uint256: Bool; + address_coins: Bool; address_bool: Bool; address_cell: Bool; address_address: Bool; @@ -420,6 +452,7 @@ struct IsEmptyAllMapsResult { int_uint8: Bool; int_uint42: Bool; int_uint256: Bool; + int_coins: Bool; int_bool: Bool; int_cell: Bool; int_address: Bool; @@ -432,6 +465,7 @@ struct IsEmptyAllMapsResult { int8_uint8: Bool; int8_uint42: Bool; int8_uint256: Bool; + int8_coins: Bool; int8_bool: Bool; int8_cell: Bool; int8_address: Bool; @@ -444,6 +478,7 @@ struct IsEmptyAllMapsResult { int42_uint8: Bool; int42_uint42: Bool; int42_uint256: Bool; + int42_coins: Bool; int42_bool: Bool; int42_cell: Bool; int42_address: Bool; @@ -456,6 +491,7 @@ struct IsEmptyAllMapsResult { int256_uint8: Bool; int256_uint42: Bool; int256_uint256: Bool; + int256_coins: Bool; int256_bool: Bool; int256_cell: Bool; int256_address: Bool; @@ -468,6 +504,7 @@ struct IsEmptyAllMapsResult { uint8_uint8: Bool; uint8_uint42: Bool; uint8_uint256: Bool; + uint8_coins: Bool; uint8_bool: Bool; uint8_cell: Bool; uint8_address: Bool; @@ -480,6 +517,7 @@ struct IsEmptyAllMapsResult { uint42_uint8: Bool; uint42_uint42: Bool; uint42_uint256: Bool; + uint42_coins: Bool; uint42_bool: Bool; uint42_cell: Bool; uint42_address: Bool; @@ -492,6 +530,7 @@ struct IsEmptyAllMapsResult { uint256_uint8: Bool; uint256_uint42: Bool; uint256_uint256: Bool; + uint256_coins: Bool; uint256_bool: Bool; uint256_cell: Bool; uint256_address: Bool; @@ -505,6 +544,7 @@ struct IsEmptyAllMapsResult { address_uint8: Bool; address_uint42: Bool; address_uint256: Bool; + address_coins: Bool; address_bool: Bool; address_cell: Bool; address_address: Bool; @@ -520,6 +560,7 @@ struct AsCellAllMapsResult { int_uint8: Cell?; int_uint42: Cell?; int_uint256: Cell?; + int_coins: Cell?; int_bool: Cell?; int_cell: Cell?; int_address: Cell?; @@ -532,6 +573,7 @@ struct AsCellAllMapsResult { int8_uint8: Cell?; int8_uint42: Cell?; int8_uint256: Cell?; + int8_coins: Cell?; int8_bool: Cell?; int8_cell: Cell?; int8_address: Cell?; @@ -544,6 +586,7 @@ struct AsCellAllMapsResult { int42_uint8: Cell?; int42_uint42: Cell?; int42_uint256: Cell?; + int42_coins: Cell?; int42_bool: Cell?; int42_cell: Cell?; int42_address: Cell?; @@ -556,6 +599,7 @@ struct AsCellAllMapsResult { int256_uint8: Cell?; int256_uint42: Cell?; int256_uint256: Cell?; + int256_coins: Cell?; int256_bool: Cell?; int256_cell: Cell?; int256_address: Cell?; @@ -568,6 +612,7 @@ struct AsCellAllMapsResult { uint8_uint8: Cell?; uint8_uint42: Cell?; uint8_uint256: Cell?; + uint8_coins: Cell?; uint8_bool: Cell?; uint8_cell: Cell?; uint8_address: Cell?; @@ -580,6 +625,7 @@ struct AsCellAllMapsResult { uint42_uint8: Cell?; uint42_uint42: Cell?; uint42_uint256: Cell?; + uint42_coins: Cell?; uint42_bool: Cell?; uint42_cell: Cell?; uint42_address: Cell?; @@ -592,6 +638,7 @@ struct AsCellAllMapsResult { uint256_uint8: Cell?; uint256_uint42: Cell?; uint256_uint256: Cell?; + uint256_coins: Cell?; uint256_bool: Cell?; uint256_cell: Cell?; uint256_address: Cell?; @@ -605,6 +652,7 @@ struct AsCellAllMapsResult { address_uint8: Cell?; address_uint42: Cell?; address_uint256: Cell?; + address_coins: Cell?; address_bool: Cell?; address_cell: Cell?; address_address: Cell?; @@ -634,6 +682,7 @@ message SetAllMaps { valueUint8: Int?; valueUint42: Int?; valueUint256: Int?; + valueCoins: Int?; valueBool: Bool?; valueCell: Cell?; valueAddress: Address?; @@ -671,6 +720,7 @@ message ReplaceAllMaps { valueUint8: Int?; valueUint42: Int?; valueUint256: Int?; + valueCoins: Int?; valueBool: Bool?; valueCell: Cell?; valueAddress: Address?; @@ -696,6 +746,7 @@ message ReplaceGetAllMaps { valueUint8: Int?; valueUint42: Int?; valueUint256: Int?; + valueCoins: Int?; valueBool: Bool?; valueCell: Cell?; valueAddress: Address?; @@ -724,6 +775,7 @@ contract MapTestContract { int_uint8: map; int_uint42: map; int_uint256: map; + int_coins: map; int_bool: map; int_cell: map; int_address: map; @@ -740,6 +792,7 @@ contract MapTestContract { int8_uint8: map; int8_uint42: map; int8_uint256: map; + int8_coins: map; int8_bool: map; int8_cell: map; int8_address: map; @@ -756,6 +809,7 @@ contract MapTestContract { int42_uint8: map; int42_uint42: map; int42_uint256: map; + int42_coins: map; int42_bool: map; int42_cell: map; int42_address: map; @@ -772,6 +826,7 @@ contract MapTestContract { int256_uint8: map; int256_uint42: map; int256_uint256: map; + int256_coins: map; int256_bool: map; int256_cell: map; int256_address: map; @@ -788,6 +843,7 @@ contract MapTestContract { uint8_uint8: map; uint8_uint42: map; uint8_uint256: map; + uint8_coins: map; uint8_bool: map; uint8_cell: map; uint8_address: map; @@ -804,6 +860,7 @@ contract MapTestContract { uint42_uint8: map; uint42_uint42: map; uint42_uint256: map; + uint42_coins: map; uint42_bool: map; uint42_cell: map; uint42_address: map; @@ -820,6 +877,7 @@ contract MapTestContract { uint256_uint8: map; uint256_uint42: map; uint256_uint256: map; + uint256_coins: map; uint256_bool: map; uint256_cell: map; uint256_address: map; @@ -836,6 +894,7 @@ contract MapTestContract { address_uint8: map; address_uint42: map; address_uint256: map; + address_coins: map; address_bool: map; address_cell: map; address_address: map; @@ -854,6 +913,7 @@ contract MapTestContract { self.int_uint8.set(msg.keyInt, msg.valueUint8); self.int_uint42.set(msg.keyInt, msg.valueUint42); self.int_uint256.set(msg.keyInt, msg.valueUint256); + self.int_coins.set(msg.keyInt, msg.valueCoins); self.int_bool.set(msg.keyInt, msg.valueBool); self.int_cell.set(msg.keyInt, msg.valueCell); self.int_address.set(msg.keyInt, msg.valueAddress); @@ -866,6 +926,7 @@ contract MapTestContract { self.int8_uint8.set(msg.keyInt8, msg.valueUint8); self.int8_uint42.set(msg.keyInt8, msg.valueUint42); self.int8_uint256.set(msg.keyInt8, msg.valueUint256); + self.int8_coins.set(msg.keyInt8, msg.valueCoins); self.int8_bool.set(msg.keyInt8, msg.valueBool); self.int8_cell.set(msg.keyInt8, msg.valueCell); self.int8_address.set(msg.keyInt8, msg.valueAddress); @@ -878,6 +939,7 @@ contract MapTestContract { self.int42_uint8.set(msg.keyInt42, msg.valueUint8); self.int42_uint42.set(msg.keyInt42, msg.valueUint42); self.int42_uint256.set(msg.keyInt42, msg.valueUint256); + self.int42_coins.set(msg.keyInt42, msg.valueCoins); self.int42_bool.set(msg.keyInt42, msg.valueBool); self.int42_cell.set(msg.keyInt42, msg.valueCell); self.int42_address.set(msg.keyInt42, msg.valueAddress); @@ -890,6 +952,7 @@ contract MapTestContract { self.int256_uint8.set(msg.keyInt256, msg.valueUint8); self.int256_uint42.set(msg.keyInt256, msg.valueUint42); self.int256_uint256.set(msg.keyInt256, msg.valueUint256); + self.int256_coins.set(msg.keyInt256, msg.valueCoins); self.int256_bool.set(msg.keyInt256, msg.valueBool); self.int256_cell.set(msg.keyInt256, msg.valueCell); self.int256_address.set(msg.keyInt256, msg.valueAddress); @@ -902,6 +965,7 @@ contract MapTestContract { self.uint8_uint8.set(msg.keyUint8, msg.valueUint8); self.uint8_uint42.set(msg.keyUint8, msg.valueUint42); self.uint8_uint256.set(msg.keyUint8, msg.valueUint256); + self.uint8_coins.set(msg.keyUint8, msg.valueCoins); self.uint8_bool.set(msg.keyUint8, msg.valueBool); self.uint8_cell.set(msg.keyUint8, msg.valueCell); self.uint8_address.set(msg.keyUint8, msg.valueAddress); @@ -914,6 +978,7 @@ contract MapTestContract { self.uint42_uint8.set(msg.keyUint42, msg.valueUint8); self.uint42_uint42.set(msg.keyUint42, msg.valueUint42); self.uint42_uint256.set(msg.keyUint42, msg.valueUint256); + self.uint42_coins.set(msg.keyUint42, msg.valueCoins); self.uint42_bool.set(msg.keyUint42, msg.valueBool); self.uint42_cell.set(msg.keyUint42, msg.valueCell); self.uint42_address.set(msg.keyUint42, msg.valueAddress); @@ -926,6 +991,7 @@ contract MapTestContract { self.uint256_uint8.set(msg.keyUint256, msg.valueUint8); self.uint256_uint42.set(msg.keyUint256, msg.valueUint42); self.uint256_uint256.set(msg.keyUint256, msg.valueUint256); + self.uint256_coins.set(msg.keyUint256, msg.valueCoins); self.uint256_bool.set(msg.keyUint256, msg.valueBool); self.uint256_cell.set(msg.keyUint256, msg.valueCell); self.uint256_address.set(msg.keyUint256, msg.valueAddress); @@ -939,6 +1005,7 @@ contract MapTestContract { self.address_uint8.set(msg.keyAddress, msg.valueUint8); self.address_uint42.set(msg.keyAddress, msg.valueUint42); self.address_uint256.set(msg.keyAddress, msg.valueUint256); + self.address_coins.set(msg.keyAddress, msg.valueCoins); self.address_bool.set(msg.keyAddress, msg.valueBool); self.address_cell.set(msg.keyAddress, msg.valueCell); self.address_address.set(msg.keyAddress, msg.valueAddress); @@ -954,6 +1021,7 @@ contract MapTestContract { self.int_uint8.del(msg.keyInt); self.int_uint42.del(msg.keyInt); self.int_uint256.del(msg.keyInt); + self.int_coins.del(msg.keyInt); self.int_bool.del(msg.keyInt); self.int_cell.del(msg.keyInt); self.int_address.del(msg.keyInt); @@ -966,6 +1034,7 @@ contract MapTestContract { self.int8_uint8.del(msg.keyInt8); self.int8_uint42.del(msg.keyInt8); self.int8_uint256.del(msg.keyInt8); + self.int8_coins.del(msg.keyInt8); self.int8_bool.del(msg.keyInt8); self.int8_cell.del(msg.keyInt8); self.int8_address.del(msg.keyInt8); @@ -978,6 +1047,7 @@ contract MapTestContract { self.int42_uint8.del(msg.keyInt42); self.int42_uint42.del(msg.keyInt42); self.int42_uint256.del(msg.keyInt42); + self.int42_coins.del(msg.keyInt42); self.int42_bool.del(msg.keyInt42); self.int42_cell.del(msg.keyInt42); self.int42_address.del(msg.keyInt42); @@ -990,6 +1060,7 @@ contract MapTestContract { self.int256_uint8.del(msg.keyInt256); self.int256_uint42.del(msg.keyInt256); self.int256_uint256.del(msg.keyInt256); + self.int256_coins.del(msg.keyInt256); self.int256_bool.del(msg.keyInt256); self.int256_cell.del(msg.keyInt256); self.int256_address.del(msg.keyInt256); @@ -1002,6 +1073,7 @@ contract MapTestContract { self.uint8_uint8.del(msg.keyUint8); self.uint8_uint42.del(msg.keyUint8); self.uint8_uint256.del(msg.keyUint8); + self.uint8_coins.del(msg.keyUint8); self.uint8_bool.del(msg.keyUint8); self.uint8_cell.del(msg.keyUint8); self.uint8_address.del(msg.keyUint8); @@ -1014,6 +1086,7 @@ contract MapTestContract { self.uint42_uint8.del(msg.keyUint42); self.uint42_uint42.del(msg.keyUint42); self.uint42_uint256.del(msg.keyUint42); + self.uint42_coins.del(msg.keyUint42); self.uint42_bool.del(msg.keyUint42); self.uint42_cell.del(msg.keyUint42); self.uint42_address.del(msg.keyUint42); @@ -1026,6 +1099,7 @@ contract MapTestContract { self.uint256_uint8.del(msg.keyUint256); self.uint256_uint42.del(msg.keyUint256); self.uint256_uint256.del(msg.keyUint256); + self.uint256_coins.del(msg.keyUint256); self.uint256_bool.del(msg.keyUint256); self.uint256_cell.del(msg.keyUint256); self.uint256_address.del(msg.keyUint256); @@ -1039,6 +1113,7 @@ contract MapTestContract { self.address_uint8.del(msg.keyAddress); self.address_uint42.del(msg.keyAddress); self.address_uint256.del(msg.keyAddress); + self.address_coins.del(msg.keyAddress); self.address_bool.del(msg.keyAddress); self.address_cell.del(msg.keyAddress); self.address_address.del(msg.keyAddress); @@ -1054,6 +1129,7 @@ contract MapTestContract { self.int_uint8.replace(msg.keyInt, msg.valueUint8); self.int_uint42.replace(msg.keyInt, msg.valueUint42); self.int_uint256.replace(msg.keyInt, msg.valueUint256); + self.int_coins.replace(msg.keyInt, msg.valueCoins); self.int_bool.replace(msg.keyInt, msg.valueBool); self.int_cell.replace(msg.keyInt, msg.valueCell); self.int_address.replace(msg.keyInt, msg.valueAddress); @@ -1066,6 +1142,7 @@ contract MapTestContract { self.int8_uint8.replace(msg.keyInt8, msg.valueUint8); self.int8_uint42.replace(msg.keyInt8, msg.valueUint42); self.int8_uint256.replace(msg.keyInt8, msg.valueUint256); + self.int8_coins.replace(msg.keyInt8, msg.valueCoins); self.int8_bool.replace(msg.keyInt8, msg.valueBool); self.int8_cell.replace(msg.keyInt8, msg.valueCell); self.int8_address.replace(msg.keyInt8, msg.valueAddress); @@ -1078,6 +1155,7 @@ contract MapTestContract { self.int42_uint8.replace(msg.keyInt42, msg.valueUint8); self.int42_uint42.replace(msg.keyInt42, msg.valueUint42); self.int42_uint256.replace(msg.keyInt42, msg.valueUint256); + self.int42_coins.replace(msg.keyInt42, msg.valueCoins); self.int42_bool.replace(msg.keyInt42, msg.valueBool); self.int42_cell.replace(msg.keyInt42, msg.valueCell); self.int42_address.replace(msg.keyInt42, msg.valueAddress); @@ -1090,6 +1168,7 @@ contract MapTestContract { self.int256_uint8.replace(msg.keyInt256, msg.valueUint8); self.int256_uint42.replace(msg.keyInt256, msg.valueUint42); self.int256_uint256.replace(msg.keyInt256, msg.valueUint256); + self.int256_coins.replace(msg.keyInt256, msg.valueCoins); self.int256_bool.replace(msg.keyInt256, msg.valueBool); self.int256_cell.replace(msg.keyInt256, msg.valueCell); self.int256_address.replace(msg.keyInt256, msg.valueAddress); @@ -1102,6 +1181,7 @@ contract MapTestContract { self.uint8_uint8.replace(msg.keyUint8, msg.valueUint8); self.uint8_uint42.replace(msg.keyUint8, msg.valueUint42); self.uint8_uint256.replace(msg.keyUint8, msg.valueUint256); + self.uint8_coins.replace(msg.keyUint8, msg.valueCoins); self.uint8_bool.replace(msg.keyUint8, msg.valueBool); self.uint8_cell.replace(msg.keyUint8, msg.valueCell); self.uint8_address.replace(msg.keyUint8, msg.valueAddress); @@ -1114,6 +1194,7 @@ contract MapTestContract { self.uint42_uint8.replace(msg.keyUint42, msg.valueUint8); self.uint42_uint42.replace(msg.keyUint42, msg.valueUint42); self.uint42_uint256.replace(msg.keyUint42, msg.valueUint256); + self.uint42_coins.replace(msg.keyUint42, msg.valueCoins); self.uint42_bool.replace(msg.keyUint42, msg.valueBool); self.uint42_cell.replace(msg.keyUint42, msg.valueCell); self.uint42_address.replace(msg.keyUint42, msg.valueAddress); @@ -1126,6 +1207,7 @@ contract MapTestContract { self.uint256_uint8.replace(msg.keyUint256, msg.valueUint8); self.uint256_uint42.replace(msg.keyUint256, msg.valueUint42); self.uint256_uint256.replace(msg.keyUint256, msg.valueUint256); + self.uint256_coins.replace(msg.keyUint256, msg.valueCoins); self.uint256_bool.replace(msg.keyUint256, msg.valueBool); self.uint256_cell.replace(msg.keyUint256, msg.valueCell); self.uint256_address.replace(msg.keyUint256, msg.valueAddress); @@ -1139,6 +1221,7 @@ contract MapTestContract { self.address_uint8.replace(msg.keyAddress, msg.valueUint8); self.address_uint42.replace(msg.keyAddress, msg.valueUint42); self.address_uint256.replace(msg.keyAddress, msg.valueUint256); + self.address_coins.replace(msg.keyAddress, msg.valueCoins); self.address_bool.replace(msg.keyAddress, msg.valueBool); self.address_cell.replace(msg.keyAddress, msg.valueCell); self.address_address.replace(msg.keyAddress, msg.valueAddress); @@ -1154,6 +1237,7 @@ contract MapTestContract { self.int_uint8.replaceGet(msg.keyInt, msg.valueUint8); self.int_uint42.replaceGet(msg.keyInt, msg.valueUint42); self.int_uint256.replaceGet(msg.keyInt, msg.valueUint256); + self.int_coins.replaceGet(msg.keyInt, msg.valueCoins); self.int_bool.replaceGet(msg.keyInt, msg.valueBool); self.int_cell.replaceGet(msg.keyInt, msg.valueCell); self.int_address.replaceGet(msg.keyInt, msg.valueAddress); @@ -1166,6 +1250,7 @@ contract MapTestContract { self.int8_uint8.replaceGet(msg.keyInt8, msg.valueUint8); self.int8_uint42.replaceGet(msg.keyInt8, msg.valueUint42); self.int8_uint256.replaceGet(msg.keyInt8, msg.valueUint256); + self.int8_coins.replaceGet(msg.keyInt8, msg.valueCoins); self.int8_bool.replaceGet(msg.keyInt8, msg.valueBool); self.int8_cell.replaceGet(msg.keyInt8, msg.valueCell); self.int8_address.replaceGet(msg.keyInt8, msg.valueAddress); @@ -1178,6 +1263,7 @@ contract MapTestContract { self.int42_uint8.replaceGet(msg.keyInt42, msg.valueUint8); self.int42_uint42.replaceGet(msg.keyInt42, msg.valueUint42); self.int42_uint256.replaceGet(msg.keyInt42, msg.valueUint256); + self.int42_coins.replaceGet(msg.keyInt42, msg.valueCoins); self.int42_bool.replaceGet(msg.keyInt42, msg.valueBool); self.int42_cell.replaceGet(msg.keyInt42, msg.valueCell); self.int42_address.replaceGet(msg.keyInt42, msg.valueAddress); @@ -1190,6 +1276,7 @@ contract MapTestContract { self.int256_uint8.replaceGet(msg.keyInt256, msg.valueUint8); self.int256_uint42.replaceGet(msg.keyInt256, msg.valueUint42); self.int256_uint256.replaceGet(msg.keyInt256, msg.valueUint256); + self.int256_coins.replaceGet(msg.keyInt256, msg.valueCoins); self.int256_bool.replaceGet(msg.keyInt256, msg.valueBool); self.int256_cell.replaceGet(msg.keyInt256, msg.valueCell); self.int256_address.replaceGet(msg.keyInt256, msg.valueAddress); @@ -1202,6 +1289,7 @@ contract MapTestContract { self.uint8_uint8.replaceGet(msg.keyUint8, msg.valueUint8); self.uint8_uint42.replaceGet(msg.keyUint8, msg.valueUint42); self.uint8_uint256.replaceGet(msg.keyUint8, msg.valueUint256); + self.uint8_coins.replaceGet(msg.keyUint8, msg.valueCoins); self.uint8_bool.replaceGet(msg.keyUint8, msg.valueBool); self.uint8_cell.replaceGet(msg.keyUint8, msg.valueCell); self.uint8_address.replaceGet(msg.keyUint8, msg.valueAddress); @@ -1214,6 +1302,7 @@ contract MapTestContract { self.uint42_uint8.replaceGet(msg.keyUint42, msg.valueUint8); self.uint42_uint42.replaceGet(msg.keyUint42, msg.valueUint42); self.uint42_uint256.replaceGet(msg.keyUint42, msg.valueUint256); + self.uint42_coins.replaceGet(msg.keyUint42, msg.valueCoins); self.uint42_bool.replaceGet(msg.keyUint42, msg.valueBool); self.uint42_cell.replaceGet(msg.keyUint42, msg.valueCell); self.uint42_address.replaceGet(msg.keyUint42, msg.valueAddress); @@ -1226,6 +1315,7 @@ contract MapTestContract { self.uint256_uint8.replaceGet(msg.keyUint256, msg.valueUint8); self.uint256_uint42.replaceGet(msg.keyUint256, msg.valueUint42); self.uint256_uint256.replaceGet(msg.keyUint256, msg.valueUint256); + self.uint256_coins.replaceGet(msg.keyUint256, msg.valueCoins); self.uint256_bool.replaceGet(msg.keyUint256, msg.valueBool); self.uint256_cell.replaceGet(msg.keyUint256, msg.valueCell); self.uint256_address.replaceGet(msg.keyUint256, msg.valueAddress); @@ -1239,6 +1329,7 @@ contract MapTestContract { self.address_uint8.replaceGet(msg.keyAddress, msg.valueUint8); self.address_uint42.replaceGet(msg.keyAddress, msg.valueUint42); self.address_uint256.replaceGet(msg.keyAddress, msg.valueUint256); + self.address_coins.replaceGet(msg.keyAddress, msg.valueCoins); self.address_bool.replaceGet(msg.keyAddress, msg.valueBool); self.address_cell.replaceGet(msg.keyAddress, msg.valueCell); self.address_address.replaceGet(msg.keyAddress, msg.valueAddress); @@ -1272,6 +1363,7 @@ contract MapTestContract { int_uint8: self.int_uint8.get(keyInt), int_uint42: self.int_uint42.get(keyInt), int_uint256: self.int_uint256.get(keyInt), + int_coins: self.int_coins.get(keyInt), int_bool: self.int_bool.get(keyInt), int_cell: self.int_cell.get(keyInt), int_address: self.int_address.get(keyInt), @@ -1284,6 +1376,7 @@ contract MapTestContract { int8_uint8: self.int8_uint8.get(keyInt8), int8_uint42: self.int8_uint42.get(keyInt8), int8_uint256: self.int8_uint256.get(keyInt8), + int8_coins: self.int8_coins.get(keyInt8), int8_bool: self.int8_bool.get(keyInt8), int8_cell: self.int8_cell.get(keyInt8), int8_address: self.int8_address.get(keyInt8), @@ -1296,6 +1389,7 @@ contract MapTestContract { int42_uint8: self.int42_uint8.get(keyInt42), int42_uint42: self.int42_uint42.get(keyInt42), int42_uint256: self.int42_uint256.get(keyInt42), + int42_coins: self.int42_coins.get(keyInt42), int42_bool: self.int42_bool.get(keyInt42), int42_cell: self.int42_cell.get(keyInt42), int42_address: self.int42_address.get(keyInt42), @@ -1308,6 +1402,7 @@ contract MapTestContract { int256_uint8: self.int256_uint8.get(keyInt256), int256_uint42: self.int256_uint42.get(keyInt256), int256_uint256: self.int256_uint256.get(keyInt256), + int256_coins: self.int256_coins.get(keyInt256), int256_bool: self.int256_bool.get(keyInt256), int256_cell: self.int256_cell.get(keyInt256), int256_address: self.int256_address.get(keyInt256), @@ -1320,6 +1415,7 @@ contract MapTestContract { uint8_uint8: self.uint8_uint8.get(keyUint8), uint8_uint42: self.uint8_uint42.get(keyUint8), uint8_uint256: self.uint8_uint256.get(keyUint8), + uint8_coins: self.uint8_coins.get(keyUint8), uint8_bool: self.uint8_bool.get(keyUint8), uint8_cell: self.uint8_cell.get(keyUint8), uint8_address: self.uint8_address.get(keyUint8), @@ -1332,6 +1428,7 @@ contract MapTestContract { uint42_uint8: self.uint42_uint8.get(keyUint42), uint42_uint42: self.uint42_uint42.get(keyUint42), uint42_uint256: self.uint42_uint256.get(keyUint42), + uint42_coins: self.uint42_coins.get(keyUint42), uint42_bool: self.uint42_bool.get(keyUint42), uint42_cell: self.uint42_cell.get(keyUint42), uint42_address: self.uint42_address.get(keyUint42), @@ -1344,6 +1441,7 @@ contract MapTestContract { uint256_uint8: self.uint256_uint8.get(keyUint256), uint256_uint42: self.uint256_uint42.get(keyUint256), uint256_uint256: self.uint256_uint256.get(keyUint256), + uint256_coins: self.uint256_coins.get(keyUint256), uint256_bool: self.uint256_bool.get(keyUint256), uint256_cell: self.uint256_cell.get(keyUint256), uint256_address: self.uint256_address.get(keyUint256), @@ -1357,6 +1455,7 @@ contract MapTestContract { address_uint8: self.address_uint8.get(keyAddress), address_uint42: self.address_uint42.get(keyAddress), address_uint256: self.address_uint256.get(keyAddress), + address_coins: self.address_coins.get(keyAddress), address_bool: self.address_bool.get(keyAddress), address_cell: self.address_cell.get(keyAddress), address_address: self.address_address.get(keyAddress), @@ -1380,6 +1479,7 @@ contract MapTestContract { valueUint8: Int, valueUint42: Int, valueUint256: Int, + valueCoins: Int, valueBool: Bool, valueCell: Cell, valueAddress: Address, @@ -1394,6 +1494,7 @@ contract MapTestContract { int_uint8: self.int_uint8.replace(keyInt, valueUint8), int_uint42: self.int_uint42.replace(keyInt, valueUint42), int_uint256: self.int_uint256.replace(keyInt, valueUint256), + int_coins: self.int_coins.replace(keyInt, valueCoins), int_bool: self.int_bool.replace(keyInt, valueBool), int_cell: self.int_cell.replace(keyInt, valueCell), int_address: self.int_address.replace(keyInt, valueAddress), @@ -1406,6 +1507,7 @@ contract MapTestContract { int8_uint8: self.int8_uint8.replace(keyInt8, valueUint8), int8_uint42: self.int8_uint42.replace(keyInt8, valueUint42), int8_uint256: self.int8_uint256.replace(keyInt8, valueUint256), + int8_coins: self.int8_coins.replace(keyInt8, valueCoins), int8_bool: self.int8_bool.replace(keyInt8, valueBool), int8_cell: self.int8_cell.replace(keyInt8, valueCell), int8_address: self.int8_address.replace(keyInt8, valueAddress), @@ -1418,6 +1520,7 @@ contract MapTestContract { int42_uint8: self.int42_uint8.replace(keyInt42, valueUint8), int42_uint42: self.int42_uint42.replace(keyInt42, valueUint42), int42_uint256: self.int42_uint256.replace(keyInt42, valueUint256), + int42_coins: self.int42_coins.replace(keyInt42, valueCoins), int42_bool: self.int42_bool.replace(keyInt42, valueBool), int42_cell: self.int42_cell.replace(keyInt42, valueCell), int42_address: self.int42_address.replace(keyInt42, valueAddress), @@ -1430,6 +1533,7 @@ contract MapTestContract { int256_uint8: self.int256_uint8.replace(keyInt256, valueUint8), int256_uint42: self.int256_uint42.replace(keyInt256, valueUint42), int256_uint256: self.int256_uint256.replace(keyInt256, valueUint256), + int256_coins: self.int256_coins.replace(keyInt256, valueCoins), int256_bool: self.int256_bool.replace(keyInt256, valueBool), int256_cell: self.int256_cell.replace(keyInt256, valueCell), int256_address: self.int256_address.replace(keyInt256, valueAddress), @@ -1442,6 +1546,7 @@ contract MapTestContract { uint8_uint8: self.uint8_uint8.replace(keyUint8, valueUint8), uint8_uint42: self.uint8_uint42.replace(keyUint8, valueUint42), uint8_uint256: self.uint8_uint256.replace(keyUint8, valueUint256), + uint8_coins: self.uint8_coins.replace(keyUint8, valueCoins), uint8_bool: self.uint8_bool.replace(keyUint8, valueBool), uint8_cell: self.uint8_cell.replace(keyUint8, valueCell), uint8_address: self.uint8_address.replace(keyUint8, valueAddress), @@ -1454,6 +1559,7 @@ contract MapTestContract { uint42_uint8: self.uint42_uint8.replace(keyUint42, valueUint8), uint42_uint42: self.uint42_uint42.replace(keyUint42, valueUint42), uint42_uint256: self.uint42_uint256.replace(keyUint42, valueUint256), + uint42_coins: self.uint42_coins.replace(keyUint42, valueCoins), uint42_bool: self.uint42_bool.replace(keyUint42, valueBool), uint42_cell: self.uint42_cell.replace(keyUint42, valueCell), uint42_address: self.uint42_address.replace(keyUint42, valueAddress), @@ -1466,6 +1572,7 @@ contract MapTestContract { uint256_uint8: self.uint256_uint8.replace(keyUint256, valueUint8), uint256_uint42: self.uint256_uint42.replace(keyUint256, valueUint42), uint256_uint256: self.uint256_uint256.replace(keyUint256, valueUint256), + uint256_coins: self.uint256_coins.replace(keyUint256, valueCoins), uint256_bool: self.uint256_bool.replace(keyUint256, valueBool), uint256_cell: self.uint256_cell.replace(keyUint256, valueCell), uint256_address: self.uint256_address.replace(keyUint256, valueAddress), @@ -1479,6 +1586,7 @@ contract MapTestContract { address_uint8: self.address_uint8.replace(keyAddress, valueUint8), address_uint42: self.address_uint42.replace(keyAddress, valueUint42), address_uint256: self.address_uint256.replace(keyAddress, valueUint256), + address_coins: self.address_coins.replace(keyAddress, valueCoins), address_bool: self.address_bool.replace(keyAddress, valueBool), address_cell: self.address_cell.replace(keyAddress, valueCell), address_address: self.address_address.replace(keyAddress, valueAddress), @@ -1502,6 +1610,7 @@ contract MapTestContract { valueUint8: Int, valueUint42: Int, valueUint256: Int, + valueCoins: Int, valueBool: Bool, valueCell: Cell, valueAddress: Address, @@ -1516,6 +1625,7 @@ contract MapTestContract { int_uint8: self.int_uint8.replaceGet(keyInt, valueUint8), int_uint42: self.int_uint42.replaceGet(keyInt, valueUint42), int_uint256: self.int_uint256.replaceGet(keyInt, valueUint256), + int_coins: self.int_coins.replaceGet(keyInt, valueCoins), int_bool: self.int_bool.replaceGet(keyInt, valueBool), int_cell: self.int_cell.replaceGet(keyInt, valueCell), int_address: self.int_address.replaceGet(keyInt, valueAddress), @@ -1528,6 +1638,7 @@ contract MapTestContract { int8_uint8: self.int8_uint8.replaceGet(keyInt8, valueUint8), int8_uint42: self.int8_uint42.replaceGet(keyInt8, valueUint42), int8_uint256: self.int8_uint256.replaceGet(keyInt8, valueUint256), + int8_coins: self.int8_coins.replaceGet(keyInt8, valueCoins), int8_bool: self.int8_bool.replaceGet(keyInt8, valueBool), int8_cell: self.int8_cell.replaceGet(keyInt8, valueCell), int8_address: self.int8_address.replaceGet(keyInt8, valueAddress), @@ -1540,6 +1651,7 @@ contract MapTestContract { int42_uint8: self.int42_uint8.replaceGet(keyInt42, valueUint8), int42_uint42: self.int42_uint42.replaceGet(keyInt42, valueUint42), int42_uint256: self.int42_uint256.replaceGet(keyInt42, valueUint256), + int42_coins: self.int42_coins.replaceGet(keyInt42, valueCoins), int42_bool: self.int42_bool.replaceGet(keyInt42, valueBool), int42_cell: self.int42_cell.replaceGet(keyInt42, valueCell), int42_address: self.int42_address.replaceGet(keyInt42, valueAddress), @@ -1552,6 +1664,7 @@ contract MapTestContract { int256_uint8: self.int256_uint8.replaceGet(keyInt256, valueUint8), int256_uint42: self.int256_uint42.replaceGet(keyInt256, valueUint42), int256_uint256: self.int256_uint256.replaceGet(keyInt256, valueUint256), + int256_coins: self.int256_coins.replaceGet(keyInt256, valueCoins), int256_bool: self.int256_bool.replaceGet(keyInt256, valueBool), int256_cell: self.int256_cell.replaceGet(keyInt256, valueCell), int256_address: self.int256_address.replaceGet(keyInt256, valueAddress), @@ -1564,6 +1677,7 @@ contract MapTestContract { uint8_uint8: self.uint8_uint8.replaceGet(keyUint8, valueUint8), uint8_uint42: self.uint8_uint42.replaceGet(keyUint8, valueUint42), uint8_uint256: self.uint8_uint256.replaceGet(keyUint8, valueUint256), + uint8_coins: self.uint8_coins.replaceGet(keyUint8, valueCoins), uint8_bool: self.uint8_bool.replaceGet(keyUint8, valueBool), uint8_cell: self.uint8_cell.replaceGet(keyUint8, valueCell), uint8_address: self.uint8_address.replaceGet(keyUint8, valueAddress), @@ -1576,6 +1690,7 @@ contract MapTestContract { uint42_uint8: self.uint42_uint8.replaceGet(keyUint42, valueUint8), uint42_uint42: self.uint42_uint42.replaceGet(keyUint42, valueUint42), uint42_uint256: self.uint42_uint256.replaceGet(keyUint42, valueUint256), + uint42_coins: self.uint42_coins.replaceGet(keyUint42, valueCoins), uint42_bool: self.uint42_bool.replaceGet(keyUint42, valueBool), uint42_cell: self.uint42_cell.replaceGet(keyUint42, valueCell), uint42_address: self.uint42_address.replaceGet(keyUint42, valueAddress), @@ -1588,6 +1703,7 @@ contract MapTestContract { uint256_uint8: self.uint256_uint8.replaceGet(keyUint256, valueUint8), uint256_uint42: self.uint256_uint42.replaceGet(keyUint256, valueUint42), uint256_uint256: self.uint256_uint256.replaceGet(keyUint256, valueUint256), + uint256_coins: self.uint256_coins.replaceGet(keyUint256, valueCoins), uint256_bool: self.uint256_bool.replaceGet(keyUint256, valueBool), uint256_cell: self.uint256_cell.replaceGet(keyUint256, valueCell), uint256_address: self.uint256_address.replaceGet(keyUint256, valueAddress), @@ -1601,6 +1717,7 @@ contract MapTestContract { address_uint8: self.address_uint8.replaceGet(keyAddress, valueUint8), address_uint42: self.address_uint42.replaceGet(keyAddress, valueUint42), address_uint256: self.address_uint256.replaceGet(keyAddress, valueUint256), + address_coins: self.address_coins.replaceGet(keyAddress, valueCoins), address_bool: self.address_bool.replaceGet(keyAddress, valueBool), address_cell: self.address_cell.replaceGet(keyAddress, valueCell), address_address: self.address_address.replaceGet(keyAddress, valueAddress), @@ -1627,6 +1744,7 @@ contract MapTestContract { int_uint8: self.int_uint8.exists(keyInt), int_uint42: self.int_uint42.exists(keyInt), int_uint256: self.int_uint256.exists(keyInt), + int_coins: self.int_coins.exists(keyInt), int_bool: self.int_bool.exists(keyInt), int_cell: self.int_cell.exists(keyInt), int_address: self.int_address.exists(keyInt), @@ -1639,6 +1757,7 @@ contract MapTestContract { int8_uint8: self.int8_uint8.exists(keyInt8), int8_uint42: self.int8_uint42.exists(keyInt8), int8_uint256: self.int8_uint256.exists(keyInt8), + int8_coins: self.int8_coins.exists(keyInt8), int8_bool: self.int8_bool.exists(keyInt8), int8_cell: self.int8_cell.exists(keyInt8), int8_address: self.int8_address.exists(keyInt8), @@ -1651,6 +1770,7 @@ contract MapTestContract { int42_uint8: self.int42_uint8.exists(keyInt42), int42_uint42: self.int42_uint42.exists(keyInt42), int42_uint256: self.int42_uint256.exists(keyInt42), + int42_coins: self.int42_coins.exists(keyInt42), int42_bool: self.int42_bool.exists(keyInt42), int42_cell: self.int42_cell.exists(keyInt42), int42_address: self.int42_address.exists(keyInt42), @@ -1663,6 +1783,7 @@ contract MapTestContract { int256_uint8: self.int256_uint8.exists(keyInt256), int256_uint42: self.int256_uint42.exists(keyInt256), int256_uint256: self.int256_uint256.exists(keyInt256), + int256_coins: self.int256_coins.exists(keyInt256), int256_bool: self.int256_bool.exists(keyInt256), int256_cell: self.int256_cell.exists(keyInt256), int256_address: self.int256_address.exists(keyInt256), @@ -1675,6 +1796,7 @@ contract MapTestContract { uint8_uint8: self.uint8_uint8.exists(keyUint8), uint8_uint42: self.uint8_uint42.exists(keyUint8), uint8_uint256: self.uint8_uint256.exists(keyUint8), + uint8_coins: self.uint8_coins.exists(keyUint8), uint8_bool: self.uint8_bool.exists(keyUint8), uint8_cell: self.uint8_cell.exists(keyUint8), uint8_address: self.uint8_address.exists(keyUint8), @@ -1687,6 +1809,7 @@ contract MapTestContract { uint42_uint8: self.uint42_uint8.exists(keyUint42), uint42_uint42: self.uint42_uint42.exists(keyUint42), uint42_uint256: self.uint42_uint256.exists(keyUint42), + uint42_coins: self.uint42_coins.exists(keyUint42), uint42_bool: self.uint42_bool.exists(keyUint42), uint42_cell: self.uint42_cell.exists(keyUint42), uint42_address: self.uint42_address.exists(keyUint42), @@ -1699,6 +1822,7 @@ contract MapTestContract { uint256_uint8: self.uint256_uint8.exists(keyUint256), uint256_uint42: self.uint256_uint42.exists(keyUint256), uint256_uint256: self.uint256_uint256.exists(keyUint256), + uint256_coins: self.uint256_coins.exists(keyUint256), uint256_bool: self.uint256_bool.exists(keyUint256), uint256_cell: self.uint256_cell.exists(keyUint256), uint256_address: self.uint256_address.exists(keyUint256), @@ -1712,6 +1836,7 @@ contract MapTestContract { address_uint8: self.address_uint8.exists(keyAddress), address_uint42: self.address_uint42.exists(keyAddress), address_uint256: self.address_uint256.exists(keyAddress), + address_coins: self.address_coins.exists(keyAddress), address_bool: self.address_bool.exists(keyAddress), address_cell: self.address_cell.exists(keyAddress), address_address: self.address_address.exists(keyAddress), @@ -1729,6 +1854,7 @@ contract MapTestContract { int_uint8: self.int_uint8.isEmpty(), int_uint42: self.int_uint42.isEmpty(), int_uint256: self.int_uint256.isEmpty(), + int_coins: self.int_coins.isEmpty(), int_bool: self.int_bool.isEmpty(), int_cell: self.int_cell.isEmpty(), int_address: self.int_address.isEmpty(), @@ -1741,6 +1867,7 @@ contract MapTestContract { int8_uint8: self.int8_uint8.isEmpty(), int8_uint42: self.int8_uint42.isEmpty(), int8_uint256: self.int8_uint256.isEmpty(), + int8_coins: self.int8_coins.isEmpty(), int8_bool: self.int8_bool.isEmpty(), int8_cell: self.int8_cell.isEmpty(), int8_address: self.int8_address.isEmpty(), @@ -1753,6 +1880,7 @@ contract MapTestContract { int42_uint8: self.int42_uint8.isEmpty(), int42_uint42: self.int42_uint42.isEmpty(), int42_uint256: self.int42_uint256.isEmpty(), + int42_coins: self.int42_coins.isEmpty(), int42_bool: self.int42_bool.isEmpty(), int42_cell: self.int42_cell.isEmpty(), int42_address: self.int42_address.isEmpty(), @@ -1765,6 +1893,7 @@ contract MapTestContract { int256_uint8: self.int256_uint8.isEmpty(), int256_uint42: self.int256_uint42.isEmpty(), int256_uint256: self.int256_uint256.isEmpty(), + int256_coins: self.int256_coins.isEmpty(), int256_bool: self.int256_bool.isEmpty(), int256_cell: self.int256_cell.isEmpty(), int256_address: self.int256_address.isEmpty(), @@ -1777,6 +1906,7 @@ contract MapTestContract { uint8_uint8: self.uint8_uint8.isEmpty(), uint8_uint42: self.uint8_uint42.isEmpty(), uint8_uint256: self.uint8_uint256.isEmpty(), + uint8_coins: self.uint8_coins.isEmpty(), uint8_bool: self.uint8_bool.isEmpty(), uint8_cell: self.uint8_cell.isEmpty(), uint8_address: self.uint8_address.isEmpty(), @@ -1789,6 +1919,7 @@ contract MapTestContract { uint42_uint8: self.uint42_uint8.isEmpty(), uint42_uint42: self.uint42_uint42.isEmpty(), uint42_uint256: self.uint42_uint256.isEmpty(), + uint42_coins: self.uint42_coins.isEmpty(), uint42_bool: self.uint42_bool.isEmpty(), uint42_cell: self.uint42_cell.isEmpty(), uint42_address: self.uint42_address.isEmpty(), @@ -1801,6 +1932,7 @@ contract MapTestContract { uint256_uint8: self.uint256_uint8.isEmpty(), uint256_uint42: self.uint256_uint42.isEmpty(), uint256_uint256: self.uint256_uint256.isEmpty(), + uint256_coins: self.uint256_coins.isEmpty(), uint256_bool: self.uint256_bool.isEmpty(), uint256_cell: self.uint256_cell.isEmpty(), uint256_address: self.uint256_address.isEmpty(), @@ -1814,6 +1946,7 @@ contract MapTestContract { address_uint8: self.address_uint8.isEmpty(), address_uint42: self.address_uint42.isEmpty(), address_uint256: self.address_uint256.isEmpty(), + address_coins: self.address_coins.isEmpty(), address_bool: self.address_bool.isEmpty(), address_cell: self.address_cell.isEmpty(), address_address: self.address_address.isEmpty(), @@ -1831,6 +1964,7 @@ contract MapTestContract { int_uint8: self.int_uint8.asCell(), int_uint42: self.int_uint42.asCell(), int_uint256: self.int_uint256.asCell(), + int_coins: self.int_coins.asCell(), int_bool: self.int_bool.asCell(), int_cell: self.int_cell.asCell(), int_address: self.int_address.asCell(), @@ -1843,6 +1977,7 @@ contract MapTestContract { int8_uint8: self.int8_uint8.asCell(), int8_uint42: self.int8_uint42.asCell(), int8_uint256: self.int8_uint256.asCell(), + int8_coins: self.int8_coins.asCell(), int8_bool: self.int8_bool.asCell(), int8_cell: self.int8_cell.asCell(), int8_address: self.int8_address.asCell(), @@ -1855,6 +1990,7 @@ contract MapTestContract { int42_uint8: self.int42_uint8.asCell(), int42_uint42: self.int42_uint42.asCell(), int42_uint256: self.int42_uint256.asCell(), + int42_coins: self.int42_coins.asCell(), int42_bool: self.int42_bool.asCell(), int42_cell: self.int42_cell.asCell(), int42_address: self.int42_address.asCell(), @@ -1867,6 +2003,7 @@ contract MapTestContract { int256_uint8: self.int256_uint8.asCell(), int256_uint42: self.int256_uint42.asCell(), int256_uint256: self.int256_uint256.asCell(), + int256_coins: self.int256_coins.asCell(), int256_bool: self.int256_bool.asCell(), int256_cell: self.int256_cell.asCell(), int256_address: self.int256_address.asCell(), @@ -1879,6 +2016,7 @@ contract MapTestContract { uint8_uint8: self.uint8_uint8.asCell(), uint8_uint42: self.uint8_uint42.asCell(), uint8_uint256: self.uint8_uint256.asCell(), + uint8_coins: self.uint8_coins.asCell(), uint8_bool: self.uint8_bool.asCell(), uint8_cell: self.uint8_cell.asCell(), uint8_address: self.uint8_address.asCell(), @@ -1891,6 +2029,7 @@ contract MapTestContract { uint42_uint8: self.uint42_uint8.asCell(), uint42_uint42: self.uint42_uint42.asCell(), uint42_uint256: self.uint42_uint256.asCell(), + uint42_coins: self.uint42_coins.asCell(), uint42_bool: self.uint42_bool.asCell(), uint42_cell: self.uint42_cell.asCell(), uint42_address: self.uint42_address.asCell(), @@ -1903,6 +2042,7 @@ contract MapTestContract { uint256_uint8: self.uint256_uint8.asCell(), uint256_uint42: self.uint256_uint42.asCell(), uint256_uint256: self.uint256_uint256.asCell(), + uint256_coins: self.uint256_coins.asCell(), uint256_bool: self.uint256_bool.asCell(), uint256_cell: self.uint256_cell.asCell(), uint256_address: self.uint256_address.asCell(), @@ -1916,6 +2056,7 @@ contract MapTestContract { address_uint8: self.address_uint8.asCell(), address_uint42: self.address_uint42.asCell(), address_uint256: self.address_uint256.asCell(), + address_coins: self.address_coins.asCell(), address_bool: self.address_bool.asCell(), address_cell: self.address_cell.asCell(), address_address: self.address_address.asCell(), diff --git a/src/test/e2e-emulated/map.spec.ts b/src/test/e2e-emulated/map.spec.ts index def08f733..245196f1e 100644 --- a/src/test/e2e-emulated/map.spec.ts +++ b/src/test/e2e-emulated/map.spec.ts @@ -65,6 +65,7 @@ type TestValues = { valueUint8: bigint; valueUint42: bigint; valueUint256: bigint; + valueCoins: bigint; valueBool: boolean; valueCell: Cell; valueAddress: Address; @@ -105,6 +106,7 @@ const testCases: TestCase[] = [ valueUint8: 255n, valueUint42: 123_456_789n, valueUint256: 999_999_999_999n, + valueCoins: 100_000_000n, valueBool: true, valueCell: beginCell().storeUint(42, 32).endCell(), valueAddress: randomAddress(0, "address"), @@ -137,6 +139,7 @@ const testCases: TestCase[] = [ valueUint8: 0n, // Min unsigned int valueUint42: 0n, // Min unsigned int valueUint256: 0n, // Min unsigned int + valueCoins: 0n, valueBool: false, valueCell: beginCell() .storeUint(2n ** 32n - 1n, 32) @@ -171,6 +174,7 @@ const testCases: TestCase[] = [ valueUint8: 1n, valueUint42: 1n, valueUint256: 1n, + valueCoins: 1n, valueBool: false, valueCell: beginCell().storeUint(0, 32).endCell(), valueAddress: randomAddress(0, "address"), @@ -203,6 +207,7 @@ const testCases: TestCase[] = [ valueUint8: 128n, // Middle value valueUint42: 2n ** 41n, // Large power of 2 valueUint256: 2n ** 255n, // Large power of 2 + valueCoins: 2n ** 120n - 1n, valueBool: true, valueCell: beginCell() .storeUint(2n ** 31n, 32) @@ -591,6 +596,7 @@ describe("MapTestContract", () => { valueUint8: null, valueUint42: null, valueUint256: null, + valueCoins: null, valueBool: null, valueCell: null, valueAddress: null, @@ -680,6 +686,7 @@ describe("MapTestContract", () => { valueUint8: null, valueUint42: null, valueUint256: null, + valueCoins: null, valueBool: null, valueCell: null, valueAddress: null, @@ -767,6 +774,7 @@ describe("MapTestContract", () => { valueUint8: null, valueUint42: null, valueUint256: null, + valueCoins: null, valueBool: null, valueCell: null, valueAddress: null, @@ -862,6 +870,7 @@ describe("MapTestContract", () => { valueUint8: null, valueUint42: null, valueUint256: null, + valueCoins: null, valueBool: null, valueCell: null, valueAddress: null, @@ -1504,6 +1513,7 @@ describe("MapTestContract", () => { valueUint8: null, valueUint42: null, valueUint256: null, + valueCoins: null, valueBool: null, valueCell: null, valueAddress: null, @@ -1578,6 +1588,7 @@ describe("MapTestContract", () => { valueUint8: null, valueUint42: null, valueUint256: null, + valueCoins: null, valueBool: null, valueCell: null, valueAddress: null, @@ -1672,6 +1683,7 @@ describe("MapTestContract", () => { valueUint8: null, valueUint42: null, valueUint256: null, + valueCoins: null, valueBool: null, valueCell: null, valueAddress: null, @@ -1764,6 +1776,7 @@ describe("MapTestContract", () => { valueUint8: null, valueUint42: null, valueUint256: null, + valueCoins: null, valueBool: null, valueCell: null, valueAddress: null, @@ -1830,6 +1843,7 @@ describe("MapTestContract", () => { values.valueUint8, values.valueUint42, values.valueUint256, + values.valueCoins, values.valueBool, values.valueCell, values.valueAddress, @@ -1873,6 +1887,7 @@ describe("MapTestContract", () => { values.valueUint8, values.valueUint42, values.valueUint256, + values.valueCoins, values.valueBool, values.valueCell, values.valueAddress, @@ -1966,6 +1981,7 @@ describe("MapTestContract", () => { valueUint8: null, valueUint42: null, valueUint256: null, + valueCoins: null, valueBool: null, valueCell: null, valueAddress: null, @@ -2032,6 +2048,7 @@ describe("MapTestContract", () => { values.valueUint8, values.valueUint42, values.valueUint256, + values.valueCoins, values.valueBool, values.valueCell, values.valueAddress, @@ -2076,6 +2093,7 @@ describe("MapTestContract", () => { values.valueUint8, values.valueUint42, values.valueUint256, + values.valueCoins, values.valueBool, values.valueCell, values.valueAddress, From 85416ae36de087159457e14cc7e16507a56fb52d Mon Sep 17 00:00:00 2001 From: Anton Trunov Date: Wed, 6 Nov 2024 12:46:09 +0400 Subject: [PATCH 05/24] fix: type-checking for `foreach` loops in trait methods (#1017) --- CHANGELOG.md | 1 + src/grammar/clone.ts | 1 + .../resolveDescriptors.spec.ts.snap | 879 ++++++++++++++++++ src/types/resolveExpression.ts | 6 +- src/types/test/trait-foreach.tact | 13 + 5 files changed, 899 insertions(+), 1 deletion(-) create mode 100644 src/types/test/trait-foreach.tact diff --git a/CHANGELOG.md b/CHANGELOG.md index 86ea8216a..9e1f45c7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Docs: correctly handle next and previous page links at the bottom of the pages when there's a separator item in the sidebar: PR [#949](https://github.com/tact-lang/tact/pull/949) - Docs: compilation of examples in `data-structures.mdx` and across Cookbook: PR [#917](https://github.com/tact-lang/tact/pull/917) - `as coins` map value serialization type is now handled correctly: PR [#987](https://github.com/tact-lang/tact/pull/987) +- Type checking for `foreach` loops in trait methods: PR [#1017](https://github.com/tact-lang/tact/pull/1017) ### Release contributors diff --git a/src/grammar/clone.ts b/src/grammar/clone.ts index 693f40972..a63a8f25d 100644 --- a/src/grammar/clone.ts +++ b/src/grammar/clone.ts @@ -126,6 +126,7 @@ export function cloneNode(src: T): T { } else if (src.kind === "statement_foreach") { return cloneAstNode({ ...src, + map: cloneNode(src.map), statements: src.statements.map(cloneNode), }); } else if (src.kind === "function_def") { diff --git a/src/types/__snapshots__/resolveDescriptors.spec.ts.snap b/src/types/__snapshots__/resolveDescriptors.spec.ts.snap index edb29b44e..e4173d1ff 100644 --- a/src/types/__snapshots__/resolveDescriptors.spec.ts.snap +++ b/src/types/__snapshots__/resolveDescriptors.spec.ts.snap @@ -10698,3 +10698,882 @@ exports[`resolveDescriptors should resolve descriptors for trait-base 1`] = ` `; exports[`resolveDescriptors should resolve descriptors for trait-base 2`] = `[]`; + +exports[`resolveDescriptors should resolve descriptors for trait-foreach 1`] = ` +[ + { + "ast": { + "id": 2, + "kind": "primitive_type_decl", + "loc": primitive Int;, + "name": { + "id": 1, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + }, + "constants": [], + "dependsOn": [], + "fields": [], + "functions": Map {}, + "header": null, + "init": null, + "interfaces": [], + "kind": "primitive_type_decl", + "name": "Int", + "origin": "user", + "partialFieldCount": 0, + "receivers": [], + "signature": null, + "tlb": null, + "traits": [], + "uid": 38154, + }, + { + "ast": { + "attributes": [], + "declarations": [], + "id": 4, + "kind": "trait", + "loc": trait BaseTrait { }, + "name": { + "id": 3, + "kind": "id", + "loc": BaseTrait, + "text": "BaseTrait", + }, + "traits": [], + }, + "constants": [], + "dependsOn": [], + "fields": [], + "functions": Map {}, + "header": null, + "init": null, + "interfaces": [], + "kind": "trait", + "name": "BaseTrait", + "origin": "user", + "partialFieldCount": 0, + "receivers": [], + "signature": null, + "tlb": null, + "traits": [], + "uid": 1020, + }, + { + "ast": { + "attributes": [], + "declarations": [ + { + "as": null, + "id": 10, + "initializer": null, + "kind": "field_decl", + "loc": m: map, + "name": { + "id": 6, + "kind": "id", + "loc": m, + "text": "m", + }, + "type": { + "id": 9, + "keyStorageType": null, + "keyType": { + "id": 7, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + "kind": "map_type", + "loc": map, + "valueStorageType": null, + "valueType": { + "id": 8, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + }, + }, + { + "attributes": [], + "id": 18, + "kind": "function_def", + "loc": fun test() { foreach(_, _ in self.m) { } }, + "name": { + "id": 11, + "kind": "id", + "loc": test, + "text": "test", + }, + "params": [], + "return": null, + "statements": [ + { + "id": 17, + "keyName": { + "id": 12, + "kind": "id", + "loc": _, + "text": "_", + }, + "kind": "statement_foreach", + "loc": foreach(_, _ in self.m) { }, + "map": { + "aggregate": { + "id": 14, + "kind": "id", + "loc": self, + "text": "self", + }, + "field": { + "id": 15, + "kind": "id", + "loc": m, + "text": "m", + }, + "id": 16, + "kind": "field_access", + "loc": self.m, + }, + "statements": [], + "valueName": { + "id": 13, + "kind": "id", + "loc": _, + "text": "_", + }, + }, + ], + }, + ], + "id": 19, + "kind": "trait", + "loc": trait TraitWithForeach { + m: map; + + fun test() { foreach(_, _ in self.m) { } } +}, + "name": { + "id": 5, + "kind": "id", + "loc": TraitWithForeach, + "text": "TraitWithForeach", + }, + "traits": [], + }, + "constants": [], + "dependsOn": [], + "fields": [ + { + "abi": { + "name": "m", + "type": { + "key": "int", + "keyFormat": undefined, + "kind": "dict", + "value": "int", + "valueFormat": undefined, + }, + }, + "as": null, + "ast": { + "as": null, + "id": 10, + "initializer": null, + "kind": "field_decl", + "loc": m: map, + "name": { + "id": 6, + "kind": "id", + "loc": m, + "text": "m", + }, + "type": { + "id": 9, + "keyStorageType": null, + "keyType": { + "id": 7, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + "kind": "map_type", + "loc": map, + "valueStorageType": null, + "valueType": { + "id": 8, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + }, + }, + "default": undefined, + "index": 0, + "loc": m: map, + "name": "m", + "type": { + "key": "Int", + "keyAs": null, + "kind": "map", + "value": "Int", + "valueAs": null, + }, + }, + ], + "functions": Map { + "test" => { + "ast": { + "attributes": [], + "id": 18, + "kind": "function_def", + "loc": fun test() { foreach(_, _ in self.m) { } }, + "name": { + "id": 11, + "kind": "id", + "loc": test, + "text": "test", + }, + "params": [], + "return": null, + "statements": [ + { + "id": 17, + "keyName": { + "id": 12, + "kind": "id", + "loc": _, + "text": "_", + }, + "kind": "statement_foreach", + "loc": foreach(_, _ in self.m) { }, + "map": { + "aggregate": { + "id": 14, + "kind": "id", + "loc": self, + "text": "self", + }, + "field": { + "id": 15, + "kind": "id", + "loc": m, + "text": "m", + }, + "id": 16, + "kind": "field_access", + "loc": self.m, + }, + "statements": [], + "valueName": { + "id": 13, + "kind": "id", + "loc": _, + "text": "_", + }, + }, + ], + }, + "isAbstract": false, + "isGetter": false, + "isInline": false, + "isMutating": true, + "isOverride": false, + "isVirtual": false, + "methodId": null, + "name": "test", + "origin": "user", + "params": [], + "returns": { + "kind": "void", + }, + "self": { + "kind": "ref", + "name": "TraitWithForeach", + "optional": false, + }, + }, + }, + "header": null, + "init": null, + "interfaces": [], + "kind": "trait", + "name": "TraitWithForeach", + "origin": "user", + "partialFieldCount": 0, + "receivers": [], + "signature": null, + "tlb": null, + "traits": [ + { + "ast": { + "attributes": [], + "declarations": [], + "id": 4, + "kind": "trait", + "loc": trait BaseTrait { }, + "name": { + "id": 3, + "kind": "id", + "loc": BaseTrait, + "text": "BaseTrait", + }, + "traits": [], + }, + "constants": [], + "dependsOn": [], + "fields": [], + "functions": Map {}, + "header": null, + "init": null, + "interfaces": [], + "kind": "trait", + "name": "BaseTrait", + "origin": "user", + "partialFieldCount": 0, + "receivers": [], + "signature": null, + "tlb": null, + "traits": [], + "uid": 1020, + }, + ], + "uid": 20892, + }, + { + "ast": { + "attributes": [], + "declarations": [ + { + "as": null, + "id": 25, + "initializer": null, + "kind": "field_decl", + "loc": m: map, + "name": { + "id": 21, + "kind": "id", + "loc": m, + "text": "m", + }, + "type": { + "id": 24, + "keyStorageType": null, + "keyType": { + "id": 22, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + "kind": "map_type", + "loc": map, + "valueStorageType": null, + "valueType": { + "id": 23, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + }, + }, + ], + "id": 27, + "kind": "contract", + "loc": contract Test with TraitWithForeach { + m: map; +}, + "name": { + "id": 20, + "kind": "id", + "loc": Test, + "text": "Test", + }, + "traits": [ + { + "id": 26, + "kind": "id", + "loc": TraitWithForeach, + "text": "TraitWithForeach", + }, + ], + }, + "constants": [], + "dependsOn": [], + "fields": [ + { + "abi": { + "name": "m", + "type": { + "key": "int", + "keyFormat": undefined, + "kind": "dict", + "value": "int", + "valueFormat": undefined, + }, + }, + "as": null, + "ast": { + "as": null, + "id": 25, + "initializer": null, + "kind": "field_decl", + "loc": m: map, + "name": { + "id": 21, + "kind": "id", + "loc": m, + "text": "m", + }, + "type": { + "id": 24, + "keyStorageType": null, + "keyType": { + "id": 22, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + "kind": "map_type", + "loc": map, + "valueStorageType": null, + "valueType": { + "id": 23, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + }, + }, + "default": undefined, + "index": 0, + "loc": m: map, + "name": "m", + "type": { + "key": "Int", + "keyAs": null, + "kind": "map", + "value": "Int", + "valueAs": null, + }, + }, + ], + "functions": Map { + "test" => { + "ast": { + "attributes": [], + "id": 33, + "kind": "function_def", + "loc": fun test() { foreach(_, _ in self.m) { } }, + "name": { + "id": 11, + "kind": "id", + "loc": test, + "text": "test", + }, + "params": [], + "return": null, + "statements": [ + { + "id": 32, + "keyName": { + "id": 12, + "kind": "id", + "loc": _, + "text": "_", + }, + "kind": "statement_foreach", + "loc": foreach(_, _ in self.m) { }, + "map": { + "aggregate": { + "id": 30, + "kind": "id", + "loc": self, + "text": "self", + }, + "field": { + "id": 15, + "kind": "id", + "loc": m, + "text": "m", + }, + "id": 31, + "kind": "field_access", + "loc": self.m, + }, + "statements": [], + "valueName": { + "id": 13, + "kind": "id", + "loc": _, + "text": "_", + }, + }, + ], + }, + "isAbstract": false, + "isGetter": false, + "isInline": false, + "isMutating": true, + "isOverride": false, + "isVirtual": false, + "methodId": null, + "name": "test", + "origin": "user", + "params": [], + "returns": { + "kind": "void", + }, + "self": { + "kind": "ref", + "name": "Test", + "optional": false, + }, + }, + }, + "header": null, + "init": { + "ast": { + "id": 29, + "kind": "contract_init", + "loc": contract Test with TraitWithForeach { + m: map; +}, + "params": [], + "statements": [], + }, + "params": [], + }, + "interfaces": [], + "kind": "contract", + "name": "Test", + "origin": "user", + "partialFieldCount": 0, + "receivers": [], + "signature": null, + "tlb": null, + "traits": [ + { + "ast": { + "attributes": [], + "declarations": [], + "id": 4, + "kind": "trait", + "loc": trait BaseTrait { }, + "name": { + "id": 3, + "kind": "id", + "loc": BaseTrait, + "text": "BaseTrait", + }, + "traits": [], + }, + "constants": [], + "dependsOn": [], + "fields": [], + "functions": Map {}, + "header": null, + "init": null, + "interfaces": [], + "kind": "trait", + "name": "BaseTrait", + "origin": "user", + "partialFieldCount": 0, + "receivers": [], + "signature": null, + "tlb": null, + "traits": [], + "uid": 1020, + }, + { + "ast": { + "attributes": [], + "declarations": [ + { + "as": null, + "id": 10, + "initializer": null, + "kind": "field_decl", + "loc": m: map, + "name": { + "id": 6, + "kind": "id", + "loc": m, + "text": "m", + }, + "type": { + "id": 9, + "keyStorageType": null, + "keyType": { + "id": 7, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + "kind": "map_type", + "loc": map, + "valueStorageType": null, + "valueType": { + "id": 8, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + }, + }, + { + "attributes": [], + "id": 18, + "kind": "function_def", + "loc": fun test() { foreach(_, _ in self.m) { } }, + "name": { + "id": 11, + "kind": "id", + "loc": test, + "text": "test", + }, + "params": [], + "return": null, + "statements": [ + { + "id": 17, + "keyName": { + "id": 12, + "kind": "id", + "loc": _, + "text": "_", + }, + "kind": "statement_foreach", + "loc": foreach(_, _ in self.m) { }, + "map": { + "aggregate": { + "id": 14, + "kind": "id", + "loc": self, + "text": "self", + }, + "field": { + "id": 15, + "kind": "id", + "loc": m, + "text": "m", + }, + "id": 16, + "kind": "field_access", + "loc": self.m, + }, + "statements": [], + "valueName": { + "id": 13, + "kind": "id", + "loc": _, + "text": "_", + }, + }, + ], + }, + ], + "id": 19, + "kind": "trait", + "loc": trait TraitWithForeach { + m: map; + + fun test() { foreach(_, _ in self.m) { } } +}, + "name": { + "id": 5, + "kind": "id", + "loc": TraitWithForeach, + "text": "TraitWithForeach", + }, + "traits": [], + }, + "constants": [], + "dependsOn": [], + "fields": [ + { + "abi": { + "name": "m", + "type": { + "key": "int", + "keyFormat": undefined, + "kind": "dict", + "value": "int", + "valueFormat": undefined, + }, + }, + "as": null, + "ast": { + "as": null, + "id": 10, + "initializer": null, + "kind": "field_decl", + "loc": m: map, + "name": { + "id": 6, + "kind": "id", + "loc": m, + "text": "m", + }, + "type": { + "id": 9, + "keyStorageType": null, + "keyType": { + "id": 7, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + "kind": "map_type", + "loc": map, + "valueStorageType": null, + "valueType": { + "id": 8, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + }, + }, + "default": undefined, + "index": 0, + "loc": m: map, + "name": "m", + "type": { + "key": "Int", + "keyAs": null, + "kind": "map", + "value": "Int", + "valueAs": null, + }, + }, + ], + "functions": Map { + "test" => { + "ast": { + "attributes": [], + "id": 18, + "kind": "function_def", + "loc": fun test() { foreach(_, _ in self.m) { } }, + "name": { + "id": 11, + "kind": "id", + "loc": test, + "text": "test", + }, + "params": [], + "return": null, + "statements": [ + { + "id": 17, + "keyName": { + "id": 12, + "kind": "id", + "loc": _, + "text": "_", + }, + "kind": "statement_foreach", + "loc": foreach(_, _ in self.m) { }, + "map": { + "aggregate": { + "id": 14, + "kind": "id", + "loc": self, + "text": "self", + }, + "field": { + "id": 15, + "kind": "id", + "loc": m, + "text": "m", + }, + "id": 16, + "kind": "field_access", + "loc": self.m, + }, + "statements": [], + "valueName": { + "id": 13, + "kind": "id", + "loc": _, + "text": "_", + }, + }, + ], + }, + "isAbstract": false, + "isGetter": false, + "isInline": false, + "isMutating": true, + "isOverride": false, + "isVirtual": false, + "methodId": null, + "name": "test", + "origin": "user", + "params": [], + "returns": { + "kind": "void", + }, + "self": { + "kind": "ref", + "name": "TraitWithForeach", + "optional": false, + }, + }, + }, + "header": null, + "init": null, + "interfaces": [], + "kind": "trait", + "name": "TraitWithForeach", + "origin": "user", + "partialFieldCount": 0, + "receivers": [], + "signature": null, + "tlb": null, + "traits": [ + { + "ast": { + "attributes": [], + "declarations": [], + "id": 4, + "kind": "trait", + "loc": trait BaseTrait { }, + "name": { + "id": 3, + "kind": "id", + "loc": BaseTrait, + "text": "BaseTrait", + }, + "traits": [], + }, + "constants": [], + "dependsOn": [], + "fields": [], + "functions": Map {}, + "header": null, + "init": null, + "interfaces": [], + "kind": "trait", + "name": "BaseTrait", + "origin": "user", + "partialFieldCount": 0, + "receivers": [], + "signature": null, + "tlb": null, + "traits": [], + "uid": 1020, + }, + ], + "uid": 20892, + }, + ], + "uid": 44104, + }, +] +`; + +exports[`resolveDescriptors should resolve descriptors for trait-foreach 2`] = `[]`; diff --git a/src/types/resolveExpression.ts b/src/types/resolveExpression.ts index 702523f0f..2500cffca 100644 --- a/src/types/resolveExpression.ts +++ b/src/types/resolveExpression.ts @@ -38,6 +38,7 @@ import { GlobalFunctions } from "../abi/global"; import { isAssignable, moreGeneralType } from "./subtyping"; import { throwInternalCompilerError } from "../errors"; import { StructFunctions } from "../abi/struct"; +import { prettyPrint } from "../prettyPrinter"; const store = createContextStore<{ ast: AstExpression; @@ -62,7 +63,10 @@ function registerExpType( if (typeRefEquals(ex.description, description)) { return ctx; } - throwInternalCompilerError(`Expression ${exp.id} already has a type`); + throwInternalCompilerError( + `Expression ${prettyPrint(exp)} with exp.id = ${exp.id} already has registered type "${printTypeRef(ex.description)}" but the typechecker is trying to re-register it as "${printTypeRef(description)}"`, + exp.loc, + ); } return store.set(ctx, exp.id, { ast: exp, description }); } diff --git a/src/types/test/trait-foreach.tact b/src/types/test/trait-foreach.tact new file mode 100644 index 000000000..34abda3a9 --- /dev/null +++ b/src/types/test/trait-foreach.tact @@ -0,0 +1,13 @@ +primitive Int; +trait BaseTrait { } + +trait TraitWithForeach { + m: map; + + fun test() { foreach(_, _ in self.m) { } } +} + + +contract Test with TraitWithForeach { + m: map; +} From 7e6e938c148de3f9a26583f0c298e878643cfd49 Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Wed, 6 Nov 2024 15:01:59 +0100 Subject: [PATCH 06/24] feat(api): export `precompile()` and allow passing `AstModule[]` in (#984) --- src/index.ts | 1 + src/pipeline/precompile.ts | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 7469a0101..da47c4368 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ export { enableFeatures, build } from "./pipeline/build"; +export { precompile } from "./pipeline/precompile"; export { TactError, TactParseError, diff --git a/src/pipeline/precompile.ts b/src/pipeline/precompile.ts index eca8f3533..8ada419da 100644 --- a/src/pipeline/precompile.ts +++ b/src/pipeline/precompile.ts @@ -7,18 +7,20 @@ import { resolveErrors } from "../types/resolveErrors"; import { resolveSignatures } from "../types/resolveSignatures"; import { resolveImports } from "../imports/resolveImports"; import { VirtualFileSystem } from "../vfs/VirtualFileSystem"; +import { AstModule } from "../grammar/ast"; export function precompile( ctx: CompilerContext, project: VirtualFileSystem, stdlib: VirtualFileSystem, entrypoint: string, + parsedModules?: AstModule[], ) { // Load all sources const imported = resolveImports({ entrypoint, project, stdlib }); // Add information about all the source code entries to the context - ctx = openContext(ctx, imported.tact, imported.func); + ctx = openContext(ctx, imported.tact, imported.func, parsedModules); // First load type descriptors and check that // they all have valid signatures From 794c651cf30f6e3e60fecc12d1849374539d1a42 Mon Sep 17 00:00:00 2001 From: Daniil Sedov Date: Wed, 6 Nov 2024 17:23:15 +0300 Subject: [PATCH 07/24] feat/fix: `..` pattern for destructuring struct fields (#969) * remove the destructuring fields quantity check in interpreter * feat(typechecker): check variables count --- CHANGELOG.md | 2 +- .../__snapshots__/grammar.spec.ts.snap | 202 +++++++++--------- src/grammar/ast.ts | 9 + src/grammar/grammar.ohm | 4 +- src/grammar/grammar.ts | 18 +- src/grammar/iterators.ts | 2 + src/grammar/test/stmt-destructuring.tact | 12 +- src/interpreter.ts | 8 - src/prettyPrinter.ts | 3 +- src/test/contracts/case-destructuring.tact | 12 +- .../renamer-expected/case-destructuring.tact | 12 +- src/test/e2e-emulated/contracts/structs.tact | 26 +++ src/test/e2e-emulated/structs.spec.ts | 2 + .../resolveStatements.spec.ts.snap | 44 ++-- src/types/resolveStatements.ts | 11 + ...ructuring-fields-non-existing-punned2.tact | 4 +- ...turing-fields-non-existing-underscore.tact | 4 +- ...tmt-destructuring-fields-non-existing.tact | 4 +- ...stmt-destructuring-fields-wrong-count.tact | 17 ++ ...tmt-destructuring-fields-wrong-count2.tact | 17 ++ src/types/stmts/stmt-destructuring.tact | 12 +- 21 files changed, 274 insertions(+), 151 deletions(-) create mode 100644 src/types/stmts-failed/stmt-destructuring-fields-wrong-count.tact create mode 100644 src/types/stmts-failed/stmt-destructuring-fields-wrong-count2.tact diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e1f45c7f..3fe5c5fa5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Docs: Google Analytics tags per every page: PR [#921](https://github.com/tact-lang/tact/pull/921) - Docs: Added NFTs cookbook: PR [#958](https://github.com/tact-lang/tact/pull/958) - Ability to specify a compile-time method ID expression for getters: PR [#922](https://github.com/tact-lang/tact/pull/922) and PR [#932](https://github.com/tact-lang/tact/pull/932) -- Destructuring of structs and messages: PR [#856](https://github.com/tact-lang/tact/pull/856) +- Destructuring of structs and messages: PR [#856](https://github.com/tact-lang/tact/pull/856), PR [#969](https://github.com/tact-lang/tact/pull/969) - Docs: automatic links to Web IDE from all code blocks: PR [#994](https://github.com/tact-lang/tact/pull/994) - The `SendDefaultMode` send mode constant to the standard library: PR [#1010](https://github.com/tact-lang/tact/pull/1010) - Docs: initial semi-automated Chinese translation of the documentation: PR [#942](https://github.com/tact-lang/tact/pull/942) diff --git a/src/grammar/__snapshots__/grammar.spec.ts.snap b/src/grammar/__snapshots__/grammar.spec.ts.snap index 333d3dc1a..060ae17e9 100644 --- a/src/grammar/__snapshots__/grammar.spec.ts.snap +++ b/src/grammar/__snapshots__/grammar.spec.ts.snap @@ -8075,7 +8075,7 @@ exports[`grammar should parse stmt-augmented-assign-logic 1`] = ` exports[`grammar should parse stmt-destructuring 1`] = ` { - "id": 140, + "id": 148, "imports": [], "items": [ { @@ -8209,17 +8209,17 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "attributes": [], - "id": 139, + "id": 147, "kind": "function_def", "loc": fun testFunc(): Int { let s = S{ a: 1, b: 2, c: 3 }; let S { a, b, c } = s; - let S { a: a1 } = s; - let S { b: b1 } = s; - let S { c: c1 } = s; - let S { a: a2, b: b2 } = s; - let S { a: a3, c: c3 } = s; - let S { b: b4, c: c4 } = s; + let S { a: a1, .. } = s; + let S { b: b1, .. } = s; + let S { c: c1, .. } = s; + let S { a: a2, b: b2, .. } = s; + let S { a: a3, c: c3, .. } = s; + let S { b: b4, c: c4, .. } = s; let m = M{ a: 1, b: 2 }; let M { a: a_m, b: b_m } = m; @@ -8321,12 +8321,12 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "expression": { - "id": 45, + "id": 46, "kind": "id", "loc": s, "text": "s", }, - "id": 46, + "id": 47, "identifiers": Map { "a" => [ { @@ -8371,6 +8371,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, ], }, + "ignoreUnspecifiedFields": false, "kind": "statement_destruct", "loc": let S { a, b, c } = s;, "type": { @@ -8382,32 +8383,33 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "expression": { - "id": 51, + "id": 53, "kind": "id", "loc": s, "text": "s", }, - "id": 52, + "id": 54, "identifiers": Map { "a" => [ { - "id": 48, + "id": 49, "kind": "id", "loc": a, "text": "a", }, { - "id": 49, + "id": 50, "kind": "id", "loc": a1, "text": "a1", }, ], }, + "ignoreUnspecifiedFields": true, "kind": "statement_destruct", - "loc": let S { a: a1 } = s;, + "loc": let S { a: a1, .. } = s;, "type": { - "id": 47, + "id": 48, "kind": "type_id", "loc": S, "text": "S", @@ -8415,32 +8417,33 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "expression": { - "id": 57, + "id": 60, "kind": "id", "loc": s, "text": "s", }, - "id": 58, + "id": 61, "identifiers": Map { "b" => [ { - "id": 54, + "id": 56, "kind": "id", "loc": b, "text": "b", }, { - "id": 55, + "id": 57, "kind": "id", "loc": b1, "text": "b1", }, ], }, + "ignoreUnspecifiedFields": true, "kind": "statement_destruct", - "loc": let S { b: b1 } = s;, + "loc": let S { b: b1, .. } = s;, "type": { - "id": 53, + "id": 55, "kind": "type_id", "loc": S, "text": "S", @@ -8448,32 +8451,33 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "expression": { - "id": 63, + "id": 67, "kind": "id", "loc": s, "text": "s", }, - "id": 64, + "id": 68, "identifiers": Map { "c" => [ { - "id": 60, + "id": 63, "kind": "id", "loc": c, "text": "c", }, { - "id": 61, + "id": 64, "kind": "id", "loc": c1, "text": "c1", }, ], }, + "ignoreUnspecifiedFields": true, "kind": "statement_destruct", - "loc": let S { c: c1 } = s;, + "loc": let S { c: c1, .. } = s;, "type": { - "id": 59, + "id": 62, "kind": "type_id", "loc": S, "text": "S", @@ -8481,22 +8485,22 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "expression": { - "id": 72, + "id": 77, "kind": "id", "loc": s, "text": "s", }, - "id": 73, + "id": 78, "identifiers": Map { "a" => [ { - "id": 66, + "id": 70, "kind": "id", "loc": a, "text": "a", }, { - "id": 67, + "id": 71, "kind": "id", "loc": a2, "text": "a2", @@ -8504,23 +8508,24 @@ exports[`grammar should parse stmt-destructuring 1`] = ` ], "b" => [ { - "id": 69, + "id": 73, "kind": "id", "loc": b, "text": "b", }, { - "id": 70, + "id": 74, "kind": "id", "loc": b2, "text": "b2", }, ], }, + "ignoreUnspecifiedFields": true, "kind": "statement_destruct", - "loc": let S { a: a2, b: b2 } = s;, + "loc": let S { a: a2, b: b2, .. } = s;, "type": { - "id": 65, + "id": 69, "kind": "type_id", "loc": S, "text": "S", @@ -8528,22 +8533,22 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "expression": { - "id": 81, + "id": 87, "kind": "id", "loc": s, "text": "s", }, - "id": 82, + "id": 88, "identifiers": Map { "a" => [ { - "id": 75, + "id": 80, "kind": "id", "loc": a, "text": "a", }, { - "id": 76, + "id": 81, "kind": "id", "loc": a3, "text": "a3", @@ -8551,23 +8556,24 @@ exports[`grammar should parse stmt-destructuring 1`] = ` ], "c" => [ { - "id": 78, + "id": 83, "kind": "id", "loc": c, "text": "c", }, { - "id": 79, + "id": 84, "kind": "id", "loc": c3, "text": "c3", }, ], }, + "ignoreUnspecifiedFields": true, "kind": "statement_destruct", - "loc": let S { a: a3, c: c3 } = s;, + "loc": let S { a: a3, c: c3, .. } = s;, "type": { - "id": 74, + "id": 79, "kind": "type_id", "loc": S, "text": "S", @@ -8575,22 +8581,22 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "expression": { - "id": 90, + "id": 97, "kind": "id", "loc": s, "text": "s", }, - "id": 91, + "id": 98, "identifiers": Map { "b" => [ { - "id": 84, + "id": 90, "kind": "id", "loc": b, "text": "b", }, { - "id": 85, + "id": 91, "kind": "id", "loc": b4, "text": "b4", @@ -8598,23 +8604,24 @@ exports[`grammar should parse stmt-destructuring 1`] = ` ], "c" => [ { - "id": 87, + "id": 93, "kind": "id", "loc": c, "text": "c", }, { - "id": 88, + "id": 94, "kind": "id", "loc": c4, "text": "c4", }, ], }, + "ignoreUnspecifiedFields": true, "kind": "statement_destruct", - "loc": let S { b: b4, c: c4 } = s;, + "loc": let S { b: b4, c: c4, .. } = s;, "type": { - "id": 83, + "id": 89, "kind": "type_id", "loc": S, "text": "S", @@ -8625,15 +8632,15 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "args": [ { "field": { - "id": 94, + "id": 101, "kind": "id", "loc": a, "text": "a", }, - "id": 96, + "id": 103, "initializer": { "base": 10, - "id": 95, + "id": 102, "kind": "number", "loc": 1, "value": 1n, @@ -8643,15 +8650,15 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "field": { - "id": 97, + "id": 104, "kind": "id", "loc": b, "text": "b", }, - "id": 99, + "id": 106, "initializer": { "base": 10, - "id": 98, + "id": 105, "kind": "number", "loc": 2, "value": 2n, @@ -8660,21 +8667,21 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": b: 2, }, ], - "id": 100, + "id": 107, "kind": "struct_instance", "loc": M{ a: 1, b: 2 }, "type": { - "id": 93, + "id": 100, "kind": "type_id", "loc": M, "text": "M", }, }, - "id": 101, + "id": 108, "kind": "statement_let", "loc": let m = M{ a: 1, b: 2 };, "name": { - "id": 92, + "id": 99, "kind": "id", "loc": m, "text": "m", @@ -8683,22 +8690,22 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "expression": { - "id": 109, + "id": 117, "kind": "id", "loc": m, "text": "m", }, - "id": 110, + "id": 118, "identifiers": Map { "a" => [ { - "id": 103, + "id": 110, "kind": "id", "loc": a, "text": "a", }, { - "id": 104, + "id": 111, "kind": "id", "loc": a_m, "text": "a_m", @@ -8706,23 +8713,24 @@ exports[`grammar should parse stmt-destructuring 1`] = ` ], "b" => [ { - "id": 106, + "id": 113, "kind": "id", "loc": b, "text": "b", }, { - "id": 107, + "id": 114, "kind": "id", "loc": b_m, "text": "b_m", }, ], }, + "ignoreUnspecifiedFields": false, "kind": "statement_destruct", "loc": let M { a: a_m, b: b_m } = m;, "type": { - "id": 102, + "id": 109, "kind": "type_id", "loc": M, "text": "M", @@ -8730,46 +8738,46 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "expression": { - "id": 137, + "id": 145, "kind": "op_binary", "left": { - "id": 135, + "id": 143, "kind": "op_binary", "left": { - "id": 133, + "id": 141, "kind": "op_binary", "left": { - "id": 131, + "id": 139, "kind": "op_binary", "left": { - "id": 129, + "id": 137, "kind": "op_binary", "left": { - "id": 127, + "id": 135, "kind": "op_binary", "left": { - "id": 125, + "id": 133, "kind": "op_binary", "left": { - "id": 123, + "id": 131, "kind": "op_binary", "left": { - "id": 121, + "id": 129, "kind": "op_binary", "left": { - "id": 119, + "id": 127, "kind": "op_binary", "left": { - "id": 117, + "id": 125, "kind": "op_binary", "left": { - "id": 115, + "id": 123, "kind": "op_binary", "left": { - "id": 113, + "id": 121, "kind": "op_binary", "left": { - "id": 111, + "id": 119, "kind": "id", "loc": a, "text": "a", @@ -8777,7 +8785,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b, "op": "+", "right": { - "id": 112, + "id": 120, "kind": "id", "loc": b, "text": "b", @@ -8786,7 +8794,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c, "op": "+", "right": { - "id": 114, + "id": 122, "kind": "id", "loc": c, "text": "c", @@ -8795,7 +8803,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1, "op": "+", "right": { - "id": 116, + "id": 124, "kind": "id", "loc": a1, "text": "a1", @@ -8804,7 +8812,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1, "op": "+", "right": { - "id": 118, + "id": 126, "kind": "id", "loc": b1, "text": "b1", @@ -8813,7 +8821,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1 + c1, "op": "+", "right": { - "id": 120, + "id": 128, "kind": "id", "loc": c1, "text": "c1", @@ -8822,7 +8830,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1 + c1 + a2, "op": "+", "right": { - "id": 122, + "id": 130, "kind": "id", "loc": a2, "text": "a2", @@ -8831,7 +8839,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1 + c1 + a2 + b2, "op": "+", "right": { - "id": 124, + "id": 132, "kind": "id", "loc": b2, "text": "b2", @@ -8840,7 +8848,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1 + c1 + a2 + b2 + a3, "op": "+", "right": { - "id": 126, + "id": 134, "kind": "id", "loc": a3, "text": "a3", @@ -8849,7 +8857,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1 + c1 + a2 + b2 + a3 + c3, "op": "+", "right": { - "id": 128, + "id": 136, "kind": "id", "loc": c3, "text": "c3", @@ -8858,7 +8866,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1 + c1 + a2 + b2 + a3 + c3 + b4, "op": "+", "right": { - "id": 130, + "id": 138, "kind": "id", "loc": b4, "text": "b4", @@ -8867,7 +8875,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1 + c1 + a2 + b2 + a3 + c3 + b4 + c4, "op": "+", "right": { - "id": 132, + "id": 140, "kind": "id", "loc": c4, "text": "c4", @@ -8876,7 +8884,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1 + c1 + a2 + b2 + a3 + c3 + b4 + c4 + a_m, "op": "+", "right": { - "id": 134, + "id": 142, "kind": "id", "loc": a_m, "text": "a_m", @@ -8885,13 +8893,13 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1 + c1 + a2 + b2 + a3 + c3 + b4 + c4 + a_m + b_m, "op": "+", "right": { - "id": 136, + "id": 144, "kind": "id", "loc": b_m, "text": "b_m", }, }, - "id": 138, + "id": 146, "kind": "statement_return", "loc": return a + b + c + a1 + b1 + c1 + a2 + b2 + a3 + c3 + b4 + c4 + a_m + b_m;, }, diff --git a/src/grammar/ast.ts b/src/grammar/ast.ts index cc75370b2..200d190e8 100644 --- a/src/grammar/ast.ts +++ b/src/grammar/ast.ts @@ -326,6 +326,7 @@ export type AstStatementDestruct = { type: AstTypeId; /** field name -> [field id, local id] */ identifiers: Map; + ignoreUnspecifiedFields: boolean; expression: AstExpression; id: number; loc: SrcInfo; @@ -592,6 +593,13 @@ export type AstDestructMapping = { loc: SrcInfo; }; +export type AstDestructEnd = { + kind: "destruct_end"; + ignoreUnspecifiedFields: boolean; + id: number; + loc: SrcInfo; +}; + export type AstNumber = { kind: "number"; base: AstNumberBase; @@ -704,6 +712,7 @@ export type AstReceiverKind = export type AstNode = | AstFuncId | AstDestructMapping + | AstDestructEnd | AstExpression | AstStatement | AstTypeDecl diff --git a/src/grammar/grammar.ohm b/src/grammar/grammar.ohm index 09a5cf2fe..3c3b4ad7f 100644 --- a/src/grammar/grammar.ohm +++ b/src/grammar/grammar.ohm @@ -158,7 +158,9 @@ Tact { StatementForEach = foreach "(" id "," id "in" Expression ")" "{" Statement* "}" - StatementDestruct = let typeId "{" ListOf ","? "}" "=" Expression (";" | &"}") + StatementDestruct = let typeId "{" ListOf EndOfIdentifiers "}" "=" Expression (";" | &"}") + EndOfIdentifiers = "," ".." --ignoreUnspecifiedFields + | ","? --regular DestructItem = id ":" id --regular | id --punned diff --git a/src/grammar/grammar.ts b/src/grammar/grammar.ts index d33aa28ca..2048e5139 100644 --- a/src/grammar/grammar.ts +++ b/src/grammar/grammar.ts @@ -1018,7 +1018,7 @@ semantics.addOperation("astOfStatement", { typeId, _lparen, identifiers, - _optTrailingComma, + endOfIdentifiers, _rparen, _equals, expression, @@ -1043,6 +1043,8 @@ semantics.addOperation("astOfStatement", { ]); return map; }, new Map()), + ignoreUnspecifiedFields: + endOfIdentifiers.astOfExpression().ignoreUnspecifiedFields, expression: expression.astOfExpression(), loc: createRef(this), }); @@ -1191,6 +1193,20 @@ semantics.addOperation("astOfExpression", { loc: createRef(this), }); }, + EndOfIdentifiers_regular(_comma) { + return createAstNode({ + kind: "destruct_end", + ignoreUnspecifiedFields: false, + loc: createRef(this), + }); + }, + EndOfIdentifiers_ignoreUnspecifiedFields(_comma, _dotDot) { + return createAstNode({ + kind: "destruct_end", + ignoreUnspecifiedFields: true, + loc: createRef(this), + }); + }, ExpressionAdd_add(left, _plus, right) { return createAstNode({ kind: "op_binary", diff --git a/src/grammar/iterators.ts b/src/grammar/iterators.ts index 8db047100..bc473a918 100644 --- a/src/grammar/iterators.ts +++ b/src/grammar/iterators.ts @@ -141,6 +141,8 @@ export function traverse(node: AstNode, callback: (node: AstNode) => void) { }); traverse(node.expression, callback); break; + case "destruct_end": + break; case "statement_return": if (node.expression) traverse(node.expression, callback); break; diff --git a/src/grammar/test/stmt-destructuring.tact b/src/grammar/test/stmt-destructuring.tact index a61c86668..7c7316826 100644 --- a/src/grammar/test/stmt-destructuring.tact +++ b/src/grammar/test/stmt-destructuring.tact @@ -12,12 +12,12 @@ message M { fun testFunc(): Int { let s = S{ a: 1, b: 2, c: 3 }; let S { a, b, c } = s; - let S { a: a1 } = s; - let S { b: b1 } = s; - let S { c: c1 } = s; - let S { a: a2, b: b2 } = s; - let S { a: a3, c: c3 } = s; - let S { b: b4, c: c4 } = s; + let S { a: a1, .. } = s; + let S { b: b1, .. } = s; + let S { c: c1, .. } = s; + let S { a: a2, b: b2, .. } = s; + let S { a: a3, c: c3, .. } = s; + let S { b: b4, c: c4, .. } = s; let m = M{ a: 1, b: 2 }; let M { a: a_m, b: b_m } = m; diff --git a/src/interpreter.ts b/src/interpreter.ts index 66213c44b..65a223e2e 100644 --- a/src/interpreter.ts +++ b/src/interpreter.ts @@ -1465,14 +1465,6 @@ export class Interpreter { ast.expression.loc, ); } - if (ast.identifiers.size !== Object.keys(val).length - 1) { - throwErrorConstEval( - `destructuring assignment expected ${Object.keys(val).length - 1} fields, but got ${ - ast.identifiers.size - }`, - ast.loc, - ); - } for (const [field, name] of ast.identifiers.values()) { if (name.text === "_") { diff --git a/src/prettyPrinter.ts b/src/prettyPrinter.ts index 4c7685802..2e1ccf630 100644 --- a/src/prettyPrinter.ts +++ b/src/prettyPrinter.ts @@ -697,7 +697,8 @@ export class PrettyPrinter { acc.push(id); return acc; }, []); - return `${this.indent()}let ${this.ppAstTypeId(statement.type)} {${ids.join(", ")}} = ${this.ppAstExpression(statement.expression)};`; + const restPattern = statement.ignoreUnspecifiedFields ? ", .." : ""; + return `${this.indent()}let ${this.ppAstTypeId(statement.type)} {${ids.join(", ")}${restPattern}} = ${this.ppAstExpression(statement.expression)};`; } } diff --git a/src/test/contracts/case-destructuring.tact b/src/test/contracts/case-destructuring.tact index 8a4c89170..e1ace4b5b 100644 --- a/src/test/contracts/case-destructuring.tact +++ b/src/test/contracts/case-destructuring.tact @@ -12,12 +12,12 @@ message M { fun testFunc(): Int { let s = S{a: 1, b: 2, c: 3}; let S {a, b, c} = s; - let S {a: a1} = s; - let S {b: b1} = s; - let S {c: c1} = s; - let S {a: a2, b: b2} = s; - let S {a: a3, c: c3} = s; - let S {b: b4, c: c4} = s; + let S {a: a1, ..} = s; + let S {b: b1, ..} = s; + let S {c: c1, ..} = s; + let S {a: a2, b: b2, ..} = s; + let S {a: a3, c: c3, ..} = s; + let S {b: b4, c: c4, ..} = s; let m = M{a: 1, b: 2}; let M {a: a_m, b: b_m} = m; return a + b + c + a1 + b1 + c1 + a2 + b2 + a3 + c3 + b4 + c4 + a_m + b_m; diff --git a/src/test/contracts/renamer-expected/case-destructuring.tact b/src/test/contracts/renamer-expected/case-destructuring.tact index c11334837..16b94fc9b 100644 --- a/src/test/contracts/renamer-expected/case-destructuring.tact +++ b/src/test/contracts/renamer-expected/case-destructuring.tact @@ -12,12 +12,12 @@ message message_decl_1 { fun function_def_2(): Int { let s = S{a: 1, b: 2, c: 3}; let S {a, b, c} = s; - let S {a: a1} = s; - let S {b: b1} = s; - let S {c: c1} = s; - let S {a: a2, b: b2} = s; - let S {a: a3, c: c3} = s; - let S {b: b4, c: c4} = s; + let S {a: a1, ..} = s; + let S {b: b1, ..} = s; + let S {c: c1, ..} = s; + let S {a: a2, b: b2, ..} = s; + let S {a: a3, c: c3, ..} = s; + let S {b: b4, c: c4, ..} = s; let m = M{a: 1, b: 2}; let M {a: a_m, b: b_m} = m; return a + b + c + a1 + b1 + c1 + a2 + b2 + a3 + c3 + b4 + c4 + a_m + b_m; diff --git a/src/test/e2e-emulated/contracts/structs.tact b/src/test/e2e-emulated/contracts/structs.tact index 4ab416e3a..cea1d5f8e 100644 --- a/src/test/e2e-emulated/contracts/structs.tact +++ b/src/test/e2e-emulated/contracts/structs.tact @@ -318,6 +318,17 @@ fun destructuringTest7(): S1 { return S1 {a: e, b: b, c: a}; } +fun destructuringTest8(): Int { + let s = S { + a: true, + b: 42 + }; + + let S {b, ..} = s; + + return b; +} + contract StructsTester { s1: S = S {a: false, b: 21 + 21}; s2: S; @@ -1101,4 +1112,19 @@ contract StructsTester { get fun destructuringTest7Const(): S1 { return destructuringTest7(); } + + get fun destructuringTest8(): Int { + let s = S { + a: true, + b: 42 + }; + + let S {b, ..} = s; + + return b; + } + + get fun destructuringTest8Const(): Int { + return destructuringTest8(); + } } diff --git a/src/test/e2e-emulated/structs.spec.ts b/src/test/e2e-emulated/structs.spec.ts index f7f20f5db..2ffd4fdc0 100644 --- a/src/test/e2e-emulated/structs.spec.ts +++ b/src/test/e2e-emulated/structs.spec.ts @@ -439,5 +439,7 @@ describe("structs", () => { b: 2n, c: 1n, }); + expect(await contract.getDestructuringTest8()).toBe(42n); + expect(await contract.getDestructuringTest8Const()).toBe(42n); }); }); diff --git a/src/types/__snapshots__/resolveStatements.spec.ts.snap b/src/types/__snapshots__/resolveStatements.spec.ts.snap index b3bc08044..0a89ef360 100644 --- a/src/types/__snapshots__/resolveStatements.spec.ts.snap +++ b/src/types/__snapshots__/resolveStatements.spec.ts.snap @@ -939,12 +939,12 @@ Line 16, col 29: `; exports[`resolveStatements should fail statements for stmt-destructuring-fields-non-existing 1`] = ` -":15:22: Field '"d"' not found in type 'S' -Line 15, col 22: +":15:19: Field '"d"' not found in type 'S' +Line 15, col 19: 14 | let s = S{ a: 1, b: 2, c: 3 }; -> 15 | let S { a, b, c, d: e } = s; - ^ - 16 | return a + b + c + e; +> 15 | let S { a, b, d: e } = s; + ^ + 16 | return a + b + e; " `; @@ -959,12 +959,12 @@ Line 15, col 16: `; exports[`resolveStatements should fail statements for stmt-destructuring-fields-non-existing-punned2 1`] = ` -":15:22: Field '"d"' not found in type 'S' -Line 15, col 22: +":15:19: Field '"d"' not found in type 'S' +Line 15, col 19: 14 | let s = S{ a: 1, b: 2, c: 3 }; -> 15 | let S { a, b, c, d } = s; - ^ - 16 | return a + b + c + d; +> 15 | let S { a, b, d } = s; + ^ + 16 | return a + b + d; " `; @@ -972,9 +972,9 @@ exports[`resolveStatements should fail statements for stmt-destructuring-fields- ":15:12: Field '"_"' not found in type 'S' Line 15, col 12: 14 | let s = S{ a: 1, b: 2, c: 3 }; -> 15 | let S {_, b} = s; +> 15 | let S {_, b, c} = s; ^ - 16 | return b; + 16 | return b + c; " `; @@ -998,6 +998,26 @@ Line 16, col 16: " `; +exports[`resolveStatements should fail statements for stmt-destructuring-fields-wrong-count 1`] = ` +":15:5: Expected 3 fields, but got 2 +Line 15, col 5: + 14 | let s = S{ a: 1, b: 2, c: 3 }; +> 15 | let S { a, b } = s; + ^~~~~~~~~~~~~~~~~~~ + 16 | return a + b; +" +`; + +exports[`resolveStatements should fail statements for stmt-destructuring-fields-wrong-count2 1`] = ` +":15:5: Expected 3 fields, but got 4 +Line 15, col 5: + 14 | let s = S{ a: 1, b: 2, c: 3 }; +> 15 | let S { a, b, c, d } = s; + ^~~~~~~~~~~~~~~~~~~~~~~~~ + 16 | return a + b + c + d; +" +`; + exports[`resolveStatements should fail statements for stmt-destructuring-fields-wrong-type 1`] = ` ":18:21: Type mismatch: "S2" is not assignable to "S1" Line 18, col 21: diff --git a/src/types/resolveStatements.ts b/src/types/resolveStatements.ts index 9ea42ceea..1e6e2afe4 100644 --- a/src/types/resolveStatements.ts +++ b/src/types/resolveStatements.ts @@ -717,6 +717,17 @@ function processStatements( ); } + // Check variables count + if ( + !s.ignoreUnspecifiedFields && + s.identifiers.size !== ty.fields.length + ) { + throwCompilationError( + `Expected ${ty.fields.length} fields, but got ${s.identifiers.size}`, + s.loc, + ); + } + // Compare type with the specified one const typeRef = resolveTypeRef(ctx, s.type); if (typeRef.kind !== "ref") { diff --git a/src/types/stmts-failed/stmt-destructuring-fields-non-existing-punned2.tact b/src/types/stmts-failed/stmt-destructuring-fields-non-existing-punned2.tact index cf02bcad2..09952ac7d 100644 --- a/src/types/stmts-failed/stmt-destructuring-fields-non-existing-punned2.tact +++ b/src/types/stmts-failed/stmt-destructuring-fields-non-existing-punned2.tact @@ -12,6 +12,6 @@ struct S { fun testFunc(): Int { let s = S{ a: 1, b: 2, c: 3 }; - let S { a, b, c, d } = s; - return a + b + c + d; + let S { a, b, d } = s; + return a + b + d; } \ No newline at end of file diff --git a/src/types/stmts-failed/stmt-destructuring-fields-non-existing-underscore.tact b/src/types/stmts-failed/stmt-destructuring-fields-non-existing-underscore.tact index 9bf8c31a5..783bcad18 100644 --- a/src/types/stmts-failed/stmt-destructuring-fields-non-existing-underscore.tact +++ b/src/types/stmts-failed/stmt-destructuring-fields-non-existing-underscore.tact @@ -12,6 +12,6 @@ struct S { fun testFunc(): Int { let s = S{ a: 1, b: 2, c: 3 }; - let S {_, b} = s; - return b; + let S {_, b, c} = s; + return b + c; } \ No newline at end of file diff --git a/src/types/stmts-failed/stmt-destructuring-fields-non-existing.tact b/src/types/stmts-failed/stmt-destructuring-fields-non-existing.tact index 8dcf1283e..1ed48343a 100644 --- a/src/types/stmts-failed/stmt-destructuring-fields-non-existing.tact +++ b/src/types/stmts-failed/stmt-destructuring-fields-non-existing.tact @@ -12,6 +12,6 @@ struct S { fun testFunc(): Int { let s = S{ a: 1, b: 2, c: 3 }; - let S { a, b, c, d: e } = s; - return a + b + c + e; + let S { a, b, d: e } = s; + return a + b + e; } \ No newline at end of file diff --git a/src/types/stmts-failed/stmt-destructuring-fields-wrong-count.tact b/src/types/stmts-failed/stmt-destructuring-fields-wrong-count.tact new file mode 100644 index 000000000..d711b2cf8 --- /dev/null +++ b/src/types/stmts-failed/stmt-destructuring-fields-wrong-count.tact @@ -0,0 +1,17 @@ +primitive Int; + +trait BaseTrait { + +} + +struct S { + a: Int; + b: Int; + c: Int; +} + +fun testFunc(): Int { + let s = S{ a: 1, b: 2, c: 3 }; + let S { a, b } = s; + return a + b; +} \ No newline at end of file diff --git a/src/types/stmts-failed/stmt-destructuring-fields-wrong-count2.tact b/src/types/stmts-failed/stmt-destructuring-fields-wrong-count2.tact new file mode 100644 index 000000000..cf02bcad2 --- /dev/null +++ b/src/types/stmts-failed/stmt-destructuring-fields-wrong-count2.tact @@ -0,0 +1,17 @@ +primitive Int; + +trait BaseTrait { + +} + +struct S { + a: Int; + b: Int; + c: Int; +} + +fun testFunc(): Int { + let s = S{ a: 1, b: 2, c: 3 }; + let S { a, b, c, d } = s; + return a + b + c + d; +} \ No newline at end of file diff --git a/src/types/stmts/stmt-destructuring.tact b/src/types/stmts/stmt-destructuring.tact index c559d99c1..e0a4ae779 100644 --- a/src/types/stmts/stmt-destructuring.tact +++ b/src/types/stmts/stmt-destructuring.tact @@ -18,12 +18,12 @@ message M { fun testFunc(): Int { let s = S{ a: 1, b: 2, c: 3 }; let S { a, b, c } = s; - let S { a: a1 } = s; - let S { b: b1 } = s; - let S { c: c1 } = s; - let S { a: a2, b: b2 } = s; - let S { a: a3, c: c3 } = s; - let S { b: b4, c: c4 } = s; + let S { a: a1, .. } = s; + let S { b: b1, .. } = s; + let S { c: c1, .. } = s; + let S { a: a2, b: b2, .. } = s; + let S { a: a3, c: c3, .. } = s; + let S { b: b4, c: c4, .. } = s; let m = M{ a: 1, b: 2 }; let M { a: a_m, b: b_m } = m; From 918d51db3cf3af96d597ce69eeb72c48986b93da Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Wed, 6 Nov 2024 15:35:20 +0100 Subject: [PATCH 08/24] feat(docs): destructuring statement (#964) --- CHANGELOG.md | 2 +- docs/src/content/docs/book/statements.mdx | 155 ++++++++++++++++++++++ 2 files changed, 156 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fe5c5fa5..a8b387f58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Docs: Google Analytics tags per every page: PR [#921](https://github.com/tact-lang/tact/pull/921) - Docs: Added NFTs cookbook: PR [#958](https://github.com/tact-lang/tact/pull/958) - Ability to specify a compile-time method ID expression for getters: PR [#922](https://github.com/tact-lang/tact/pull/922) and PR [#932](https://github.com/tact-lang/tact/pull/932) -- Destructuring of structs and messages: PR [#856](https://github.com/tact-lang/tact/pull/856), PR [#969](https://github.com/tact-lang/tact/pull/969) +- Destructuring of structs and messages: PR [#856](https://github.com/tact-lang/tact/pull/856), PR [#964](https://github.com/tact-lang/tact/pull/964), PR [#969](https://github.com/tact-lang/tact/pull/969) - Docs: automatic links to Web IDE from all code blocks: PR [#994](https://github.com/tact-lang/tact/pull/994) - The `SendDefaultMode` send mode constant to the standard library: PR [#1010](https://github.com/tact-lang/tact/pull/1010) - Docs: initial semi-automated Chinese translation of the documentation: PR [#942](https://github.com/tact-lang/tact/pull/942) diff --git a/docs/src/content/docs/book/statements.mdx b/docs/src/content/docs/book/statements.mdx index 4db01b93c..ce363f5a3 100644 --- a/docs/src/content/docs/book/statements.mdx +++ b/docs/src/content/docs/book/statements.mdx @@ -3,6 +3,8 @@ title: Statements description: "This page lists all the statements in Tact, which can appear anywhere in the function bodies" --- +import { Badge } from '@astrojs/starlight/components'; + The following statements can appear anywhere in the [function](/book/functions) body. ## `let` statement {#let} @@ -96,6 +98,157 @@ value += 5; // augmented assignment (one of the many, see below) ::: +## Destructuring assignment + +

+ +The destructuring assignment is a concise way to unpack [Structs][s] and [Messages][m] into distinct variables. It mirrors the [instantiation syntax](/book/expressions#instantiation), but instead of creating a new [Struct][s] or [Message][m] it binds every field or some of the fields to their respective variables. + +The syntax is derived from the [`let` statement](#let), and instead of specifying the variable name directly it involves specifying the structure type on the left side of the [assignment operator `={:tact}`](/book/operators#assignment), which corresponds to the structure type of the value on the right side. + +```tact {6} +// Definition of Example +struct Example { number: Int } + +// An arbitrary helper function +fun get42(): Example { return Example { number: 42 } } + +fun basic() { + // Basic syntax of destructuring assignment (to the left of "="): + let Example { number } = get42(); + // ------- ------ ------- + // ↑ ↑ ↑ + // | | gives the Example Struct + // | definition of "number" variable, derived + // | from the field "number" in Example Struct + // target structure type "Example" + // to destructure fields from + + // Same as above, but with an instantiation + // to showcase how destructuring syntax mirrors it: + let Example { number } = Example { number: 42 }; + // ---------------------- + // ↑ + // instantiation of Example Struct + + // Above examples of syntax are roughly equivalent + // to the following series of statements: + let example = Example { number: 42 }; + let number = example.number; +} +``` + +Just like in [instantiation](/book/expressions#instantiation), the trailing comma is allowed. + +```tact +struct Example { number: Int } + +fun trailblazing() { + let Example { + number, // trailing comma inside variable list + } = Example { + number: 42, // trailing comma inside field list + }; +} +``` + +:::note + + [Augmented assignment operators](/book/operators#augmented-assignment) do not make sense for such assignments and will therefore be reported as parsing errors: + + ```tact + struct Example { number: Int } + fun get42(): Example { return Example { number: 42 } } + + fun basic() { + let Example { number } += get42(); + // ^ this will result in the parse error: + // expected "=" + } + ``` + +::: + +To create a binding under a different variable name, specify it after the semicolon `:{:tact}`. + +```tact +// Similar definition, but this time field is called "field", not "number" +struct Example { field: Int } + +fun naming(s: Example) { + let Example { field: varFromField } = s; + // ------------ ↑ + // ↑ | + // | instance of Example Struct, received + // | as a parameter of the function "naming" + // definition of "varFromField" variable, derived + // from the field "field" in Example Struct +} +``` + +Note, that the order of bindings doesn't matter — all the fields retain their values and types under their names no matter the order in which they stand in their definition in the respective [Struct][s] or [Message][m]. + +```tact +// "first" goes first, then goes "second" +struct Two { first: Int; second: String } + +fun order(s: Two) { + let Two { second, first } = s; + // ------ ----- + // ↑ ↑ + // | this variable will be of type Int, + // | same as the "first" field on Struct Two + // this variable will be of type String, + // same as the "second" field in Struct Two +} +``` + +Destructuring assignment is exhaustive and requires specifying all the fields as variables. To deliberately ignore some of the fields, use an underscore `_{:tact}`, which discards the considered field's value. Note, that such wildcard variable name `_{:tact}` cannot be accessed: + +```tact +// "first" goes first, then goes "second" +struct Two { first: Int; second: String } + +fun discard(s: Two) { + let Two { second: _, first } = s; + // --- + // ↑ + // discards the "second" field, only taking the "first" +} +``` + +To completely ignore the rest of the fields, use `..` at the end of the list: + +```tact +struct Many { one: Int; two: Int; three: Int; fans: Int } + +fun ignore(s: Many) { + let Many { fans, .. } = s; + // -- + // ↑ + // ignores all the unspecified fields, + // defining only "fans" +} +``` + +:::caution + + At the moment, destructuring of nested [Structs][s] or [Messages][m] isn't allowed. That is, the following won't work: + + ```tact + struct First { nested: Second } + struct Second { field: Int } + + fun example() { + let prep = First { nested: Second { field: 42 } }; + let First { nested: { field: thing } } = prep; + // ^ this will result in the parse error: + // expected "_", "A".."Z", or "a".."z" + } + ``` + +::: + ## Branches Control the flow of the code. @@ -415,3 +568,5 @@ foreach (_, _ in quartiles) { ::: [int]: /book/integers +[s]: /book/structs-and-messages#structs +[m]: /book/structs-and-messages#messages From b8c07e1c48373d69bbb77956a57ba4217f106124 Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Thu, 7 Nov 2024 13:07:27 +0100 Subject: [PATCH 09/24] feat: implement `Set.isSubsetOf()` for Node <22, which defaults to builtin implementation whenever possible (#1009) --- .github/workflows/tact.yml | 23 +++++++++ scripts/copy-files.ts | 4 +- src/types/resolveDescriptors.ts | 9 ++-- src/utils/isSubsetOf.spec.ts | 91 +++++++++++++++++++++++++++++++++ src/utils/isSubsetOf.ts | 38 ++++++++++++++ 5 files changed, 160 insertions(+), 5 deletions(-) create mode 100644 src/utils/isSubsetOf.spec.ts create mode 100644 src/utils/isSubsetOf.ts diff --git a/.github/workflows/tact.yml b/.github/workflows/tact.yml index 81733e3ed..9bdc67ca9 100644 --- a/.github/workflows/tact.yml +++ b/.github/workflows/tact.yml @@ -22,6 +22,29 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 + - name: Setup Node.js 18 for backwards compat tests + uses: actions/setup-node@v3 + with: + node-version: 18 + # without caching + + - name: Backwards compat tests + run: | + # Temporarily ignore engines + yarn config set ignore-engines true + # Install dependencies, gen and build the compiler + yarn install + yarn clean + yarn gen + yarn build + # Test some specific things for backwards compatibility. + # It's important to restrain from using too much of Node.js 22+ features + # until it goes into maintenance LTS state and majority of users catches up + yarn jest -t 'isSubsetOf' + # Clean-up + yarn cleanall + yarn config delete ignore-engines + - name: Setup Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: diff --git a/scripts/copy-files.ts b/scripts/copy-files.ts index 4859b57c9..ae69bcd37 100644 --- a/scripts/copy-files.ts +++ b/scripts/copy-files.ts @@ -1,8 +1,10 @@ import * as fs from "node:fs/promises"; import * as path from "node:path"; +import * as glob from "glob"; const cp = async (fromGlob: string, toPath: string) => { - for await (const file of fs.glob(fromGlob)) { + const files = glob.sync(fromGlob); + for (const file of files) { await fs.copyFile(file, path.join(toPath, path.basename(file))); } }; diff --git a/src/types/resolveDescriptors.ts b/src/types/resolveDescriptors.ts index f6eaafa3e..557c83fb5 100644 --- a/src/types/resolveDescriptors.ts +++ b/src/types/resolveDescriptors.ts @@ -43,6 +43,7 @@ import { import { getRawAST } from "../grammar/store"; import { cloneNode } from "../grammar/clone"; import { crc16 } from "../utils/crc16"; +import { isSubsetOf } from "../utils/isSubsetOf"; import { evalConstantExpression } from "../constEval"; import { resolveABIType, intMapFormats } from "./resolveABITypeRef"; import { enabledExternals } from "../config/features"; @@ -909,13 +910,13 @@ export function resolveDescriptors(ctx: CompilerContext) { const paramSet = new Set( a.params.map((typedId) => idText(typedId.name)), ); - if (!paramSet.isSubsetOf(shuffleArgSet)) { + if (!isSubsetOf(paramSet, shuffleArgSet)) { throwCompilationError( "asm argument rearrangement must mention all function parameters", a.loc, ); } - if (!shuffleArgSet.isSubsetOf(paramSet)) { + if (!isSubsetOf(shuffleArgSet, paramSet)) { throwCompilationError( "asm argument rearrangement must mention only function parameters", a.loc, @@ -973,13 +974,13 @@ export function resolveDescriptors(ctx: CompilerContext) { // mutating functions also return `self` arg (implicitly in Tact, but explicitly in FunC) retTupleSize += isMutating ? 1 : 0; const returnValueSet = new Set([...Array(retTupleSize).keys()]); - if (!returnValueSet.isSubsetOf(shuffleRetSet)) { + if (!isSubsetOf(returnValueSet, shuffleRetSet)) { throwCompilationError( `asm return rearrangement must mention all return position numbers: [0..${retTupleSize - 1}]`, a.loc, ); } - if (!shuffleRetSet.isSubsetOf(returnValueSet)) { + if (!isSubsetOf(shuffleRetSet, returnValueSet)) { throwCompilationError( `asm return rearrangement must mention only valid return position numbers: [0..${retTupleSize - 1}]`, a.loc, diff --git a/src/utils/isSubsetOf.spec.ts b/src/utils/isSubsetOf.spec.ts new file mode 100644 index 000000000..cde7c2925 --- /dev/null +++ b/src/utils/isSubsetOf.spec.ts @@ -0,0 +1,91 @@ +import { ReadonlySetLike, isSubsetOf } from "./isSubsetOf"; + +// Tests are adapted from: +// https://github.com/zloirock/core-js/blob/227a758ef96fa585a66cc1e89741e7d0bb696f48/tests/unit-global/es.set.is-subset-of.js + +describe("isSubsetOf", () => { + /* eslint-disable @typescript-eslint/no-explicit-any */ + let s1: Set; + let s2: ReadonlySetLike; + + it("should implement isSubsetOf correctly", () => { + s1 = new Set([1]); + s2 = new Set([1, 2, 3]); + expect(isSubsetOf(s1, s2)).toBe(true); + + s1 = new Set([1]); + s2 = new Set([2, 3, 4]); + expect(isSubsetOf(s1, s2)).toBe(false); + + s1 = new Set([1, 2, 3]); + s2 = new Set([5, 4, 3, 2, 1]); + expect(isSubsetOf(s1, s2)).toBe(true); + + s1 = new Set([1, 2, 3]); + s2 = new Set([5, 4, 3, 2]); + expect(isSubsetOf(s1, s2)).toBe(false); + + s1 = new Set([1]); + s2 = createSetLike([1, 2, 3]); + expect(isSubsetOf(s1, s2)).toBe(true); + + s1 = new Set([1]); + s2 = createSetLike([2, 3, 4]); + expect(isSubsetOf(s1, s2)).toBe(false); + + s1 = new Set([1, 2, 3]); + s2 = createSetLike([5, 4, 3, 2, 1]); + expect(isSubsetOf(s1, s2)).toBe(true); + + s1 = new Set([1, 2, 3]); + s2 = createSetLike([5, 4, 3, 2]); + expect(isSubsetOf(s1, s2)).toBe(false); + + s1 = new Set([1, 2, 3]); + s2 = new Set([1]); + expect(isSubsetOf(s1, s2)).toBe(false); + + s1 = new Set([1, 2, 3]); + s2 = new Set(); + expect(isSubsetOf(s1, s2)).toBe(false); + + s1 = new Set(); + s2 = new Set([1, 2, 3]); + expect(isSubsetOf(s1, s2)).toBe(true); + }); +}); + +// Helper functions are adapted from: +// https://github.com/zloirock/core-js/blob/227a758ef96fa585a66cc1e89741e7d0bb696f48/tests/helpers/helpers.js + +function createSetLike(elements: T[]): ReadonlySetLike { + return { + size: elements.length, + has(value: T): boolean { + return includes(elements, value); + }, + keys(): Iterator { + return createIterator(elements); + }, + }; +} + +function includes(target: T[], wanted: T) { + return target.some((element) => element === wanted); +} + +function createIterator(elements: T[]): Iterator { + let index = 0; + const iterator = { + called: false, + /* eslint-disable @typescript-eslint/no-explicit-any */ + next(): IteratorResult { + iterator.called = true; + return { + value: elements[index++], + done: index > elements.length, + }; + }, + }; + return iterator; +} diff --git a/src/utils/isSubsetOf.ts b/src/utils/isSubsetOf.ts new file mode 100644 index 000000000..c333bb58c --- /dev/null +++ b/src/utils/isSubsetOf.ts @@ -0,0 +1,38 @@ +/** Taken from TypeScript collection lib to perfectly match the .isSubsetOf signature */ +export interface ReadonlySetLike { + /** + * Despite its name, returns an iterator of the values in the set-like. + */ + keys(): Iterator; + /** + * @returns a boolean indicating whether an element with the specified value exists in the set-like or not. + */ + has(value: T): boolean; + /** + * @returns the number of (unique) elements in the set-like. + */ + readonly size: number; +} + +/** + * @returns a boolean indicating whether all the elements in Set `one` are also in the `other`. + */ +export function isSubsetOf( + one: Set, + other: ReadonlySetLike, +): boolean { + // If the builtin method exists, just call it + if ("isSubsetOf" in Set.prototype) { + return one.isSubsetOf(other); + } + // If not, provide the implementation + if (one.size > other.size) { + return false; + } + for (const element of one) { + if (!other.has(element)) { + return false; + } + } + return true; +} From bb25297f32d5fcb428889c8fd0cf76a193d1f5a5 Mon Sep 17 00:00:00 2001 From: verytactical <186486509+verytactical@users.noreply.github.com> Date: Mon, 11 Nov 2024 22:28:27 +0400 Subject: [PATCH 10/24] refactor: add utility to err on unreachable branches (#991) and use it in pattern matching over all key/value map type pairs --- CHANGELOG.md | 1 + src/generator/writers/writeStdlib.ts | 217 +++++++++------------------ src/utils/tricks.ts | 90 +++++++++++ 3 files changed, 164 insertions(+), 144 deletions(-) create mode 100644 src/utils/tricks.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index a8b387f58..f5bd192f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The `SendDefaultMode` send mode constant to the standard library: PR [#1010](https://github.com/tact-lang/tact/pull/1010) - Docs: initial semi-automated Chinese translation of the documentation: PR [#942](https://github.com/tact-lang/tact/pull/942) - The `replace` and `replaceGet` methods for the `Map` type: PR [#941](https://github.com/tact-lang/tact/pull/941) +- Utility for logging errors in code that was supposed to be unreachable [#991](https://github.com/tact-lang/tact/pull/991) ### Changed diff --git a/src/generator/writers/writeStdlib.ts b/src/generator/writers/writeStdlib.ts index 790a4e5f1..4bd365603 100644 --- a/src/generator/writers/writeStdlib.ts +++ b/src/generator/writers/writeStdlib.ts @@ -1,7 +1,7 @@ import { contractErrors } from "../../abi/errors"; import { maxTupleSize } from "../../bindings/typescript/writeStruct"; import { enabledMasterchain } from "../../config/features"; -import { throwInternalCompilerError } from "../../errors"; +import { match } from "../../utils/tricks"; import { WriterContext } from "../Writer"; export function writeStdlib(ctx: WriterContext): void { @@ -1279,44 +1279,24 @@ function genTactDictSet( return "idict_delete?"; } }; - const returnExpr = () => { - switch (`${key}:${value}`) { - case "int:int": - return "(idict_set_builder(d, kl, k, begin_cell().store_int(v, vl)), ())"; - case "int:uint": - return "(idict_set_builder(d, kl, k, begin_cell().store_uint(v, vl)), ())"; - case "int:coins": - return "(idict_set_builder(d, kl, k, begin_cell().store_coins(v)), ())"; - case "uint:int": - return "(udict_set_builder(d, kl, k, begin_cell().store_int(v, vl)), ())"; - case "uint:uint": - return "(udict_set_builder(d, kl, k, begin_cell().store_uint(v, vl)), ())"; - case "uint:coins": - return "(udict_set_builder(d, kl, k, begin_cell().store_coins(v)), ())"; - case "slice:int": - return "(dict_set_builder(d, kl, k, begin_cell().store_int(v, vl)), ())"; - case "slice:uint": - return "(dict_set_builder(d, kl, k, begin_cell().store_uint(v, vl)), ())"; - case "slice:coins": - return "(dict_set_builder(d, kl, k, begin_cell().store_coins(v)), ())"; - case "int:cell": - return "(idict_set_ref(d, kl, k, v), ())"; - case "uint:cell": - return "(udict_set_ref(d, kl, k, v), ())"; - case "int:slice": - return "(idict_set(d, kl, k, v), ())"; - case "uint:slice": - return "(udict_set(d, kl, k, v), ())"; - case "slice:cell": - return `${ctx.used("__tact_dict_set_ref")}(d, kl, k, v)`; - case "slice:slice": - return "(dict_set_builder(d, kl, k, begin_cell().store_slice(v)), ())"; - default: - throwInternalCompilerError( - `Unprocessed combination of key/value types ${key}/${value} in dict set operation`, - ); - } - }; + // prettier-ignore + const returnExpr = () => match(key, value) + .on("int", "int")(() => "(idict_set_builder(d, kl, k, begin_cell().store_int(v, vl)), ())") + .on("int", "uint")(() => "(idict_set_builder(d, kl, k, begin_cell().store_uint(v, vl)), ())") + .on("int", "coins")(() => "(idict_set_builder(d, kl, k, begin_cell().store_coins(v)), ())") + .on("uint", "int")(() => "(udict_set_builder(d, kl, k, begin_cell().store_int(v, vl)), ())") + .on("uint", "uint")(() => "(udict_set_builder(d, kl, k, begin_cell().store_uint(v, vl)), ())") + .on("uint", "coins")(() => "(udict_set_builder(d, kl, k, begin_cell().store_coins(v)), ())") + .on("slice", "int")(() => "(dict_set_builder(d, kl, k, begin_cell().store_int(v, vl)), ())") + .on("slice", "uint")(() => "(dict_set_builder(d, kl, k, begin_cell().store_uint(v, vl)), ())") + .on("slice", "coins")(() => "(dict_set_builder(d, kl, k, begin_cell().store_coins(v)), ())") + .on("int", "cell")(() => "(idict_set_ref(d, kl, k, v), ())") + .on("uint", "cell")(() => "(udict_set_ref(d, kl, k, v), ())") + .on("int", "slice")(() => "(idict_set(d, kl, k, v), ())") + .on("uint", "slice")(() => "(udict_set(d, kl, k, v), ())") + .on("slice", "cell")(() => `${ctx.used("__tact_dict_set_ref")}(d, kl, k, v)`) + .on("slice", "slice")(() => "(dict_set_builder(d, kl, k, begin_cell().store_slice(v)), ())") + .end(); ctx.fun(`__tact_dict_set_${key}_${value}`, () => { ctx.signature( `(cell, ()) __tact_dict_set_${key}_${value}(cell d, int kl, ${signatureKeyType} k, ${signatureValueType} v${valBitsArg()})`, @@ -1354,35 +1334,24 @@ function genTactDictGetMin( return ", int vl"; } }; - const dictGetMin = () => { - switch (`${key}:${value}`) { - case "int:int": - case "int:uint": - case "int:coins": - case "int:slice": - return "idict_get_min?"; - case "uint:int": - case "uint:uint": - case "uint:coins": - case "uint:slice": - return "udict_get_min?"; - case "slice:int": - case "slice:uint": - case "slice:coins": - case "slice:slice": - return ctx.used("__tact_dict_min"); - case "int:cell": - return "idict_get_min_ref?"; - case "uint:cell": - return "udict_get_min_ref?"; - case "slice:cell": - return ctx.used("__tact_dict_min_ref"); - default: - throwInternalCompilerError( - `Unprocessed combination of key/value types ${key}/${value} in dict get min operation`, - ); - } - }; + // prettier-ignore + const dictGetMin = () => match(key, value) + .on("int", "int")(() => "idict_get_min?") + .on("int", "uint")(() => "idict_get_min?") + .on("int", "coins")(() => "idict_get_min?") + .on("int", "slice")(() => "idict_get_min?") + .on("uint", "int")(() => "udict_get_min?") + .on("uint", "uint")(() => "udict_get_min?") + .on("uint", "coins")(() => "udict_get_min?") + .on("uint", "slice")(() => "udict_get_min?") + .on("slice", "int")(() => ctx.used("__tact_dict_min")) + .on("slice", "uint")(() => ctx.used("__tact_dict_min")) + .on("slice", "coins")(() => ctx.used("__tact_dict_min")) + .on("slice", "slice")(() => ctx.used("__tact_dict_min")) + .on("int", "cell")(() => "idict_get_min_ref?") + .on("uint", "cell")(() => "udict_get_min_ref?") + .on("slice", "cell")(() => ctx.used("__tact_dict_min_ref")) + .end(); const returnValExpr = () => { switch (value) { case "int": @@ -1510,44 +1479,24 @@ function genTactDictReplace( return "idict_delete?"; } }; - const returnExpr = () => { - switch (`${key}:${value}`) { - case "int:int": - return "idict_replace_builder?(d, kl, k, begin_cell().store_int(v, vl))"; - case "int:uint": - return "idict_replace_builder?(d, kl, k, begin_cell().store_uint(v, vl))"; - case "int:coins": - return "idict_replace_builder?(d, kl, k, begin_cell().store_coins(v))"; - case "uint:int": - return "udict_replace_builder?(d, kl, k, begin_cell().store_int(v, vl))"; - case "uint:uint": - return "udict_replace_builder?(d, kl, k, begin_cell().store_uint(v, vl))"; - case "uint:coins": - return "udict_replace_builder?(d, kl, k, begin_cell().store_coins(v))"; - case "slice:int": - return "dict_replace_builder?(d, kl, k, begin_cell().store_int(v, vl))"; - case "slice:uint": - return "dict_replace_builder?(d, kl, k, begin_cell().store_uint(v, vl))"; - case "slice:coins": - return "dict_replace_builder?(d, kl, k, begin_cell().store_coins(v))"; - case "int:cell": - return "idict_replace_ref?(d, kl, k, v)"; - case "uint:cell": - return "udict_replace_ref?(d, kl, k, v)"; - case "int:slice": - return "idict_replace?(d, kl, k, v)"; - case "uint:slice": - return "udict_replace?(d, kl, k, v)"; - case "slice:cell": - return `${ctx.used("__tact_dict_replace_ref")}(d, kl, k, v)`; - case "slice:slice": - return "dict_replace_builder?(d, kl, k, begin_cell().store_slice(v))"; - default: - throwInternalCompilerError( - `Unprocessed combination of key/value types ${key}/${value} in dict replace operation`, - ); - } - }; + // prettier-ignore + const returnExpr = () => match(key, value) + .on("int", "int")(() => "idict_replace_builder?(d, kl, k, begin_cell().store_int(v, vl))") + .on("int", "uint")(() => "idict_replace_builder?(d, kl, k, begin_cell().store_uint(v, vl))") + .on("int", "coins")(() => "idict_replace_builder?(d, kl, k, begin_cell().store_coins(v))") + .on("uint", "int")(() => "udict_replace_builder?(d, kl, k, begin_cell().store_int(v, vl))") + .on("uint", "uint")(() => "udict_replace_builder?(d, kl, k, begin_cell().store_uint(v, vl))") + .on("uint", "coins")(() => "udict_replace_builder?(d, kl, k, begin_cell().store_coins(v))") + .on("slice", "int")(() => "dict_replace_builder?(d, kl, k, begin_cell().store_int(v, vl))") + .on("slice", "uint")(() => "dict_replace_builder?(d, kl, k, begin_cell().store_uint(v, vl))") + .on("slice", "coins")(() => "dict_replace_builder?(d, kl, k, begin_cell().store_coins(v))") + .on("int", "cell")(() => "idict_replace_ref?(d, kl, k, v)") + .on("uint", "cell")(() => "udict_replace_ref?(d, kl, k, v)") + .on("int", "slice")(() => "idict_replace?(d, kl, k, v)") + .on("uint", "slice")(() => "udict_replace?(d, kl, k, v)") + .on("slice", "cell")(() => `${ctx.used("__tact_dict_replace_ref")}(d, kl, k, v)`) + .on("slice", "slice")(() => "dict_replace_builder?(d, kl, k, begin_cell().store_slice(v))") + .end(); ctx.fun(`__tact_dict_replace_${key}_${value}`, () => { ctx.signature( `(cell, (int)) __tact_dict_replace_${key}_${value}(cell d, int kl, ${signatureKeyType} k, ${signatureValueType} v${valBitsArg()})`, @@ -1596,44 +1545,24 @@ function genTactDictReplaceGet( return `idict_delete_get${cellSuffix}?`; } }; - const returnExpr = () => { - switch (`${key}:${value}`) { - case "int:int": - return "d~idict_replaceget?(kl, k, begin_cell().store_int(v, vl).end_cell().begin_parse())"; - case "int:uint": - return "d~idict_replaceget?(kl, k, begin_cell().store_uint(v, vl).end_cell().begin_parse())"; - case "int:coins": - return "d~idict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse())"; - case "uint:int": - return "d~udict_replaceget?(kl, k, begin_cell().store_int(v, vl).end_cell().begin_parse())"; - case "uint:uint": - return "d~udict_replaceget?(kl, k, begin_cell().store_uint(v, vl).end_cell().begin_parse())"; - case "uint:coins": - return "d~udict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse())"; - case "slice:int": - return "d~dict_replaceget?(kl, k, begin_cell().store_int(v, vl).end_cell().begin_parse())"; - case "slice:uint": - return "d~dict_replaceget?(kl, k, begin_cell().store_uint(v, vl).end_cell().begin_parse())"; - case "slice:coins": - return "d~dict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse())"; - case "int:cell": - return "d~idict_replaceget_ref?(kl, k, v)"; - case "uint:cell": - return "d~udict_replaceget_ref?(kl, k, v)"; - case "int:slice": - return "d~idict_replaceget?(kl, k, v)"; - case "uint:slice": - return "d~udict_replaceget?(kl, k, v)"; - case "slice:cell": - return `d~${ctx.used("__tact_dict_replaceget_ref")}(kl, k, v)`; - case "slice:slice": - return "d~dict_replaceget?(kl, k, begin_cell().store_slice(v).end_cell().begin_parse())"; - default: - throwInternalCompilerError( - `Unprocessed combination of key/value types ${key}/${value} in dict replaceGet operation`, - ); - } - }; + // prettier-ignore + const returnExpr = () => match(key, value) + .on("int", "int")(() => "d~idict_replaceget?(kl, k, begin_cell().store_int(v, vl).end_cell().begin_parse())") + .on("int", "uint")(() => "d~idict_replaceget?(kl, k, begin_cell().store_uint(v, vl).end_cell().begin_parse())") + .on("int", "coins")(() => "d~idict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse())") + .on("uint", "int")(() => "d~udict_replaceget?(kl, k, begin_cell().store_int(v, vl).end_cell().begin_parse())") + .on("uint", "uint")(() => "d~udict_replaceget?(kl, k, begin_cell().store_uint(v, vl).end_cell().begin_parse())") + .on("uint", "coins")(() => "d~udict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse())") + .on("slice", "int")(() => "d~dict_replaceget?(kl, k, begin_cell().store_int(v, vl).end_cell().begin_parse())") + .on("slice", "uint")(() => "d~dict_replaceget?(kl, k, begin_cell().store_uint(v, vl).end_cell().begin_parse())") + .on("slice", "coins")(() => "d~dict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse())") + .on("int", "cell")(() => "d~idict_replaceget_ref?(kl, k, v)") + .on("uint", "cell")(() => "d~udict_replaceget_ref?(kl, k, v)") + .on("int", "slice")(() => "d~idict_replaceget?(kl, k, v)") + .on("uint", "slice")(() => "d~udict_replaceget?(kl, k, v)") + .on("slice", "cell")(() => `d~${ctx.used("__tact_dict_replaceget_ref")}(kl, k, v)`) + .on("slice", "slice")(() => "d~dict_replaceget?(kl, k, begin_cell().store_slice(v).end_cell().begin_parse())") + .end(); const parseExpr = () => { switch (value) { case "slice": diff --git a/src/utils/tricks.ts b/src/utils/tricks.ts new file mode 100644 index 000000000..f85c7e6dd --- /dev/null +++ b/src/utils/tricks.ts @@ -0,0 +1,90 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +type Extend = H extends infer A ? [...T, A] : never; +type Flat = TS extends [ + infer H, + ...infer T, +] + ? Flat> + : R; + +declare const NoSuchCase: unique symbol; +interface NoSuchCaseBug extends Array { + [NoSuchCase]: L; +} +type On = { + on: ( + ...key: I extends Flat ? DI : NoSuchCaseBug + ) => ( + handler: (...args: Extract>) => DO, + ) => MV>, O | DO>; +}; + +declare const CasesAreNotExhaustive: unique symbol; +interface NonExhaustiveBug { + [CasesAreNotExhaustive]: L; +} +type End = [I] extends [never] + ? EndInternal + : { + otherwise: (handle: (...input: I) => DO) => O | DO; + end: NonExhaustiveBug; + }; +type MV = End & On; + +type OnInternal = { + on: ( + ...key: DI + ) => ( + handler: (...args: Extract>) => DO, + ) => MVInternal>, O | DO>; +}; +type EndInternal = { + otherwise: (handle: (...input: I) => DO) => O | DO; + end: () => O; +}; +type MVInternal = EndInternal & OnInternal; + +const deepMatch = (a: unknown, b: unknown): boolean => { + if ( + a === b && + ["number", "string", "boolean", "bigint"].includes(typeof a) && + typeof a === typeof b + ) { + return true; + } + if (a === null || b === null) { + return a === b; + } + if (typeof a === "object" && typeof b === "object") { + if (Array.isArray(a) && Array.isArray(b) && a.length === b.length) { + return a.every((a, i) => deepMatch(a, b[i])); + } else { + return Object.entries(b).every(([k, b]) => + deepMatch(k in a ? (a as any)[k] : undefined, b), + ); + } + } + return false; +}; + +export const match = ( + ...args: I +): MV, never> => { + const rec = (end: () => O): MVInternal => ({ + end, + otherwise: (handler) => handler(...(args as unknown as I)), + on: + (...match: DI) => + (handler: (...args: Extract>) => DO) => + rec>, O | DO>(() => + deepMatch(args, match) + ? handler( + ...(args as unknown as Extract>), + ) + : end(), + ), + }); + return rec, never>(() => { + throw new Error("Not exhaustive"); + }) as MV, never>; +}; From e387a1059d7b100719893c2d8f9e1d1bdde5bebf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Nov 2024 18:30:25 +0400 Subject: [PATCH 11/24] chore(deps-dev): bump @types/node from 22.8.7 to 22.9.0 (#1027) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.8.7 to 22.9.0. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 227432a95..e07bbdf6e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1454,9 +1454,9 @@ integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== "@types/node@*", "@types/node@>=13.7.0", "@types/node@^22.5.0": - version "22.8.7" - resolved "https://registry.npmjs.org/@types/node/-/node-22.8.7.tgz#04ab7a073d95b4a6ee899f235d43f3c320a976f4" - integrity sha512-LidcG+2UeYIWcMuMUpBKOnryBWG/rnmOHQR5apjn8myTQcx3rinFRn7DcIFhMnS0PPFSC6OafdIKEad0lj6U0Q== + version "22.9.0" + resolved "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz#b7f16e5c3384788542c72dc3d561a7ceae2c0365" + integrity sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ== dependencies: undici-types "~6.19.8" From 056abf1e6703f32e7bb7a32d57c4774f2a45c6a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Nov 2024 18:30:48 +0400 Subject: [PATCH 12/24] chore(deps-dev): bump knip from 5.36.2 to 5.36.7 (#1026) Bumps [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip) from 5.36.2 to 5.36.7. - [Release notes](https://github.com/webpro-nl/knip/releases) - [Changelog](https://github.com/webpro-nl/knip/blob/main/packages/knip/.release-it.json) - [Commits](https://github.com/webpro-nl/knip/commits/5.36.7/packages/knip) --- updated-dependencies: - dependency-name: knip dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index e07bbdf6e..a5e0ddd17 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4008,9 +4008,9 @@ kleur@^3.0.3: integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== knip@^5.24.1: - version "5.36.2" - resolved "https://registry.npmjs.org/knip/-/knip-5.36.2.tgz#346ce5eb464bdf34329cdcf3a6d41d0a41dce647" - integrity sha512-MudNTKBSqThAFAV29GuRPSKSebByZeQCFeNgXVRVSd+sXcubehTgQHTGqqiwlXGCt4WBP7vuVekp0ZehfZtHuw== + version "5.36.7" + resolved "https://registry.npmjs.org/knip/-/knip-5.36.7.tgz#5a570e89aa5781ea0f98929f558596c2128777e7" + integrity sha512-PSuu62+6wqd1Q1V/ZzbDhvJ3X+RU8wZILon90h2s93+d1OZL118ZE9WihzSqwP29GVt72MTlbS/HHG+O47H68w== dependencies: "@nodelib/fs.walk" "1.2.8" "@snyk/github-codeowners" "1.1.0" From 08133e8418f3c6dcb49229b45cfeb7dd261bbe1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Nov 2024 18:31:07 +0400 Subject: [PATCH 13/24] chore(deps-dev): bump cspell from 8.15.7 to 8.16.0 (#1028) Bumps [cspell](https://github.com/streetsidesoftware/cspell/tree/HEAD/packages/cspell) from 8.15.7 to 8.16.0. - [Release notes](https://github.com/streetsidesoftware/cspell/releases) - [Changelog](https://github.com/streetsidesoftware/cspell/blob/main/packages/cspell/CHANGELOG.md) - [Commits](https://github.com/streetsidesoftware/cspell/commits/v8.16.0/packages/cspell) --- updated-dependencies: - dependency-name: cspell dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 280 +++++++++++++++++++++++++++--------------------------- 1 file changed, 140 insertions(+), 140 deletions(-) diff --git a/yarn.lock b/yarn.lock index a5e0ddd17..ebf553d9b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -308,17 +308,17 @@ resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@cspell/cspell-bundled-dicts@8.15.7": - version "8.15.7" - resolved "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-8.15.7.tgz#beeb2af34b9691febd489c915211bc2a919e0e90" - integrity sha512-lNbrlHhDnOWCJh/vNCliZJz4X1KMEZqWJZpTgmdPrEGS9ZyfEiCmZyoQzw+fauC5Xo7dwd2KdS9VMHftAJqMPQ== +"@cspell/cspell-bundled-dicts@8.16.0": + version "8.16.0" + resolved "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-8.16.0.tgz#f82749921e3e1181e295670f17ca28453cddc238" + integrity sha512-R0Eqq5kTZnmZ0elih5uY3TWjMqqAeMl7ciU7maUs+m1FNjCEdJXtJ9wrQxNgjmXi0tX8cvahZRO3O558tEz/KA== dependencies: "@cspell/dict-ada" "^4.0.5" "@cspell/dict-al" "^1.0.3" "@cspell/dict-aws" "^4.0.7" "@cspell/dict-bash" "^4.1.8" "@cspell/dict-companies" "^3.1.7" - "@cspell/dict-cpp" "^6.0.0" + "@cspell/dict-cpp" "^6.0.1" "@cspell/dict-cryptocurrencies" "^5.0.3" "@cspell/dict-csharp" "^4.0.5" "@cspell/dict-css" "^4.0.16" @@ -351,8 +351,8 @@ "@cspell/dict-makefile" "^1.0.3" "@cspell/dict-markdown" "^2.0.7" "@cspell/dict-monkeyc" "^1.0.9" - "@cspell/dict-node" "^5.0.4" - "@cspell/dict-npm" "^5.1.9" + "@cspell/dict-node" "^5.0.5" + "@cspell/dict-npm" "^5.1.11" "@cspell/dict-php" "^4.0.13" "@cspell/dict-powershell" "^5.0.13" "@cspell/dict-public-licenses" "^2.0.11" @@ -361,7 +361,7 @@ "@cspell/dict-ruby" "^5.0.7" "@cspell/dict-rust" "^4.0.9" "@cspell/dict-scala" "^5.0.6" - "@cspell/dict-software-terms" "^4.1.12" + "@cspell/dict-software-terms" "^4.1.13" "@cspell/dict-sql" "^2.1.8" "@cspell/dict-svelte" "^1.0.5" "@cspell/dict-swift" "^2.0.4" @@ -369,34 +369,34 @@ "@cspell/dict-typescript" "^3.1.11" "@cspell/dict-vue" "^3.0.3" -"@cspell/cspell-json-reporter@8.15.7": - version "8.15.7" - resolved "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-8.15.7.tgz#3c87ba514ce9917c8ee3e6819748dab3b06b2a34" - integrity sha512-kOcJaThztX1A8jkALBiAyp8dWrHPMuDOXki8Q5ZG1dL25wKsAnPyqflXhXg6Up4VGVIkkgy1S3T0Q/i+G5f12w== +"@cspell/cspell-json-reporter@8.16.0": + version "8.16.0" + resolved "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-8.16.0.tgz#d83f09a8ba4467db22c0cda4ca5ba20a3e20cf00" + integrity sha512-KLjPK94gA3JNuWy70LeenJ6EL3SFk2ejERKYJ6SVV/cVOKIvVd2qe42yX3/A/DkF2xzuZ2LD4z0sfoqQL1BaqA== dependencies: - "@cspell/cspell-types" "8.15.7" + "@cspell/cspell-types" "8.16.0" -"@cspell/cspell-pipe@8.15.7": - version "8.15.7" - resolved "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-8.15.7.tgz#2971c1d27daff0cb127fa51d1bf24e41b0be829c" - integrity sha512-EyVSJPqJFrDA9Sj4Nzx13vytloMS0V3HaevhqMFLHJ53QNz/ZP7vuECbXApRAJwLonuToJBvY3b9xzB6eEhU/A== +"@cspell/cspell-pipe@8.16.0": + version "8.16.0" + resolved "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-8.16.0.tgz#368dc5de4ea4807ec109962045de1f7ad9c0dbc3" + integrity sha512-WoCgrv/mrtwCY4lhc6vEcqN3AQ7lT6K0NW5ShoSo116U2tRaW0unApIYH4Va8u7T9g3wyspFEceQRR1xD9qb9w== -"@cspell/cspell-resolver@8.15.7": - version "8.15.7" - resolved "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-8.15.7.tgz#f513b84862c84ef61e88fed548fcf120bb7ae112" - integrity sha512-9RPZ5VwjYPYLTLWkoPGHqV3Kuai5QwTWgZJW3Yk2GgJkxss/LDsXME+9CalPcPBQpnCIBEOtE2DjDQbFjAiMnA== +"@cspell/cspell-resolver@8.16.0": + version "8.16.0" + resolved "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-8.16.0.tgz#0360270e7beb17dcb7dfbff6af7ed310e1aacdfc" + integrity sha512-b+99bph43ptkXlQHgPXSkN/jK6LQHy2zL1Fm9up7+x6Yr64bxAzWzoeqJAPtnrPvFuOrFN0jZasZzKBw8CvrrQ== dependencies: global-directory "^4.0.1" -"@cspell/cspell-service-bus@8.15.7": - version "8.15.7" - resolved "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-8.15.7.tgz#fcc343d6c0d8e56736cc58c9a839950d219d8744" - integrity sha512-kL+1+K4VApdwZccGlg7Vjmh4CzzjoT+G556/gErdESQFPY0y9/4OPPVKLrFkbEDODtp9Py7aTRHdhl6w1xxCCw== +"@cspell/cspell-service-bus@8.16.0": + version "8.16.0" + resolved "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-8.16.0.tgz#f55dec069b2ed9f2017c6b687fe6288ed473097c" + integrity sha512-+fn763JKA4EYCOv+1VShFq015UMEBAFRDr+rlCnesgLE0fv9TSFVLsjOfh9/g6GuGQLCRLUqKztwwuueeErstQ== -"@cspell/cspell-types@8.15.7": - version "8.15.7" - resolved "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-8.15.7.tgz#a9e629aae2d81531300af4ea226395ece7f62e7f" - integrity sha512-QMbGJsUXTdV8/V9Gsc/kBgdkBwm4hvcChhQf6KE9yeg3CZlbd95NkFJuSiqp1phJOWMTzHCZ0Ro7v7P/LGKdVA== +"@cspell/cspell-types@8.16.0": + version "8.16.0" + resolved "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-8.16.0.tgz#22b641512eb6b33b79b2940d1273df0bb5478ba8" + integrity sha512-bGrIK7p4NVsK+QX/CYWmjax+FkzfSIZaIaoiBESGV5gmwgXDVRMJ3IP6tQVAmTtckOYHCmtT5CZgI8zXWr8dHQ== "@cspell/dict-ada@^4.0.5": version "4.0.5" @@ -423,10 +423,10 @@ resolved "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-3.1.7.tgz#c9abd6f5293f103062f54dde01f2bee939189f79" integrity sha512-ncVs/efuAkP1/tLDhWbXukBjgZ5xOUfe03neHMWsE8zvXXc5+Lw6TX5jaJXZLOoES/f4j4AhRE20jsPCF5pm+A== -"@cspell/dict-cpp@^6.0.0": - version "6.0.1" - resolved "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-6.0.1.tgz#f6e7f04c5e8ad23f45a322feef34bc782a17ca88" - integrity sha512-AxMC1KVu/9JSme1eG1SPQQTSLQbGUpoICMdKjQEEaB4RyrEev2V6fcVnqH38lzs+zN5Dffh04B2bTW0pT4lr9g== +"@cspell/dict-cpp@^6.0.1": + version "6.0.2" + resolved "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-6.0.2.tgz#e4549ee1bdf4b6402c0b978eb9dd3deac0eb05df" + integrity sha512-yw5eejWvY4bAnc6LUA44m4WsFwlmgPt2uMSnO7QViGMBDuoeopMma4z9XYvs4lSjTi8fIJs/A1YDfM9AVzb8eg== "@cspell/dict-cryptocurrencies@^5.0.3": version "5.0.3" @@ -593,15 +593,15 @@ resolved "https://registry.npmjs.org/@cspell/dict-monkeyc/-/dict-monkeyc-1.0.9.tgz#58b5f6f15fc7c11ce0eeffd0742fba4b39fc0b8b" integrity sha512-Jvf6g5xlB4+za3ThvenYKREXTEgzx5gMUSzrAxIiPleVG4hmRb/GBSoSjtkGaibN3XxGx5x809gSTYCA/IHCpA== -"@cspell/dict-node@^5.0.4": - version "5.0.4" - resolved "https://registry.npmjs.org/@cspell/dict-node/-/dict-node-5.0.4.tgz#dfe1f159a1ffb1c4f389ec43b15f705123113658" - integrity sha512-Hz5hiuOvZTd7Cp1IBqUZ7/ChwJeQpD5BJuwCaDn4mPNq4iMcQ1iWBYMThvNVqCEDgKv63X52nT8RAWacss98qg== +"@cspell/dict-node@^5.0.5": + version "5.0.5" + resolved "https://registry.npmjs.org/@cspell/dict-node/-/dict-node-5.0.5.tgz#11653612ebdd833208432e8b3cbe61bd6dd35dc3" + integrity sha512-7NbCS2E8ZZRZwlLrh2sA0vAk9n1kcTUiRp/Nia8YvKaItGXLfxYqD2rMQ3HpB1kEutal6hQLVic3N2Yi1X7AaA== -"@cspell/dict-npm@^5.1.9": - version "5.1.10" - resolved "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.1.10.tgz#274d5eb3a44d1017771fe7768aabeaf7e46764d5" - integrity sha512-YmOLvM3ERk/yrdk0s0HhMI7Ws4epLRycGQH5uWyvWg5F64C31mbV557+jfxjrn6Ewq3UdT4ILCS9EyCHVyirig== +"@cspell/dict-npm@^5.1.11": + version "5.1.11" + resolved "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.1.11.tgz#fbaef533d7c25ad3a01f8cd823624a74f6cc778c" + integrity sha512-5ricJyVMw5TmqR0NfsZS8jEJu1+DLzyUXyjpVFnffPuEtz9jF2XswLK0swZqc9uwWrz0M7IhGVCnmq90srVZCA== "@cspell/dict-php@^4.0.13": version "4.0.13" @@ -645,10 +645,10 @@ resolved "https://registry.npmjs.org/@cspell/dict-scala/-/dict-scala-5.0.6.tgz#5e925def2fe6dc27ee2ad1c452941c3d6790fb6d" integrity sha512-tl0YWAfjUVb4LyyE4JIMVE8DlLzb1ecHRmIWc4eT6nkyDqQgHKzdHsnusxFEFMVLIQomgSg0Zz6hJ5S1E4W4ww== -"@cspell/dict-software-terms@^4.1.12": - version "4.1.13" - resolved "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-4.1.13.tgz#7c9116e155d69eb7e732de2bc484d9496bbffd6d" - integrity sha512-S8TLDjY+piV8nmzn4/acwKjDbUtOqfJ9Cb3gZ9egjU/Fm8DBaQ7ziR1S2wntxlGLud9cly+LoWnEoWJYDZFveQ== +"@cspell/dict-software-terms@^4.1.13": + version "4.1.14" + resolved "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-4.1.14.tgz#19e159ae854e5dbabedc0ec823d05153b77b8940" + integrity sha512-p3oZQSQTgdu3UjZ5aaEeU5aKRD00j/oZzt51ohbhhJ94UYECi8te8SfcA45UbGkylSSGcAtJWkuwjCLMiKAgyQ== "@cspell/dict-sql@^2.1.8": version "2.1.8" @@ -680,27 +680,27 @@ resolved "https://registry.npmjs.org/@cspell/dict-vue/-/dict-vue-3.0.3.tgz#295c288f6fd363879898223202ec3be048663b98" integrity sha512-akmYbrgAGumqk1xXALtDJcEcOMYBYMnkjpmGzH13Ozhq1mkPF4VgllFQlm1xYde+BUKNnzMgPEzxrL2qZllgYA== -"@cspell/dynamic-import@8.15.7": - version "8.15.7" - resolved "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-8.15.7.tgz#285e1860e6b8c911f75d97163fa37b90d97b288b" - integrity sha512-qFlVisdP2lvFcS4Kre4Dl+f4Y7U9w/Y7IQAS+XXl5KlInImMaYhNUDEru8DoUPQHYsXKAPJsu/Y2JloHNE502Q== +"@cspell/dynamic-import@8.16.0": + version "8.16.0" + resolved "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-8.16.0.tgz#bcb785335fe6f22a5cf66780a5ade61483da7f32" + integrity sha512-FH+B5y71qfunagXiLSJhXP9h/Vwb1Z8Cc/hLmliGekw/Y8BuYknL86tMg9grXBYNmM0kifIv6ZesQl8Km/p/rA== dependencies: import-meta-resolve "^4.1.0" -"@cspell/filetypes@8.15.7": - version "8.15.7" - resolved "https://registry.npmjs.org/@cspell/filetypes/-/filetypes-8.15.7.tgz#b74866c84c4d08c63b5fae8c62142f0c0f82fb91" - integrity sha512-MeP6gh8Om9vHSxYoYey2BFCib4m+vEyMLQCSub1Gk+uXiJjj1l/S5MFWM9zHhjGBBNNdvuopuUKP6Gcgcw+3Cw== +"@cspell/filetypes@8.16.0": + version "8.16.0" + resolved "https://registry.npmjs.org/@cspell/filetypes/-/filetypes-8.16.0.tgz#7b1163946cd115a17f382d6332a6afed9bc23734" + integrity sha512-u2Ub0uSwXFPJFvXhAO/0FZBj3sMr4CeYCiQwTUsdFRkRMFpbTc7Vf+a+aC2vIj6WcaWrYXrJy3NZF/yjqF6SGw== -"@cspell/strong-weak-map@8.15.7": - version "8.15.7" - resolved "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-8.15.7.tgz#656b3a311a0ace90b4f77505db7d2ca4a2f16700" - integrity sha512-SGlkhRnHXoBzLY2SxVppMsREhyaDHpyXQrPDUfsCnyG5DC8UVmXnTVQp9c2kqhAZw6g6g6V7uoqTLqJQmrWOFQ== +"@cspell/strong-weak-map@8.16.0": + version "8.16.0" + resolved "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-8.16.0.tgz#608ec5036d2c4e6086823eed7581bbaf764781f4" + integrity sha512-R6N12wEIQpBk2uyni/FU1SFSIjP0uql7ynXVcF1ob8/JJeRoikssydi9Xq5J6ghMw+X50u35mFvg9BgWKz0d+g== -"@cspell/url@8.15.7": - version "8.15.7" - resolved "https://registry.npmjs.org/@cspell/url/-/url-8.15.7.tgz#152aa1caf47e6995427c6f1052f4b019a1f4b6c9" - integrity sha512-IzBsrl54TyO5Ezbyr25ZOUZA3Sg2UbSWDZZar9jSRAsoikcsoy1ivgSumcYJYOH8HAtanfr8YGN0+8UF/kbYqg== +"@cspell/url@8.16.0": + version "8.16.0" + resolved "https://registry.npmjs.org/@cspell/url/-/url-8.16.0.tgz#1c426dd19b52bc9b5f700ff28a0221575256340a" + integrity sha512-zW+6hAieD/FjysfjY4mVv7iHWWasBP3ldj6L+xy2p4Kuax1nug7uuJqMHlAVude/OywNwENG0rYaP/P9Pg4O+w== "@cspotcode/source-map-support@^0.8.0": version "0.8.1" @@ -2262,80 +2262,80 @@ cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -cspell-config-lib@8.15.7: - version "8.15.7" - resolved "https://registry.npmjs.org/cspell-config-lib/-/cspell-config-lib-8.15.7.tgz#1e7f7820327f58585c1dd77a57eb08447b066094" - integrity sha512-orxPKLMLQjk+Px1wlZdMElsHlKFGiwlXhQcG/36hODFKsle9DnGqVefOjH6aWFO5DyDF+Z678leiO2F30wtXEQ== +cspell-config-lib@8.16.0: + version "8.16.0" + resolved "https://registry.npmjs.org/cspell-config-lib/-/cspell-config-lib-8.16.0.tgz#3da31b827786f83dd584e55df9e7ef40f32ccf25" + integrity sha512-PGT6ohLtIYXYLIm+R5hTcTrF0dzj8e7WAUJSJe5WlV/7lrwVdwgWaliLcXtSSPmfxgczr6sndX9TMJ2IEmPrmg== dependencies: - "@cspell/cspell-types" "8.15.7" + "@cspell/cspell-types" "8.16.0" comment-json "^4.2.5" yaml "^2.6.0" -cspell-dictionary@8.15.7: - version "8.15.7" - resolved "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-8.15.7.tgz#9aaf8c132cbbcb25a8e7170582caf0b7d470e1e3" - integrity sha512-jmOk9kJ/bsVFg0/ObnNMUHA3wPgHb4eGFx6yF+Lx28eYx9j2XkLZuXXicbNyOWqJ9AzP0CavPmHwAS6bJrxD3Q== +cspell-dictionary@8.16.0: + version "8.16.0" + resolved "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-8.16.0.tgz#11bdf41702432ef8bcdb57c7181a85f3cc069355" + integrity sha512-Y3sN6ttLBKbu0dOLcduY641n5QP1srUvZkW4bOTnG455DbIZfilrP1El/2Hl0RS6hC8LN9PM4bsIm/2xgdbApA== dependencies: - "@cspell/cspell-pipe" "8.15.7" - "@cspell/cspell-types" "8.15.7" - cspell-trie-lib "8.15.7" + "@cspell/cspell-pipe" "8.16.0" + "@cspell/cspell-types" "8.16.0" + cspell-trie-lib "8.16.0" fast-equals "^5.0.1" -cspell-gitignore@8.15.7: - version "8.15.7" - resolved "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-8.15.7.tgz#cbff91ec234f502db9e5c88b094b0267e8078840" - integrity sha512-LxX/PS3z6UqXHUqw3wIB1OJwZrexxKn/EUertYcLce/K2M2wLsUA+uneU5EvUqzkM6vwMHvdv/hl/tROFQJIbw== +cspell-gitignore@8.16.0: + version "8.16.0" + resolved "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-8.16.0.tgz#fd64d18a3101705b9cf9b4fd5b35522c1a75e071" + integrity sha512-ODKe0ooyzYSBJkwgIVZSRIvzoZfT4tEbFt4fFDT88wPyyfX7xp7MAQhXy5KD1ocXH0WvYbdv37qzn2UbckrahA== dependencies: - "@cspell/url" "8.15.7" - cspell-glob "8.15.7" - cspell-io "8.15.7" + "@cspell/url" "8.16.0" + cspell-glob "8.16.0" + cspell-io "8.16.0" find-up-simple "^1.0.0" -cspell-glob@8.15.7: - version "8.15.7" - resolved "https://registry.npmjs.org/cspell-glob/-/cspell-glob-8.15.7.tgz#a0ff17bfc93108b5ef45cc0515730aa7fe363e14" - integrity sha512-BI0mF0IWqVxEGpRkH2kBgT9Ey7lAMlEhvY/zKCy3JQY5PSn/qI3RhlsXrsTDt2RJxhicuzJIe3KiNdUKXQM0Ig== +cspell-glob@8.16.0: + version "8.16.0" + resolved "https://registry.npmjs.org/cspell-glob/-/cspell-glob-8.16.0.tgz#4f08edf54fcdaa71dc8352416960aad5f884a42f" + integrity sha512-xJSXRHwfENCNFmjpVSEucXY8E3BrpSCA+TukmOYtLyaMKtn6EAwoCpEU7Oj2tZOjdivprPmQ74k4Dqb1RHjIVQ== dependencies: - "@cspell/url" "8.15.7" + "@cspell/url" "8.16.0" micromatch "^4.0.8" -cspell-grammar@8.15.7: - version "8.15.7" - resolved "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-8.15.7.tgz#6904ca220d2ad15c0e656508a1599fed28a01aef" - integrity sha512-g7ocpFG9Gam4+b2bHrqhmXVaFNV4BjFjVnaEKS3RoqcMjJuQUa9wD5HWO6AvBJeJf/auvQS7CgmumQqSo9xxCw== - dependencies: - "@cspell/cspell-pipe" "8.15.7" - "@cspell/cspell-types" "8.15.7" - -cspell-io@8.15.7: - version "8.15.7" - resolved "https://registry.npmjs.org/cspell-io/-/cspell-io-8.15.7.tgz#709aeae1061087120d6c2030971db2b431816a0f" - integrity sha512-GEnMPu+xyyHTal2QdCbuRrPUEpjCYo0mF/Tz/YkcZNJdn0sj6MylH2EA0A+d6WzheRpw9ijd1dRvq3h5jJgmuQ== - dependencies: - "@cspell/cspell-service-bus" "8.15.7" - "@cspell/url" "8.15.7" - -cspell-lib@8.15.7: - version "8.15.7" - resolved "https://registry.npmjs.org/cspell-lib/-/cspell-lib-8.15.7.tgz#afe0e0815b912ae9124912c63793ff3dc9138f03" - integrity sha512-RxxPEymTENc76f8ny1LN+aPyR4Efwyah8m5c20xOwxD/lAhBrNs2PPE1taEMPkI7WTXWjKm4D0DJpZatD+W6pg== - dependencies: - "@cspell/cspell-bundled-dicts" "8.15.7" - "@cspell/cspell-pipe" "8.15.7" - "@cspell/cspell-resolver" "8.15.7" - "@cspell/cspell-types" "8.15.7" - "@cspell/dynamic-import" "8.15.7" - "@cspell/filetypes" "8.15.7" - "@cspell/strong-weak-map" "8.15.7" - "@cspell/url" "8.15.7" +cspell-grammar@8.16.0: + version "8.16.0" + resolved "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-8.16.0.tgz#8be03e617f559627fd39e82dd7812636a054efd7" + integrity sha512-vvbJEkBqXocGH/H975RtkfMzVpNxNGMd0JCDd+NjbpeRyZceuChFw5Tie7kHteFY29SwZovub+Am3F4H1kmf9A== + dependencies: + "@cspell/cspell-pipe" "8.16.0" + "@cspell/cspell-types" "8.16.0" + +cspell-io@8.16.0: + version "8.16.0" + resolved "https://registry.npmjs.org/cspell-io/-/cspell-io-8.16.0.tgz#f32c60a2d272a88883a7c883be1e3d8b7be8fe64" + integrity sha512-WIK5uhPMjGsTAzm2/fGRbIdr7zWsMVG1fn8wNJYUiYELuyvzvLelfI1VG6szaFCGYqd6Uvgb/fS0uNbwGqCLAQ== + dependencies: + "@cspell/cspell-service-bus" "8.16.0" + "@cspell/url" "8.16.0" + +cspell-lib@8.16.0: + version "8.16.0" + resolved "https://registry.npmjs.org/cspell-lib/-/cspell-lib-8.16.0.tgz#26824f7c096fba1cd1672b50696053f03da342eb" + integrity sha512-fU8CfECyuhT12COIi4ViQu2bTkdqaa+05YSd2ZV8k8NA7lapPaMFnlooxdfcwwgZJfHeMhRVMzvQF1OhWmwGfA== + dependencies: + "@cspell/cspell-bundled-dicts" "8.16.0" + "@cspell/cspell-pipe" "8.16.0" + "@cspell/cspell-resolver" "8.16.0" + "@cspell/cspell-types" "8.16.0" + "@cspell/dynamic-import" "8.16.0" + "@cspell/filetypes" "8.16.0" + "@cspell/strong-weak-map" "8.16.0" + "@cspell/url" "8.16.0" clear-module "^4.1.2" comment-json "^4.2.5" - cspell-config-lib "8.15.7" - cspell-dictionary "8.15.7" - cspell-glob "8.15.7" - cspell-grammar "8.15.7" - cspell-io "8.15.7" - cspell-trie-lib "8.15.7" + cspell-config-lib "8.16.0" + cspell-dictionary "8.16.0" + cspell-glob "8.16.0" + cspell-grammar "8.16.0" + cspell-io "8.16.0" + cspell-trie-lib "8.16.0" env-paths "^3.0.0" fast-equals "^5.0.1" gensequence "^7.0.0" @@ -2345,33 +2345,33 @@ cspell-lib@8.15.7: vscode-uri "^3.0.8" xdg-basedir "^5.1.0" -cspell-trie-lib@8.15.7: - version "8.15.7" - resolved "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-8.15.7.tgz#241452831da22503379576f674aa6bfa9002ef85" - integrity sha512-b8WWWOx5wfhT72K43fk3dMoE4H2c1UpbCEVB2JXJ5scub7mjqoT/CRiZlw1IDfQT6BmUtJuD7sZ8NRFanF9hQA== +cspell-trie-lib@8.16.0: + version "8.16.0" + resolved "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-8.16.0.tgz#47c1a9c521c3c3cabcdc8567cbd6018d29728bf1" + integrity sha512-Io1qqI0r4U9ewAWBLClFBBlxLeAoIi15PUGJi4Za1xrlgQJwRE8PMNIJNHKmPEIp78Iute3o/JyC2OfWlxl4Sw== dependencies: - "@cspell/cspell-pipe" "8.15.7" - "@cspell/cspell-types" "8.15.7" + "@cspell/cspell-pipe" "8.16.0" + "@cspell/cspell-types" "8.16.0" gensequence "^7.0.0" cspell@^8.8.3: - version "8.15.7" - resolved "https://registry.npmjs.org/cspell/-/cspell-8.15.7.tgz#367a04ac579e6b67fc5eb451bf466f9f134345dc" - integrity sha512-68Bs/brr31M0W6tljNCgHcz09xdfDnRobyyRQJ8z0ZrovfTHHj9gSQldJJt5Fq3AMlCeYbECnKPsY9DkzIP1sQ== - dependencies: - "@cspell/cspell-json-reporter" "8.15.7" - "@cspell/cspell-pipe" "8.15.7" - "@cspell/cspell-types" "8.15.7" - "@cspell/dynamic-import" "8.15.7" - "@cspell/url" "8.15.7" + version "8.16.0" + resolved "https://registry.npmjs.org/cspell/-/cspell-8.16.0.tgz#1897d123f8854304bc84ac332590c730e93c5123" + integrity sha512-U6Up/4nODE+Ca+zqwZXTgBioGuF2JQHLEUIuoRJkJzAZkIBYDqrMXM+zdSL9E39+xb9jAtr9kPAYJf1Eybgi9g== + dependencies: + "@cspell/cspell-json-reporter" "8.16.0" + "@cspell/cspell-pipe" "8.16.0" + "@cspell/cspell-types" "8.16.0" + "@cspell/dynamic-import" "8.16.0" + "@cspell/url" "8.16.0" chalk "^5.3.0" chalk-template "^1.1.0" commander "^12.1.0" - cspell-dictionary "8.15.7" - cspell-gitignore "8.15.7" - cspell-glob "8.15.7" - cspell-io "8.15.7" - cspell-lib "8.15.7" + cspell-dictionary "8.16.0" + cspell-gitignore "8.16.0" + cspell-glob "8.16.0" + cspell-io "8.16.0" + cspell-lib "8.16.0" fast-json-stable-stringify "^2.1.0" file-entry-cache "^9.1.0" get-stdin "^9.0.0" From 5f4fae61e95bc7841db6bfc84efa754af3b250df Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Wed, 13 Nov 2024 17:22:50 +0100 Subject: [PATCH 14/24] feat(docs): `Slice.preloadRef()` (#1044) --- CHANGELOG.md | 3 +- docs/src/content/docs/ref/core-cells.mdx | 40 +++++++++++++-- src/imports/stdlib.ts | 63 ++++++++++++++---------- stdlib/std/cells.tact | 25 ++++++++++ 4 files changed, 98 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5bd192f7..7c627ad11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The `SendDefaultMode` send mode constant to the standard library: PR [#1010](https://github.com/tact-lang/tact/pull/1010) - Docs: initial semi-automated Chinese translation of the documentation: PR [#942](https://github.com/tact-lang/tact/pull/942) - The `replace` and `replaceGet` methods for the `Map` type: PR [#941](https://github.com/tact-lang/tact/pull/941) -- Utility for logging errors in code that was supposed to be unreachable [#991](https://github.com/tact-lang/tact/pull/991) +- Utility for logging errors in code that was supposed to be unreachable: PR [#991](https://github.com/tact-lang/tact/pull/991) +- Docs: `preloadRef` method for the `Slice` type: PR [#1044](https://github.com/tact-lang/tact/pull/1044) ### Changed diff --git a/docs/src/content/docs/ref/core-cells.mdx b/docs/src/content/docs/ref/core-cells.mdx index f9bd67e71..f29d38650 100644 --- a/docs/src/content/docs/ref/core-cells.mdx +++ b/docs/src/content/docs/ref/core-cells.mdx @@ -634,15 +634,45 @@ Attempts to load more data than [`Slice{:tact}`][slice] contains throw an except Usage examples: ```tact -let s: Slice = beginCell().storeRef(emptyCell()).asSlice(); -let fizz: Cell = s.loadRef(); +let s1: Slice = beginCell().storeRef(emptyCell()).asSlice(); +let fizz: Cell = s1.loadRef(); -let s: Slice = beginCell() +let s2: Slice = beginCell() .storeRef(emptyCell()) + .storeRef(s1.asCell()) + .asSlice(); +let ref1: Cell = s2.loadRef(); +let ref2: Cell = s2.loadRef(); +ref1 == ref2; // false +``` + +## Slice.preloadRef + +```tact +extends fun preloadRef(self: Slice): Cell; +``` + +Extension function for the [`Slice{:tact}`][slice]. + +Preloads the next reference from the [`Slice{:tact}`][slice] as a [`Cell{:tact}`][cell]. Doesn't modify the original [`Slice{:tact}`][slice]. + +Attempts to preload such reference [`Cell{:tact}`][cell] when [`Slice{:tact}`][slice] doesn't contain it throw an exception with [exit code 8](/book/exit-codes#8): `Cell overflow`. + +Attempts to preload more data than [`Slice{:tact}`][slice] contains throw an exception with [exit code 9](/book/exit-codes#9): `Cell underflow`. + +Usage examples: + +```tact +let s1: Slice = beginCell().storeRef(emptyCell()).asSlice(); +let fizz: Cell = s1.preloadRef(); // didn't modify s1 + +let s2: Slice = beginCell() .storeRef(emptyCell()) + .storeRef(s1.asCell()) .asSlice(); -let ref1: Cell = s.loadRef(); -let ref2: Cell = s.loadRef(); +let ref1: Cell = s2.preloadRef(); +let ref2: Cell = s2.preloadRef(); +ref1 == ref2; // true ``` ## Slice.refs diff --git a/src/imports/stdlib.ts b/src/imports/stdlib.ts index 516eacf2e..a79113eed 100644 --- a/src/imports/stdlib.ts +++ b/src/imports/stdlib.ts @@ -148,33 +148,42 @@ files['std/cells.tact'] = 'T1BUUkVGIH0KCmFzbSBleHRlbmRzIGZ1biBlbmRDZWxsKHNlbGY6IEJ1aWxkZXIpOiBDZWxsIHsgRU5EQyB9Cgphc20gZXh0ZW5kcyBmdW4gcmVmcyhzZWxmOiBCdWls' + 'ZGVyKTogSW50IHsgQlJFRlMgfQoKYXNtIGV4dGVuZHMgZnVuIGJpdHMoc2VsZjogQnVpbGRlcik6IEludCB7IEJCSVRTIH0KCi8vCi8vIFNsaWNlCi8vCgphc20gZXh0' + 'ZW5kcyBmdW4gYmVnaW5QYXJzZShzZWxmOiBDZWxsKTogU2xpY2UgeyBDVE9TIH0KCmFzbSgtPiAxIDApIGV4dGVuZHMgbXV0YXRlcyBmdW4gbG9hZFJlZihzZWxmOiBT' + - 'bGljZSk6IENlbGwgeyBMRFJFRiB9Cgphc20gZXh0ZW5kcyBmdW4gcHJlbG9hZFJlZihzZWxmOiBTbGljZSk6IENlbGwgeyBQTERSRUYgfQoKLy8gc3BlY2lhbCB0cmVh' + - 'dG1lbnQgaW4gRnVuYyBjb21waWxlciwgc28gbm90IHJlcGxhY2VkIHdpdGggYXNtICJMRFNMSUNFWCIKQG5hbWUobG9hZF9iaXRzKQpleHRlbmRzIG11dGF0ZXMgbmF0' + - 'aXZlIGxvYWRCaXRzKHNlbGY6IFNsaWNlLCBsOiBJbnQpOiBTbGljZTsKCi8vIHNwZWNpYWwgdHJlYXRtZW50IGluIEZ1bmMgY29tcGlsZXIsIHNvIG5vdCByZXBsYWNl' + - 'ZCB3aXRoIGFzbSAiUExEU0xJQ0VYIgpAbmFtZShwcmVsb2FkX2JpdHMpCmV4dGVuZHMgbmF0aXZlIHByZWxvYWRCaXRzKHNlbGY6IFNsaWNlLCBsOiBJbnQpOiBTbGlj' + - 'ZTsKCi8vIHNwZWNpYWwgdHJlYXRtZW50IGluIEZ1bmMgY29tcGlsZXIsIHNvIG5vdCByZXBsYWNlZCB3aXRoIGFzbSAiTERJWCIKQG5hbWUobG9hZF9pbnQpCmV4dGVu' + - 'ZHMgbXV0YXRlcyBuYXRpdmUgbG9hZEludChzZWxmOiBTbGljZSwgbDogSW50KTogSW50OwoKLy8gc3BlY2lhbCB0cmVhdG1lbnQgaW4gRnVuYyBjb21waWxlciwgc28g' + - 'bm90IHJlcGxhY2VkIHdpdGggYXNtICJQTERJWCIKQG5hbWUocHJlbG9hZF9pbnQpCmV4dGVuZHMgbmF0aXZlIHByZWxvYWRJbnQoc2VsZjogU2xpY2UsIGw6IEludCk6' + - 'IEludDsKCi8vIHNwZWNpYWwgdHJlYXRtZW50IGluIEZ1bmMgY29tcGlsZXIsIHNvIG5vdCByZXBsYWNlZCB3aXRoIGFzbSAiTERVWCIKQG5hbWUobG9hZF91aW50KQpl' + - 'eHRlbmRzIG11dGF0ZXMgbmF0aXZlIGxvYWRVaW50KHNlbGY6IFNsaWNlLCBsOiBJbnQpOiBJbnQ7CgovLyBzcGVjaWFsIHRyZWF0bWVudCBpbiBGdW5jIGNvbXBpbGVy' + - 'LCBzbyBub3QgcmVwbGFjZWQgd2l0aCBhc20gIlBMRFVYIgpAbmFtZShwcmVsb2FkX3VpbnQpCmV4dGVuZHMgbmF0aXZlIHByZWxvYWRVaW50KHNlbGY6IFNsaWNlLCBs' + - 'OiBJbnQpOiBJbnQ7Cgphc20oLT4gMSAwKSBleHRlbmRzIG11dGF0ZXMgZnVuIGxvYWRCb29sKHNlbGY6IFNsaWNlKTogQm9vbCB7IDEgTERJIH0KCi8vLyBFeHRlbnNp' + - 'b24gbXV0YXRpb24gZnVuY3Rpb24gZm9yIHRoZSBgU2xpY2VgLiBBbGlhcyB0byBgU2xpY2UubG9hZEJvb2woKWAuCi8vLwovLy8gYGBgdGFjdAovLy8gbGV0IHM6IFNs' + - 'aWNlID0gYmVnaW5DZWxsKCkuc3RvcmVCb29sKHRydWUpLmFzU2xpY2UoKTsKLy8vIGxldCBmaXp6OiBCb29sID0gcy5sb2FkQml0KCk7IC8vIHRydWUKLy8vIGBgYAov' + - 'Ly8KLy8vIEBzaW5jZSBUYWN0IDEuNS4wCi8vLyBAc2VlIGh0dHBzOi8vZG9jcy50YWN0LWxhbmcub3JnL3JlZi9jb3JlLWNlbGxzI3NsaWNlbG9hZGJpdAovLy8KYXNt' + - 'KC0+IDEgMCkgZXh0ZW5kcyBtdXRhdGVzIGZ1biBsb2FkQml0KHNlbGY6IFNsaWNlKTogQm9vbCB7IDEgTERJIH0KCmFzbSggLT4gMSAwKSBleHRlbmRzIG11dGF0ZXMg' + - 'ZnVuIGxvYWRDb2lucyhzZWxmOiBTbGljZSk6IEludCB7IExEVkFSVUlOVDE2IH0KCkBuYW1lKF9fdGFjdF9sb2FkX2FkZHJlc3MpCmV4dGVuZHMgbXV0YXRlcyBuYXRp' + - 'dmUgbG9hZEFkZHJlc3Moc2VsZjogU2xpY2UpOiBBZGRyZXNzOwoKYXNtIGV4dGVuZHMgbXV0YXRlcyBmdW4gc2tpcEJpdHMoc2VsZjogU2xpY2UsIGw6IEludCkgeyBT' + - 'RFNLSVBGSVJTVCB9Cgphc20gZXh0ZW5kcyBmdW4gZW5kUGFyc2Uoc2VsZjogU2xpY2UpIHsgRU5EUyB9CgovLwovLyBTbGljZSBzaXplCi8vCgphc20gZXh0ZW5kcyBm' + - 'dW4gcmVmcyhzZWxmOiBTbGljZSk6IEludCB7IFNSRUZTIH0KCmFzbSBleHRlbmRzIGZ1biBiaXRzKHNlbGY6IFNsaWNlKTogSW50IHsgU0JJVFMgfQoKYXNtIGV4dGVu' + - 'ZHMgZnVuIGVtcHR5KHNlbGY6IFNsaWNlKTogQm9vbCB7IFNFTVBUWSB9Cgphc20gZXh0ZW5kcyBmdW4gZGF0YUVtcHR5KHNlbGY6IFNsaWNlKTogQm9vbCB7IFNERU1Q' + - 'VFkgfQoKYXNtIGV4dGVuZHMgZnVuIHJlZnNFbXB0eShzZWxmOiBTbGljZSk6IEJvb2wgeyBTUkVNUFRZIH0KCi8vCi8vIENvbnZlcnNpb25zCi8vCgppbmxpbmUgZXh0' + - 'ZW5kcyBmdW4gYXNTbGljZShzZWxmOiBCdWlsZGVyKTogU2xpY2UgewogICAgcmV0dXJuIHNlbGYuZW5kQ2VsbCgpLmJlZ2luUGFyc2UoKTsKfQoKaW5saW5lIGV4dGVu' + - 'ZHMgZnVuIGFzU2xpY2Uoc2VsZjogQ2VsbCk6IFNsaWNlIHsKICAgIHJldHVybiBzZWxmLmJlZ2luUGFyc2UoKTsKfQoKaW5saW5lIGV4dGVuZHMgZnVuIGFzQ2VsbChz' + - 'ZWxmOiBTbGljZSk6IENlbGwgewogICAgcmV0dXJuIGJlZ2luQ2VsbCgpCiAgICAgICAgLnN0b3JlU2xpY2Uoc2VsZikKICAgICAgICAuZW5kQ2VsbCgpOwp9Cgppbmxp' + - 'bmUgZXh0ZW5kcyBmdW4gYXNDZWxsKHNlbGY6IEJ1aWxkZXIpOiBDZWxsIHsKICAgIHJldHVybiBzZWxmLmVuZENlbGwoKTsKfQoKaW5saW5lIGZ1biBlbXB0eUNlbGwo' + - 'KTogQ2VsbCB7CiAgICByZXR1cm4gYmVnaW5DZWxsKCkuZW5kQ2VsbCgpOwp9CgppbmxpbmUgZnVuIGVtcHR5U2xpY2UoKTogU2xpY2UgewogICAgcmV0dXJuIGVtcHR5' + - 'Q2VsbCgpLmFzU2xpY2UoKTsKfQo='; + 'bGljZSk6IENlbGwgeyBMRFJFRiB9CgovLy8gRXh0ZW5zaW9uIGZ1bmN0aW9uIGZvciB0aGUgYFNsaWNlYC4gQXZhaWxhYmxlIHNpbmNlIFRhY3QgMS41LjAuCi8vLwov' + + 'Ly8gUHJlbG9hZHMgdGhlIG5leHQgcmVmZXJlbmNlIGZyb20gdGhlIGBTbGljZWAgYXMgYSBgQ2VsbGAuIERvZXNuJ3QgbW9kaWZ5IHRoZSBvcmlnaW5hbCBgU2xpY2Vg' + + 'LgovLy8KLy8vIEF0dGVtcHRzIHRvIHByZWxvYWQgc3VjaCByZWZlcmVuY2UgYENlbGxgIHdoZW4gYFNsaWNlYCBkb2Vzbid0IGNvbnRhaW4gaXQgdGhyb3cgYW4gZXhj' + + 'ZXB0aW9uIHdpdGggZXhpdCBjb2RlIDg6IGBDZWxsIG92ZXJmbG93YC4KLy8vCi8vLyBBdHRlbXB0cyB0byBwcmVsb2FkIG1vcmUgZGF0YSB0aGFuIGBTbGljZWAgY29u' + + 'dGFpbnMgdGhyb3cgYW4gZXhjZXB0aW9uIHdpdGggZXhpdCBjb2RlIDk6IGBDZWxsIHVuZGVyZmxvd2AuCi8vLwovLy8gYGBgdGFjdAovLy8gbGV0IHMxOiBTbGljZSA9' + + 'IGJlZ2luQ2VsbCgpLnN0b3JlUmVmKGVtcHR5Q2VsbCgpKS5hc1NsaWNlKCk7Ci8vLyBsZXQgZml6ejogQ2VsbCA9IHMxLnByZWxvYWRSZWYoKTsgLy8gZGlkbid0IG1v' + + 'ZGlmeSBzMQovLy8KLy8vIGxldCBzMjogU2xpY2UgPSBiZWdpbkNlbGwoKQovLy8gICAgIC5zdG9yZVJlZihlbXB0eUNlbGwoKSkKLy8vICAgICAuc3RvcmVSZWYoczEu' + + 'YXNDZWxsKCkpCi8vLyAgICAgLmFzU2xpY2UoKTsKLy8vIGxldCByZWYxOiBDZWxsID0gczIucHJlbG9hZFJlZigpOwovLy8gbGV0IHJlZjI6IENlbGwgPSBzMi5wcmVs' + + 'b2FkUmVmKCk7Ci8vLyByZWYxID09IHJlZjI7IC8vIHRydWUKLy8vIGBgYAovLy8KLy8vIFNlZToKLy8vICogaHR0cHM6Ly9kb2NzLnRhY3QtbGFuZy5vcmcvcmVmL2Nv' + + 'cmUtY2VsbHMjc2xpY2VwcmVsb2FkcmVmCi8vLyAqIGh0dHBzOi8vZG9jcy50YWN0LWxhbmcub3JnL2Jvb2svZXhpdC1jb2RlcwovLy8KYXNtIGV4dGVuZHMgZnVuIHBy' + + 'ZWxvYWRSZWYoc2VsZjogU2xpY2UpOiBDZWxsIHsgUExEUkVGIH0KCi8vIHNwZWNpYWwgdHJlYXRtZW50IGluIEZ1bmMgY29tcGlsZXIsIHNvIG5vdCByZXBsYWNlZCB3' + + 'aXRoIGFzbSAiTERTTElDRVgiCkBuYW1lKGxvYWRfYml0cykKZXh0ZW5kcyBtdXRhdGVzIG5hdGl2ZSBsb2FkQml0cyhzZWxmOiBTbGljZSwgbDogSW50KTogU2xpY2U7' + + 'CgovLyBzcGVjaWFsIHRyZWF0bWVudCBpbiBGdW5jIGNvbXBpbGVyLCBzbyBub3QgcmVwbGFjZWQgd2l0aCBhc20gIlBMRFNMSUNFWCIKQG5hbWUocHJlbG9hZF9iaXRz' + + 'KQpleHRlbmRzIG5hdGl2ZSBwcmVsb2FkQml0cyhzZWxmOiBTbGljZSwgbDogSW50KTogU2xpY2U7CgovLyBzcGVjaWFsIHRyZWF0bWVudCBpbiBGdW5jIGNvbXBpbGVy' + + 'LCBzbyBub3QgcmVwbGFjZWQgd2l0aCBhc20gIkxESVgiCkBuYW1lKGxvYWRfaW50KQpleHRlbmRzIG11dGF0ZXMgbmF0aXZlIGxvYWRJbnQoc2VsZjogU2xpY2UsIGw6' + + 'IEludCk6IEludDsKCi8vIHNwZWNpYWwgdHJlYXRtZW50IGluIEZ1bmMgY29tcGlsZXIsIHNvIG5vdCByZXBsYWNlZCB3aXRoIGFzbSAiUExESVgiCkBuYW1lKHByZWxv' + + 'YWRfaW50KQpleHRlbmRzIG5hdGl2ZSBwcmVsb2FkSW50KHNlbGY6IFNsaWNlLCBsOiBJbnQpOiBJbnQ7CgovLyBzcGVjaWFsIHRyZWF0bWVudCBpbiBGdW5jIGNvbXBp' + + 'bGVyLCBzbyBub3QgcmVwbGFjZWQgd2l0aCBhc20gIkxEVVgiCkBuYW1lKGxvYWRfdWludCkKZXh0ZW5kcyBtdXRhdGVzIG5hdGl2ZSBsb2FkVWludChzZWxmOiBTbGlj' + + 'ZSwgbDogSW50KTogSW50OwoKLy8gc3BlY2lhbCB0cmVhdG1lbnQgaW4gRnVuYyBjb21waWxlciwgc28gbm90IHJlcGxhY2VkIHdpdGggYXNtICJQTERVWCIKQG5hbWUo' + + 'cHJlbG9hZF91aW50KQpleHRlbmRzIG5hdGl2ZSBwcmVsb2FkVWludChzZWxmOiBTbGljZSwgbDogSW50KTogSW50OwoKYXNtKC0+IDEgMCkgZXh0ZW5kcyBtdXRhdGVz' + + 'IGZ1biBsb2FkQm9vbChzZWxmOiBTbGljZSk6IEJvb2wgeyAxIExESSB9CgovLy8gRXh0ZW5zaW9uIG11dGF0aW9uIGZ1bmN0aW9uIGZvciB0aGUgYFNsaWNlYC4gQWxp' + + 'YXMgdG8gYFNsaWNlLmxvYWRCb29sKClgLgovLy8KLy8vIGBgYHRhY3QKLy8vIGxldCBzOiBTbGljZSA9IGJlZ2luQ2VsbCgpLnN0b3JlQm9vbCh0cnVlKS5hc1NsaWNl' + + 'KCk7Ci8vLyBsZXQgZml6ejogQm9vbCA9IHMubG9hZEJpdCgpOyAvLyB0cnVlCi8vLyBgYGAKLy8vCi8vLyBAc2luY2UgVGFjdCAxLjUuMAovLy8gQHNlZSBodHRwczov' + + 'L2RvY3MudGFjdC1sYW5nLm9yZy9yZWYvY29yZS1jZWxscyNzbGljZWxvYWRiaXQKLy8vCmFzbSgtPiAxIDApIGV4dGVuZHMgbXV0YXRlcyBmdW4gbG9hZEJpdChzZWxm' + + 'OiBTbGljZSk6IEJvb2wgeyAxIExESSB9Cgphc20oIC0+IDEgMCkgZXh0ZW5kcyBtdXRhdGVzIGZ1biBsb2FkQ29pbnMoc2VsZjogU2xpY2UpOiBJbnQgeyBMRFZBUlVJ' + + 'TlQxNiB9CgpAbmFtZShfX3RhY3RfbG9hZF9hZGRyZXNzKQpleHRlbmRzIG11dGF0ZXMgbmF0aXZlIGxvYWRBZGRyZXNzKHNlbGY6IFNsaWNlKTogQWRkcmVzczsKCmFz' + + 'bSBleHRlbmRzIG11dGF0ZXMgZnVuIHNraXBCaXRzKHNlbGY6IFNsaWNlLCBsOiBJbnQpIHsgU0RTS0lQRklSU1QgfQoKYXNtIGV4dGVuZHMgZnVuIGVuZFBhcnNlKHNl' + + 'bGY6IFNsaWNlKSB7IEVORFMgfQoKLy8KLy8gU2xpY2Ugc2l6ZQovLwoKYXNtIGV4dGVuZHMgZnVuIHJlZnMoc2VsZjogU2xpY2UpOiBJbnQgeyBTUkVGUyB9Cgphc20g' + + 'ZXh0ZW5kcyBmdW4gYml0cyhzZWxmOiBTbGljZSk6IEludCB7IFNCSVRTIH0KCmFzbSBleHRlbmRzIGZ1biBlbXB0eShzZWxmOiBTbGljZSk6IEJvb2wgeyBTRU1QVFkg' + + 'fQoKYXNtIGV4dGVuZHMgZnVuIGRhdGFFbXB0eShzZWxmOiBTbGljZSk6IEJvb2wgeyBTREVNUFRZIH0KCmFzbSBleHRlbmRzIGZ1biByZWZzRW1wdHkoc2VsZjogU2xp' + + 'Y2UpOiBCb29sIHsgU1JFTVBUWSB9CgovLwovLyBDb252ZXJzaW9ucwovLwoKaW5saW5lIGV4dGVuZHMgZnVuIGFzU2xpY2Uoc2VsZjogQnVpbGRlcik6IFNsaWNlIHsK' + + 'ICAgIHJldHVybiBzZWxmLmVuZENlbGwoKS5iZWdpblBhcnNlKCk7Cn0KCmlubGluZSBleHRlbmRzIGZ1biBhc1NsaWNlKHNlbGY6IENlbGwpOiBTbGljZSB7CiAgICBy' + + 'ZXR1cm4gc2VsZi5iZWdpblBhcnNlKCk7Cn0KCmlubGluZSBleHRlbmRzIGZ1biBhc0NlbGwoc2VsZjogU2xpY2UpOiBDZWxsIHsKICAgIHJldHVybiBiZWdpbkNlbGwo' + + 'KQogICAgICAgIC5zdG9yZVNsaWNlKHNlbGYpCiAgICAgICAgLmVuZENlbGwoKTsKfQoKaW5saW5lIGV4dGVuZHMgZnVuIGFzQ2VsbChzZWxmOiBCdWlsZGVyKTogQ2Vs' + + 'bCB7CiAgICByZXR1cm4gc2VsZi5lbmRDZWxsKCk7Cn0KCmlubGluZSBmdW4gZW1wdHlDZWxsKCk6IENlbGwgewogICAgcmV0dXJuIGJlZ2luQ2VsbCgpLmVuZENlbGwo' + + 'KTsKfQoKaW5saW5lIGZ1biBlbXB0eVNsaWNlKCk6IFNsaWNlIHsKICAgIHJldHVybiBlbXB0eUNlbGwoKS5hc1NsaWNlKCk7Cn0K'; files['std/config.tact'] = 'YXNtIGZ1biBnZXRDb25maWdQYXJhbShpZDogSW50KTogQ2VsbD8geyBDT05GSUdPUFRQQVJBTSB9Cg=='; files['std/context.tact'] = diff --git a/stdlib/std/cells.tact b/stdlib/std/cells.tact index eb9b1c561..2bb474d1a 100644 --- a/stdlib/std/cells.tact +++ b/stdlib/std/cells.tact @@ -74,6 +74,31 @@ asm extends fun beginParse(self: Cell): Slice { CTOS } asm(-> 1 0) extends mutates fun loadRef(self: Slice): Cell { LDREF } +/// Extension function for the `Slice`. Available since Tact 1.5.0. +/// +/// Preloads the next reference from the `Slice` as a `Cell`. Doesn't modify the original `Slice`. +/// +/// Attempts to preload such reference `Cell` when `Slice` doesn't contain it throw an exception with exit code 8: `Cell overflow`. +/// +/// Attempts to preload more data than `Slice` contains throw an exception with exit code 9: `Cell underflow`. +/// +/// ```tact +/// let s1: Slice = beginCell().storeRef(emptyCell()).asSlice(); +/// let fizz: Cell = s1.preloadRef(); // didn't modify s1 +/// +/// let s2: Slice = beginCell() +/// .storeRef(emptyCell()) +/// .storeRef(s1.asCell()) +/// .asSlice(); +/// let ref1: Cell = s2.preloadRef(); +/// let ref2: Cell = s2.preloadRef(); +/// ref1 == ref2; // true +/// ``` +/// +/// See: +/// * https://docs.tact-lang.org/ref/core-cells#slicepreloadref +/// * https://docs.tact-lang.org/book/exit-codes +/// asm extends fun preloadRef(self: Slice): Cell { PLDREF } // special treatment in Func compiler, so not replaced with asm "LDSLICEX" From 387238cb611aaa2351f75e34adaa223d9a2ac11b Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Thu, 14 Nov 2024 19:03:11 +0100 Subject: [PATCH 15/24] fix: prevent `sha256()` from throwing on statically known strings and document potential issues with runtime hashing (#907) --- CHANGELOG.md | 1 + docs/links-to-web-ide.js | 2 +- docs/src/content/docs/book/operators.mdx | 4 +-- docs/src/content/docs/book/statements.mdx | 2 +- docs/src/content/docs/ref/core-math.mdx | 13 ++++++--- docs/src/content/docs/ref/stdlib-dns.mdx | 2 +- package.json | 2 +- src/abi/global.ts | 27 ++++++++++++------- src/interpreter.ts | 15 +++++------ .../abi-global-errors.spec.ts | 13 +++++++++ .../sha256-expects-string-or-slice.tact | 6 +++++ src/test/compilation-failed/tact.config.json | 5 ++++ .../e2e-emulated/contracts/intrinsics.tact | 10 ++++++- src/test/e2e-emulated/intrinsics.spec.ts | 10 +++++++ 14 files changed, 83 insertions(+), 29 deletions(-) create mode 100644 src/test/compilation-failed/abi-global-errors.spec.ts create mode 100644 src/test/compilation-failed/contracts/sha256-expects-string-or-slice.tact diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c627ad11..861dda719 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Docs: compilation of examples in `data-structures.mdx` and across Cookbook: PR [#917](https://github.com/tact-lang/tact/pull/917) - `as coins` map value serialization type is now handled correctly: PR [#987](https://github.com/tact-lang/tact/pull/987) - Type checking for `foreach` loops in trait methods: PR [#1017](https://github.com/tact-lang/tact/pull/1017) +- The `sha256()` function no longer throws on statically known strings of any length: PR [#907](https://github.com/tact-lang/tact/pull/907) ### Release contributors diff --git a/docs/links-to-web-ide.js b/docs/links-to-web-ide.js index 4903ff7e6..1e5794995 100644 --- a/docs/links-to-web-ide.js +++ b/docs/links-to-web-ide.js @@ -75,7 +75,7 @@ export default function remarkLinksToWebIDE() { // Constructing opening tag [ // Open the tag - ' = new Map([ // String case if (arg0.name === "String") { + let str: string | undefined; + + // Try const-eval try { - const str = evalConstantExpression( + str = evalConstantExpression( resolved[0]!, ctx.ctx, ) as string; - if (Buffer.from(str).length > 128) { - throwCompilationError( - "sha256 expects string argument with byte length <= 128", - ref, - ); - } + } catch (error) { + if ( + !(error instanceof TactConstEvalError) || + error.fatal + ) + throw error; + } + + // If const-eval did succeed + if (str !== undefined) { return BigInt( "0x" + sha256_sync(str).toString("hex"), ).toString(10); - } catch (e) { - // Not a constant } + + // Otherwise, revert back to runtime hash through SHA256U const exp = writeExpression(resolved[0]!, ctx); return `string_hash(${exp})`; } diff --git a/src/interpreter.ts b/src/interpreter.ts index 65a223e2e..8a40737c3 100644 --- a/src/interpreter.ts +++ b/src/interpreter.ts @@ -1,4 +1,4 @@ -import { Address, beginCell, BitString, Cell, toNano } from "@ton/core"; +import { Address, beginCell, BitString, Cell, Slice, toNano } from "@ton/core"; import { paddedBufferToBits } from "@ton/core/dist/boc/utils/paddedBits"; import * as crc32 from "crc-32"; import { evalConstantExpression } from "./constEval"; @@ -1068,17 +1068,14 @@ export class Interpreter { } case "sha256": { ensureFunArity(1, ast.args, ast.loc); - const str = ensureString( - this.interpretExpression(ast.args[0]!), - ast.args[0]!.loc, - ); - const dataSize = Buffer.from(str).length; - if (dataSize > 128) { - throwErrorConstEval( - `data is too large for sha256 hash, expected up to 128 bytes, got ${dataSize}`, + const expr = this.interpretExpression(ast.args[0]!); + if (expr instanceof Slice) { + throwNonFatalErrorConstEval( + "slice argument is currently not supported", ast.loc, ); } + const str = ensureString(expr, ast.args[0]!.loc); return BigInt("0x" + sha256_sync(str).toString("hex")); } case "emptyMap": { diff --git a/src/test/compilation-failed/abi-global-errors.spec.ts b/src/test/compilation-failed/abi-global-errors.spec.ts new file mode 100644 index 000000000..3f9808dfc --- /dev/null +++ b/src/test/compilation-failed/abi-global-errors.spec.ts @@ -0,0 +1,13 @@ +import { __DANGER_resetNodeId } from "../../grammar/ast"; +import { itShouldNotCompile } from "./util"; + +describe("abi/global.ts errors", () => { + beforeEach(() => { + __DANGER_resetNodeId(); + }); + + itShouldNotCompile({ + testName: "sha256-expects-string-or-slice", + errorMessage: "sha256 expects string or slice argument", + }); +}); diff --git a/src/test/compilation-failed/contracts/sha256-expects-string-or-slice.tact b/src/test/compilation-failed/contracts/sha256-expects-string-or-slice.tact new file mode 100644 index 000000000..fc29d475a --- /dev/null +++ b/src/test/compilation-failed/contracts/sha256-expects-string-or-slice.tact @@ -0,0 +1,6 @@ +contract Sha256 { + val: Int = 0; + receive() { + sha256(self.val); + } +} diff --git a/src/test/compilation-failed/tact.config.json b/src/test/compilation-failed/tact.config.json index d63b7dc1a..29a952d88 100644 --- a/src/test/compilation-failed/tact.config.json +++ b/src/test/compilation-failed/tact.config.json @@ -226,6 +226,11 @@ "name": "scope-const-shadows-stdlib-ident", "path": "./contracts/scope-const-shadows-stdlib-ident.tact", "output": "./contracts/output" + }, + { + "name": "sha256-expects-string-or-slice", + "path": "./contracts/sha256-expects-string-or-slice.tact", + "output": "./contracts/output" } ] } diff --git a/src/test/e2e-emulated/contracts/intrinsics.tact b/src/test/e2e-emulated/contracts/intrinsics.tact index d3ef60d6f..5f45cd563 100644 --- a/src/test/e2e-emulated/contracts/intrinsics.tact +++ b/src/test/e2e-emulated/contracts/intrinsics.tact @@ -92,6 +92,14 @@ contract IntrinsicsTester { return sha256(src); } + get fun getHashLongComptime(): Int { + return sha256("------------------------------------------------------------------------------------------------------------------------------129"); + } + + get fun getHashLongRuntime(src: String): Int { + return sha256(src); + } + receive("emit_1") { emit("Hello world".asComment()); } @@ -231,4 +239,4 @@ contract IntrinsicsTester { get fun getRawSlice24(): Slice { return self.v; } -} \ No newline at end of file +} diff --git a/src/test/e2e-emulated/intrinsics.spec.ts b/src/test/e2e-emulated/intrinsics.spec.ts index eebb4a877..95a77a0be 100644 --- a/src/test/e2e-emulated/intrinsics.spec.ts +++ b/src/test/e2e-emulated/intrinsics.spec.ts @@ -106,6 +106,16 @@ describe("intrinsics", () => { ), ).toBe(sha256("sometest")); expect(await contract.getGetHash4("wallet")).toBe(sha256("wallet")); + const longString = + "------------------------------------------------------------------------------------------------------------------------------129"; + expect(await contract.getGetHashLongComptime()).toBe( + sha256(longString), + ); + // NOTE: The discrepancy here is expected, since SHA256U operates only on the first 127 bytes + expect( + (await contract.getGetHashLongRuntime(longString)) !== + sha256(longString), + ).toBe(true); // Check `slice` expect( From 21808e439fdbc124f850d3bc87b67a6686467d5d Mon Sep 17 00:00:00 2001 From: verytactical <186486509+verytactical@users.noreply.github.com> Date: Thu, 14 Nov 2024 22:07:10 +0400 Subject: [PATCH 16/24] refactor/fix: pretty-printer (#1018) * refactor: introduce DSL to handle indents, etc. * fix: precedence levels for expressions --- package.json | 1 + src/generator/writers/writeFunction.ts | 4 +- src/grammar/ast.ts | 15 +- src/grammar/grammar.ohm | 21 +- src/prettyPrinter.ts | 1615 +++++++++-------- src/test/contracts/case-priority.tact | 17 + .../renamer-expected/case-priority.tact | 17 + src/utils/array.ts | 42 + src/utils/tricks.ts | 46 + 9 files changed, 1000 insertions(+), 778 deletions(-) create mode 100644 src/test/contracts/case-priority.tact create mode 100644 src/test/contracts/renamer-expected/case-priority.tact create mode 100644 src/utils/array.ts diff --git a/package.json b/package.json index d885a51ab..4db5dec06 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "test": "jest", "coverage": "cross-env COVERAGE=true jest", "release": "yarn clean && yarn build && yarn coverage && yarn release-it --npm.yarn1", + "type": "tsc --noEmit", "lint": "yarn eslint .", "lint:schema": "ajv validate -s schemas/configSchema.json -d tact.config.json", "fmt": "yarn prettier -l -w .", diff --git a/src/generator/writers/writeFunction.ts b/src/generator/writers/writeFunction.ts index 25cc63cf6..88c3fc51c 100644 --- a/src/generator/writers/writeFunction.ts +++ b/src/generator/writers/writeFunction.ts @@ -23,7 +23,7 @@ import { resolveFuncTupleType } from "./resolveFuncTupleType"; import { ops } from "./ops"; import { freshIdentifier } from "./freshIdentifier"; import { idTextErr, throwInternalCompilerError } from "../../errors"; -import { prettyPrintAsmShuffle } from "../../prettyPrinter"; +import { ppAsmShuffle } from "../../prettyPrinter"; export function writeCastedExpression( expression: AstExpression, @@ -580,7 +580,7 @@ export function writeFunction(f: FunctionDescription, ctx: WriterContext) { args: fAst.shuffle.args.map((id) => idOfText(funcIdOf(id))), }; ctx.asm( - prettyPrintAsmShuffle(asmShuffleEscaped), + ppAsmShuffle(asmShuffleEscaped), fAst.instructions.join(" "), ); }); diff --git a/src/grammar/ast.ts b/src/grammar/ast.ts index 200d190e8..536d31346 100644 --- a/src/grammar/ast.ts +++ b/src/grammar/ast.ts @@ -378,19 +378,22 @@ export type AstBouncedMessageType = { // export type AstExpression = + | AstExpressionPrimary | AstOpBinary | AstOpUnary - | AstFieldAccess - | AstNumber - | AstId - | AstBoolean + | AstConditional; + +export type AstExpressionPrimary = | AstMethodCall + | AstFieldAccess | AstStaticCall | AstStructInstance + | AstNumber + | AstBoolean + | AstId | AstNull | AstInitOf - | AstString - | AstConditional; + | AstString; export type AstBinaryOperation = | "+" diff --git a/src/grammar/grammar.ohm b/src/grammar/grammar.ohm index 3c3b4ad7f..2f17501e4 100644 --- a/src/grammar/grammar.ohm +++ b/src/grammar/grammar.ohm @@ -212,13 +212,22 @@ Tact { | "+" ExpressionUnary --plus | "!" ExpressionUnary --not | "~" ExpressionUnary --bitwiseNot - | ExpressionPrimary + | ExpressionPostfix // Order is important - ExpressionPrimary = ExpressionUnboxNotNull + ExpressionPostfix = ExpressionUnboxNotNull | ExpressionMethodCall | ExpressionFieldAccess - | ExpressionStaticCall + | ExpressionPrimary + + ExpressionUnboxNotNull = ExpressionPostfix "!!" + + ExpressionFieldAccess = ExpressionPostfix "." id ~"(" + + ExpressionMethodCall = ExpressionPostfix "." id Arguments + + // Order is important + ExpressionPrimary = ExpressionStaticCall | ExpressionParens | ExpressionStructInstance | integerLiteral @@ -230,12 +239,6 @@ Tact { ExpressionParens = "(" Expression ")" - ExpressionUnboxNotNull = ExpressionPrimary "!!" - - ExpressionFieldAccess = ExpressionPrimary "." id ~"(" - - ExpressionMethodCall = ExpressionPrimary "." id Arguments - ExpressionStructInstance = typeId "{" ListOf ","? "}" ExpressionStaticCall = id Arguments diff --git a/src/prettyPrinter.ts b/src/prettyPrinter.ts index 2e1ccf630..a825ec5f0 100644 --- a/src/prettyPrinter.ts +++ b/src/prettyPrinter.ts @@ -1,804 +1,897 @@ -import { - AstConstantDef, - AstImport, - AstNativeFunctionDecl, - AstReceiver, - AstStatementRepeat, - AstStatementUntil, - AstStatementWhile, - AstStatementForEach, - AstStatementTry, - AstStatementTryCatch, - AstCondition, - AstStatementAugmentedAssign, - AstStatementAssign, - AstStatementExpression, - AstStatementReturn, - AstStatementLet, - AstFunctionDef, - AstType, - AstStatement, - AstExpression, - AstContract, - AstTrait, - AstId, - AstModule, - AstModuleItem, - AstStructDecl, - AstMessageDecl, - AstTraitDeclaration, - AstFunctionDecl, - AstConstantDecl, - AstContractDeclaration, - AstContractInit, - AstStructFieldInitializer, - AstPrimitiveTypeDecl, - AstTypeId, - AstMapType, - AstBouncedMessageType, - AstFieldDecl, - AstOptionalType, - AstNode, - AstFuncId, - idText, - AstAsmFunctionDef, - AstFunctionAttribute, - AstTypedParameter, - AstAsmInstruction, - AstAsmShuffle, - astNumToString, - AstStatementDestruct, -} from "./grammar/ast"; -import { throwInternalCompilerError } from "./errors"; -import JSONbig from "json-bigint"; +import * as A from "./grammar/ast"; +import { groupBy, intercalate, isUndefined } from "./utils/array"; +import { makeVisitor } from "./utils/tricks"; + +// +// Types +// + +export const ppAstTypeId = A.idText; + +export const ppAstTypeIdWithStorage = ( + type: A.AstTypeId, + storageType: A.AstId | null, +): string => { + const alias = storageType ? ` as ${ppAstId(storageType)}` : ""; + return `${ppAstTypeId(type)}${alias}`; +}; + +export const ppAstMapType = ({ + keyType, + keyStorageType, + valueType, + valueStorageType, +}: A.AstMapType): string => { + const key = ppAstTypeIdWithStorage(keyType, keyStorageType); + const value = ppAstTypeIdWithStorage(valueType, valueStorageType); + return `map<${key}, ${value}>`; +}; + +export const ppAstBouncedMessageType = ({ + messageType, +}: A.AstBouncedMessageType): string => `bounced<${ppAstTypeId(messageType)}>`; + +export const ppAstOptionalType = ({ typeArg }: A.AstOptionalType): string => + `${ppAstType(typeArg)}?`; + +export const ppAstType = makeVisitor()({ + type_id: ppAstTypeId, + map_type: ppAstMapType, + bounced_message_type: ppAstBouncedMessageType, + optional_type: ppAstOptionalType, +}); + +// +// Expressions +// + +export const unaryOperatorType: Record = { + "+": "pre", + "-": "pre", + "!": "pre", + "~": "pre", + + "!!": "post", +}; + +export const checkPostfix = (operator: A.AstUnaryOperation) => + unaryOperatorType[operator] === "post"; /** - * Provides methods to format and indent Tact code. + * Description of precedence of certain type of AST node */ -export class PrettyPrinter { +export type Precedence = { /** - * @param indentLevel Initial level of indentation. - * @param indentSpaces Number of spaces per indentation level. + * Add parentheses around `code` if in this `parent` position we need brackets + * @param check Position-checking function from parent + * @param code Code to put parentheses around + * @returns */ - constructor( - private indentLevel: number = 0, - private readonly indentSpaces: number = 4, - ) {} + brace: ( + position: (childPrecedence: number) => boolean, + code: string, + ) => string; + /** + * Used in positions where grammar rule mentions itself + * + * Passed down when a position allows same unparenthesized operator + * For example, on left side of addition we can use another addition without + * parentheses: `1 + 2 + 3` means `(1 + 2) + 3`. Thus for left-associative + * operators we pass `self` to their left argument printer. + */ + self: (childPrecedence: number) => boolean; + /** + * Used in positions where grammar rule mentions other rule + * + * Passed down when a position disallows same unparenthesized operator + * For example, on the right side of subtraction we can't use another subtraction + * without parentheses: `1 - (2 - 3)` is not the same as `(1 - 2) - 3`. Thus for + * left-associative operators we pass `child` to their right argument printer. + */ + child: (childPrecedence: number) => boolean; +}; - private increaseIndent() { - this.indentLevel += 1; - } +/** + * Given numeric value of precedence, where higher values stand for higher binding power, + * create a helper object for precedence checking + */ +export const makePrecedence = (myPrecedence: number): Precedence => ({ + brace: (position, code) => (position(myPrecedence) ? `(${code})` : code), + self: (childPrecedence) => childPrecedence < myPrecedence, + child: (childPrecedence) => childPrecedence <= myPrecedence, +}); - private decreaseIndent() { - this.indentLevel -= 1; - } +// Least binding operator +export const lowestPrecedence = makePrecedence(0); - private indent(): string { - return " ".repeat(this.indentLevel * this.indentSpaces); - } +export const conditionalPrecedence = makePrecedence(20); - ppAstPrimitiveTypeDecl(primitive: AstPrimitiveTypeDecl): string { - return `${this.indent()}primitive ${this.ppAstId(primitive.name)};`; - } +export const binaryPrecedence: Readonly< + Record +> = { + "||": makePrecedence(30), - // - // Types - // - - ppAstType(typeRef: AstType): string { - switch (typeRef.kind) { - case "type_id": - return this.ppAstTypeId(typeRef); - case "map_type": - return this.ppAstMapType(typeRef); - case "bounced_message_type": - return this.ppAstBouncedMessageType(typeRef); - case "optional_type": - return this.ppAstOptionalType(typeRef); - } - } + "&&": makePrecedence(40), - ppAstTypeId(typeRef: AstTypeId): string { - return idText(typeRef); - } + "|": makePrecedence(50), - ppAstOptionalType(typeRef: AstOptionalType): string { - return `${this.ppAstType(typeRef.typeArg)}?`; - } + "^": makePrecedence(60), - ppAstMapType(typeRef: AstMapType): string { - const keyAlias = typeRef.keyStorageType - ? ` as ${this.ppAstId(typeRef.keyStorageType)}` - : ""; - const valueAlias = typeRef.valueStorageType - ? ` as ${this.ppAstId(typeRef.valueStorageType)}` - : ""; - return `map<${this.ppAstTypeId(typeRef.keyType)}${keyAlias}, ${this.ppAstTypeId(typeRef.valueType)}${valueAlias}>`; - } + "&": makePrecedence(70), - ppAstBouncedMessageType(typeRef: AstBouncedMessageType): string { - return `bounced<${this.ppAstTypeId(typeRef.messageType)}>`; - } + "==": makePrecedence(80), + "!=": makePrecedence(80), - // - // Expressions - // + "<": makePrecedence(90), + ">": makePrecedence(90), + "<=": makePrecedence(90), + ">=": makePrecedence(90), - /** - * Returns precedence used in unary/binary operations. - * Lower number means higher precedence - */ - getPrecedence(kind: string, op?: string): number { - switch (kind) { - case "op_binary": - switch (op) { - case "||": - return 1; - case "&&": - return 2; - case "|": - return 3; - case "^": - return 4; - case "&": - return 5; - case "==": - case "!=": - return 6; - case "<": - case ">": - case "<=": - case ">=": - return 7; - case "+": - case "-": - return 8; - case "*": - case "/": - case "%": - return 9; - default: - return 11; - } - case "conditional": - case "static_call": - case "method_call": - return 0; - case "op_unary": - return 10; - default: - return 11; - } - } + "<<": makePrecedence(100), + ">>": makePrecedence(100), - ppAstExpression(expr: AstExpression, parentPrecedence: number = 0): string { - let result; - let currentPrecedence = this.getPrecedence(expr.kind); - - switch (expr.kind) { - case "op_binary": - currentPrecedence = this.getPrecedence(expr.kind, expr.op); - result = `${this.ppAstExpression(expr.left, currentPrecedence)} ${expr.op} ${this.ppAstExpression(expr.right, currentPrecedence)}`; - break; - case "op_unary": - currentPrecedence = this.getPrecedence(expr.kind, expr.op); - result = `${expr.op}${this.ppAstExpression(expr.operand, currentPrecedence)}`; - break; - case "field_access": - result = `${this.ppAstExpression(expr.aggregate, currentPrecedence)}.${this.ppAstId(expr.field)}`; - break; - case "method_call": - result = `${this.ppAstExpression(expr.self, currentPrecedence)}.${this.ppAstId(expr.method)}(${expr.args.map((arg) => this.ppAstExpression(arg, currentPrecedence)).join(", ")})`; - break; - case "static_call": - result = `${this.ppAstId(expr.function)}(${expr.args.map((arg) => this.ppAstExpression(arg, currentPrecedence)).join(", ")})`; - break; - case "struct_instance": - result = `${this.ppAstId(expr.type)}{${expr.args.map((x) => this.ppAstStructFieldInit(x)).join(", ")}}`; - break; - case "init_of": - result = `initOf ${this.ppAstId(expr.contract)}(${expr.args.map((arg) => this.ppAstExpression(arg, currentPrecedence)).join(", ")})`; - break; - case "conditional": - result = `${this.ppAstExpression(expr.condition, currentPrecedence)} ? ${this.ppAstExpression(expr.thenBranch, currentPrecedence)} : ${this.ppAstExpression(expr.elseBranch, currentPrecedence)}`; - break; - case "number": - result = astNumToString(expr); - break; - case "id": - result = expr.text; - break; - case "boolean": - result = expr.value.toString(); - break; - case "string": - result = `"${expr.value}"`; - break; - case "null": - result = "null"; - break; - } + "+": makePrecedence(110), + "-": makePrecedence(110), - // Set parens when needed - if ( - parentPrecedence > 0 && - currentPrecedence > 0 && - currentPrecedence < parentPrecedence - ) { - result = `(${result})`; - } - - return result; - } + "*": makePrecedence(120), + "/": makePrecedence(120), + "%": makePrecedence(120), +}; - ppAstStructFieldInit(param: AstStructFieldInitializer): string { - return `${this.ppAstId(param.field)}: ${this.ppAstExpression(param.initializer)}`; - } +export const prefixPrecedence = makePrecedence(140); - // - // Program - // - - ppAstModule(program: AstModule): string { - const importsFormatted = - program.imports.length > 0 - ? `${program.imports - .map((entry) => this.ppAstImport(entry)) - .join("\n")}\n\n` - : ""; - const entriesFormatted = program.items - .map((entry, index, array) => { - const formattedEntry = this.ppModuleItem(entry); - const nextEntry = array[index + 1]; - if ( - entry.kind === "constant_def" && - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - nextEntry?.kind === "constant_def" - ) { - return formattedEntry; - } - return formattedEntry + "\n"; - }) - .join("\n"); - return `${importsFormatted}${entriesFormatted.trim()}`; - } - - ppModuleItem(item: AstModuleItem): string { - switch (item.kind) { - case "struct_decl": - return this.ppAstStruct(item); - case "contract": - return this.ppAstContract(item); - case "primitive_type_decl": - return this.ppAstPrimitiveTypeDecl(item); - case "function_def": - return this.ppAstFunctionDef(item); - case "asm_function_def": - return this.ppAstAsmFunctionDef(item); - case "native_function_decl": - return this.ppAstNativeFunction(item); - case "trait": - return this.ppAstTrait(item); - // case "program_import": - // return this.ppASTProgramImport(item); - case "constant_def": - return this.ppAstConstant(item); - case "message_decl": - return this.ppAstMessage(item); - } - } - - ppAstImport(importItem: AstImport): string { - return `${this.indent()}import "${importItem.path.value}";`; - } - - ppAstStruct(struct: AstStructDecl): string { - this.increaseIndent(); - const fieldsFormatted = struct.fields - .map((field) => this.ppAstFieldDecl(field)) - .join("\n"); - this.decreaseIndent(); - return `${this.indent()}struct ${this.ppAstId(struct.name)} {\n${fieldsFormatted}\n}`; - } +// Used by postfix unary !!, method calls and field accesses +export const postfixPrecedence = makePrecedence(150); - ppAstMessage(struct: AstMessageDecl): string { - const prefixFormatted = - struct.opcode !== null ? `(${astNumToString(struct.opcode)})` : ""; - this.increaseIndent(); - const fieldsFormatted = struct.fields - .map((field) => this.ppAstFieldDecl(field)) - .join("\n"); - this.decreaseIndent(); - return `${this.indent()}message${prefixFormatted} ${this.ppAstId(struct.name)} {\n${fieldsFormatted}\n}`; - } - - ppAstTrait(trait: AstTrait): string { - const traitsFormatted = trait.traits - .map((t) => this.ppAstId(t)) - .join(", "); - const attrsRaw = trait.attributes - .map((attr) => `@${attr.type}("${attr.name.value}")`) - .join(" "); - const attrsFormatted = attrsRaw ? `${attrsRaw} ` : ""; - this.increaseIndent(); - const bodyFormatted = trait.declarations - .map((dec, index, array) => { - const formattedDec = this.ppTraitBody(dec); - const nextDec = array[index + 1]; - /* eslint-disable @typescript-eslint/no-unnecessary-condition */ - if ( - ((dec.kind === "constant_def" || - dec.kind === "constant_decl") && - (nextDec?.kind === "constant_def" || - nextDec?.kind === "constant_decl")) || - (dec.kind === "field_decl" && - nextDec?.kind === "field_decl") - ) { - return formattedDec; - } - /* eslint-enable @typescript-eslint/no-unnecessary-condition */ - return formattedDec + "\n"; - }) - .join("\n"); - const header = traitsFormatted - ? `trait ${this.ppAstId(trait.name)} with ${traitsFormatted}` - : `trait ${this.ppAstId(trait.name)}`; - this.decreaseIndent(); - return `${this.indent()}${attrsFormatted}${header} {\n${bodyFormatted}${this.indent()}}`; - } - - ppTraitBody(item: AstTraitDeclaration): string { - switch (item.kind) { - case "field_decl": - return this.ppAstFieldDecl(item); - case "function_def": - return this.ppAstFunctionDef(item); - case "asm_function_def": - return this.ppAstAsmFunctionDef(item); - case "receiver": - return this.ppAstReceiver(item); - case "constant_def": - return this.ppAstConstant(item); - case "function_decl": - return this.ppAstFunctionDecl(item); - case "constant_decl": - return this.ppAstConstDecl(item); - } - } - - ppAstFieldDecl(field: AstFieldDecl): string { - const typeFormatted = this.ppAstType(field.type); - const initializer = field.initializer - ? ` = ${this.ppAstExpression(field.initializer)}` - : ""; - const asAlias = field.as ? ` as ${this.ppAstId(field.as)}` : ""; - return `${this.indent()}${this.ppAstId(field.name)}: ${typeFormatted}${asAlias}${initializer};`; - } +/** + * Expression printer takes an expression and a function from parent AST node printer that checks + * whether expressions with given precedence should be parenthesized in parent context + */ +export type ExprPrinter = ( + expr: T, +) => (check: (childPrecedence: number) => boolean) => string; - ppAstConstant(constant: AstConstantDef): string { - const valueFormatted = ` = ${this.ppAstExpression(constant.initializer)}`; - const attrsRaw = constant.attributes.map((attr) => attr.type).join(" "); - const attrsFormatted = attrsRaw ? `${attrsRaw} ` : ""; - return `${this.indent()}${attrsFormatted}const ${this.ppAstId(constant.name)}: ${this.ppAstType(constant.type)}${valueFormatted};`; - } +/** + * Wrapper for AST nodes that should never be parenthesized, and thus do not require information + * about the position they're printed in + * + * Takes a regular printer function and returns corresponding ExprPrinter that ignores all + * position and precedence information + */ +export const ppLeaf = + (printer: (t: T) => string): ExprPrinter => + (node) => + () => + printer(node); + +export const ppExprArgs = (args: A.AstExpression[]) => + args.map((arg) => ppAstExpression(arg)).join(", "); + +export const ppAstStructFieldInit = ( + param: A.AstStructFieldInitializer, +): string => `${ppAstId(param.field)}: ${ppAstExpression(param.initializer)}`; + +export const ppAstStructInstance = ({ type, args }: A.AstStructInstance) => + `${ppAstId(type)}{${args.map((x) => ppAstStructFieldInit(x)).join(", ")}}`; + +export const ppAstInitOf = ({ contract, args }: A.AstInitOf) => + `initOf ${ppAstId(contract)}(${ppExprArgs(args)})`; + +export const ppAstNumber = A.astNumToString; +export const ppAstBoolean = ({ value }: A.AstBoolean) => value.toString(); +export const ppAstId = ({ text }: A.AstId) => text; +export const ppAstNull = (_expr: A.AstNull) => "null"; +export const ppAstString = ({ value }: A.AstString) => `"${value}"`; + +export const ppAstStaticCall = ({ function: func, args }: A.AstStaticCall) => { + return `${ppAstId(func)}(${ppExprArgs(args)})`; +}; + +export const ppAstMethodCall: ExprPrinter = + ({ self: object, method, args }) => + (position) => { + const { brace, self } = postfixPrecedence; + return brace( + position, + `${ppAstExpressionNested(object)(self)}.${ppAstId(method)}(${ppExprArgs(args)})`, + ); + }; + +export const ppAstFieldAccess: ExprPrinter = + ({ aggregate, field }) => + (position) => { + const { brace, self } = postfixPrecedence; + return brace( + position, + `${ppAstExpressionNested(aggregate)(self)}.${ppAstId(field)}`, + ); + }; + +export const ppAstOpUnary: ExprPrinter = + ({ op, operand }) => + (position) => { + const isPostfix = checkPostfix(op); + const { brace, self } = isPostfix + ? postfixPrecedence + : prefixPrecedence; + const code = ppAstExpressionNested(operand)(self); + return brace(position, isPostfix ? `${code}${op}` : `${op}${code}`); + }; + +export const ppAstOpBinary: ExprPrinter = + ({ left, op, right }) => + (position) => { + const { brace, self, child } = binaryPrecedence[op]; + const leftCode = ppAstExpressionNested(left)(self); + const rightCode = ppAstExpressionNested(right)(child); + return brace(position, `${leftCode} ${op} ${rightCode}`); + }; + +export const ppAstConditional: ExprPrinter = + ({ condition, thenBranch, elseBranch }) => + (position) => { + const { brace, self, child } = conditionalPrecedence; + const conditionCode = ppAstExpressionNested(condition)(child); + const thenCode = ppAstExpressionNested(thenBranch)(child); + const elseCode = ppAstExpressionNested(elseBranch)(self); + return brace(position, `${conditionCode} ? ${thenCode} : ${elseCode}`); + }; + +export const ppAstExpressionNested = makeVisitor()({ + struct_instance: ppLeaf(ppAstStructInstance), + number: ppLeaf(ppAstNumber), + boolean: ppLeaf(ppAstBoolean), + id: ppLeaf(ppAstId), + null: ppLeaf(ppAstNull), + init_of: ppLeaf(ppAstInitOf), + string: ppLeaf(ppAstString), + static_call: ppLeaf(ppAstStaticCall), + + method_call: ppAstMethodCall, + field_access: ppAstFieldAccess, + + op_unary: ppAstOpUnary, + + op_binary: ppAstOpBinary, + + conditional: ppAstConditional, +}); + +export const ppAstExpression = (expr: A.AstExpression): string => { + return ppAstExpressionNested(expr)(lowestPrecedence.child); +}; - ppAstConstDecl(constant: AstConstantDecl): string { - const attrsRaw = constant.attributes.map((attr) => attr.type).join(" "); - const attrsFormatted = attrsRaw ? `${attrsRaw} ` : ""; - return `${this.indent()}${attrsFormatted}const ${this.ppAstId(constant.name)}: ${this.ppAstType(constant.type)};`; - } +/** + * An intermediate language that is only concerned of spacing and indentation + */ +type Context = { + /** + * Line of code with \n implied + */ + row: (s: string) => U; - ppAstContract(contract: AstContract): string { - const traitsFormatted = contract.traits - .map((trait) => trait.text) - .join(", "); - this.increaseIndent(); - const bodyFormatted = contract.declarations - .map((dec, index, array) => { - const formattedDec = this.ppContractBody(dec); - const nextDec = array[index + 1]; - if ( - (dec.kind === "constant_def" && - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - nextDec?.kind === "constant_def") || - (dec.kind === "field_decl" && - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - nextDec?.kind === "field_decl") - ) { - return formattedDec; - } - return formattedDec + "\n"; - }) - .join("\n"); - this.decreaseIndent(); - const header = traitsFormatted - ? `contract ${this.ppAstId(contract.name)} with ${traitsFormatted}` - : `contract ${this.ppAstId(contract.name)}`; - const attrsRaw = contract.attributes - .map((attr) => `@interface("${attr.name.value}")`) - .join(" "); - const attrsFormatted = attrsRaw ? `${attrsRaw} ` : ""; - return `${this.indent()}${attrsFormatted}${header} {\n${bodyFormatted}${this.indent()}}`; - } + /** + * Stacks lines after each other + */ + block: (rows: readonly U[]) => U; - ppContractBody(declaration: AstContractDeclaration): string { - switch (declaration.kind) { - case "field_decl": - return this.ppAstFieldDecl(declaration); - case "function_def": - return this.ppAstFunctionDef(declaration); - case "asm_function_def": - return this.ppAstAsmFunctionDef(declaration); - case "contract_init": - return this.ppAstInitFunction(declaration); - case "receiver": - return this.ppAstReceiver(declaration); - case "constant_def": - return this.ppAstConstant(declaration); - } - } + /** + * Similar to `block`, but adjacent lines of groups get concatenated + * [a, b] + [c, d] = [a, bc, d] + */ + concat: (rows: readonly U[]) => U; - public ppAstFunctionDef(f: AstFunctionDef): string { - const body = this.ppStatementBlock(f.statements); - return `${this.indent()}${this.ppAstFunctionSignature(f.attributes, f.name, f.return, f.params)} ${body}`; - } + /** + * Same as `indent`, but indents `rows` 1 level deeper and adds `{` and `}` + */ + braced: (rows: readonly U[]) => U; - public ppAstAsmFunctionDef(f: AstAsmFunctionDef): string { - const asmAttr = `asm${prettyPrintAsmShuffle(f.shuffle)}`; - const body = this.ppAsmInstructionsBlock(f.instructions); - return `${this.indent()}${asmAttr} ${this.ppAstFunctionSignature(f.attributes, f.name, f.return, f.params)} ${body}`; - } + /** + * Print a list of `items` with `print` + */ + list: (items: readonly T[], print: Printer) => readonly U[]; - ppAstFunctionDecl(f: AstFunctionDecl): string { - return `${this.indent()}${this.ppAstFunctionSignature(f.attributes, f.name, f.return, f.params)};`; - } + /** + * Display `items` with `print` in groups distinguished by return value of `getTag` + */ + grouped: (options: { + items: readonly T[]; + /** + * Items with the same tag are displayed without extra empty line between them + * + * Use NaN for tag whenever items should always be displayed with empty line, + * because NaN !== NaN + */ + getTag: (t: T) => V; + print: Printer; + }) => readonly U[]; +}; - ppAstFunctionSignature( - attributes: AstFunctionAttribute[], - name: AstId, - retTy: AstType | null, - params: AstTypedParameter[], - ): string { - const argsFormatted = params - .map( - (arg) => - `${this.ppAstId(arg.name)}: ${this.ppAstType(arg.type)}`, - ) - .join(", "); - const attrsRaw = attributes - .map((attr) => this.ppAstFunctionAttribute(attr)) - .join(" "); - const attrsFormatted = attrsRaw ? `${attrsRaw} ` : ""; - const returnType = retTy ? `: ${this.ppAstType(retTy)}` : ""; - return `${attrsFormatted}fun ${this.ppAstId(name)}(${argsFormatted})${returnType}`; - } +/** + * Generates line of code with indentation, given desired indentation level of outer scope + */ +type LevelFn = (level: number) => string; - ppAstFunctionAttribute(attr: AstFunctionAttribute): string { - if (attr.type === "get" && attr.methodId !== null) { - return `get(${this.ppAstExpression(attr.methodId)})`; - } else { - return attr.type; - } - } +/** + * Result of printing an expression is an array of rows, parameterized over indentation + * of outer scope + */ +type ContextModel = readonly LevelFn[]; - ppAstReceiver(receive: AstReceiver): string { - const header = this.ppAstReceiverHeader(receive); - const stmtsFormatted = this.ppStatementBlock(receive.statements); - return `${this.indent()}${header} ${stmtsFormatted}`; - } +/** + * Concatenates an array of printing results, so that last line of each expression is merged + * with first line of next expression + * + * Typically used to generate multiline indented code as part of single-line expression + * + * Roughly, `concat(["while (true)"], [" "], ["{", "...", "}"]) = ["while (true) {", "...", "}"]` + */ +const concat = ([head, ...tail]: readonly ContextModel[]): ContextModel => { + // If we're concatenating an empty array, the result is always empty + if (isUndefined(head)) { + return []; + } + // Create a copy of first printing result, where we'll accumulate other results + const init = [...head]; + // Recursively concatenate all printing results except for first + const next = concat(tail); + // Take last line of first printing result + const last = init.pop(); + // If first printing result has no lines, return concatenation result of all others + if (isUndefined(last)) { + return next; + } + // Get first line on concatenated printing results starting with second + const [nextHead, ...nextTail] = next; + // If they all concatenated into an array of 0 lines, just return first printing result + if (isUndefined(nextHead)) { + return head; + } + // Otherwise concatenate results, leaving indent only in front of the merged line + return [...init, (level) => last(level) + nextHead(0), ...nextTail]; +}; + +const createContext = (spaces: number): Context => { + const row = (s: string) => [ + // Empty lines are not indented + (level: number) => (s === "" ? s : " ".repeat(level * spaces) + s), + ]; + const block = (rows: readonly ContextModel[]) => rows.flat(); + const indent = (rows: readonly ContextModel[]) => + block(rows).map((f) => (level: number) => f(level + 1)); + const braced = (rows: readonly ContextModel[]) => + block([row(`{`), indent(rows), row(`}`)]); + const list = (items: readonly T[], print: Printer) => + items.map((node) => print(node)(ctx)); + const grouped = ({ + items, + getTag, + print, + }: { + items: readonly T[]; + getTag: (t: T) => V; + print: Printer; + }) => { + return intercalate( + groupBy(items, getTag).map((group) => list(group, print)), + row(""), + ); + }; + const ctx: Context = { + row, + concat, + block, + braced, + list, + grouped, + }; + return ctx; +}; - ppAstReceiverHeader(receive: AstReceiver): string { - switch (receive.selector.kind) { - case "internal-simple": - return `receive(${this.ppAstId(receive.selector.param.name)}: ${this.ppAstType(receive.selector.param.type)})`; - case "internal-fallback": - return `receive()`; - case "internal-comment": - return `receive("${receive.selector.comment.value}")`; - case "bounce": - return `bounced(${this.ppAstId(receive.selector.param.name)}: ${this.ppAstType(receive.selector.param.type)})`; - case "external-simple": - return `external(${this.ppAstId(receive.selector.param.name)}: ${this.ppAstType(receive.selector.param.type)})`; - case "external-fallback": - return `external()`; - case "external-comment": - return `external("${receive.selector.comment.value}")`; +/** + * Prints AST node of type `T` into an intermediate language of row of type `U` + * + * We enforce `U` to be a generic argument so that no implementation can (ab)use + * the fact it's a string and generate some indentation without resorting to + * methods of `Context`. + */ +type Printer = (item: T) => (ctx: Context) => U; + +export const ppAstModule: Printer = + ({ imports, items }) => + (c) => { + const itemsCode = c.grouped({ + items, + getTag: ({ kind }) => (kind === "constant_def" ? 1 : NaN), + print: ppModuleItem, + }); + if (imports.length === 0) { + return c.block(itemsCode); } - } - - ppAstNativeFunction(func: AstNativeFunctionDecl): string { - const argsFormatted = func.params - .map( - (arg) => - `${this.ppAstId(arg.name)}: ${this.ppAstType(arg.type)}`, - ) + return c.block([ + ...c.list(imports, ppAstImport), + c.row(""), + ...itemsCode, + ]); + }; + +export const ppAstStruct: Printer = + ({ name, fields }) => + (c) => + c.concat([ + c.row(`struct ${ppAstId(name)} `), + c.braced(c.list(fields, ppAstFieldDecl)), + ]); + +export const ppAstContract: Printer = + ({ name, traits, declarations, attributes }) => + (c) => { + const attrsCode = attributes + .map(({ name: { value } }) => `@interface("${value}") `) + .join(""); + const traitsCode = traits.map((trait) => trait.text).join(", "); + const header = traitsCode + ? `contract ${ppAstId(name)} with ${traitsCode}` + : `contract ${ppAstId(name)}`; + return c.concat([ + c.row(`${attrsCode}${header} `), + c.braced( + c.grouped({ + items: declarations, + getTag: ({ kind }) => + kind === "constant_def" + ? 1 + : kind === "field_decl" + ? 2 + : NaN, + print: ppContractBody, + }), + ), + ]); + }; + +export const ppAstPrimitiveTypeDecl: Printer = + ({ name }) => + (c) => + c.row(`primitive ${ppAstId(name)};`); + +export const ppAstFunctionDef: Printer = (node) => (c) => + c.concat([ + c.row(ppAstFunctionSignature(node)), + c.row(" "), + ppStatementBlock(node.statements)(c), + ]); + +export const ppAsmShuffle = ({ args, ret }: A.AstAsmShuffle): string => { + if (args.length === 0 && ret.length === 0) { + return ""; + } + const argsCode = args.map(({ text }) => text).join(" "); + if (ret.length === 0) { + return `(${argsCode})`; + } + const retCode = ret.map(({ value }) => value.toString()).join(" "); + return `(${argsCode} -> ${retCode})`; +}; + +export const ppAstAsmFunctionDef: Printer = + (node) => (c) => + c.concat([ + c.row( + `asm${ppAsmShuffle(node.shuffle)} ${ppAstFunctionSignature(node)} `, + ), + ppAsmInstructionsBlock(node.instructions)(c), + ]); + +export const ppAstNativeFunction: Printer = + ({ name, nativeName, params, return: retTy, attributes }) => + (c) => { + const attrs = attributes.map(({ type }) => type + " ").join(""); + const argsCode = params + .map(({ name, type }) => `${ppAstId(name)}: ${ppAstType(type)}`) .join(", "); - const returnType = func.return - ? `: ${this.ppAstType(func.return)}` + const returnType = retTy ? `: ${ppAstType(retTy)}` : ""; + return c.block([ + c.row(`@name(${ppAstFuncId(nativeName)})`), + c.row(`${attrs}native ${ppAstId(name)}(${argsCode})${returnType};`), + ]); + }; + +export const ppAstTrait: Printer = + ({ name, traits, attributes, declarations }) => + (c) => { + const attrsCode = attributes + .map((attr) => `@${attr.type}("${attr.name.value}") `) + .join(""); + const traitsCode = traits.map((t) => ppAstId(t)).join(", "); + const header = traitsCode + ? `trait ${ppAstId(name)} with ${traitsCode}` + : `trait ${ppAstId(name)}`; + return c.concat([ + c.row(`${attrsCode}${header} `), + c.braced( + c.grouped({ + items: declarations, + getTag: ({ kind }) => + kind === "constant_def" || kind === "constant_decl" + ? 1 + : kind === "field_decl" + ? 2 + : NaN, + print: ppTraitBody, + }), + ), + ]); + }; + +export const ppAstConstant: Printer = + ({ attributes, initializer, name, type }) => + (c) => { + const attrsCode = attributes.map(({ type }) => type + " ").join(""); + return c.row( + `${attrsCode}const ${ppAstId(name)}: ${ppAstType(type)} = ${ppAstExpression(initializer)};`, + ); + }; + +export const ppAstMessage: Printer = + ({ name, opcode, fields }) => + (c) => { + const prefixCode = + opcode !== null ? `(${A.astNumToString(opcode)})` : ""; + + return c.concat([ + c.row(`message${prefixCode} ${ppAstId(name)} `), + c.braced(c.list(fields, ppAstFieldDecl)), + ]); + }; + +export const ppModuleItem: Printer = + makeVisitor()({ + struct_decl: ppAstStruct, + contract: ppAstContract, + primitive_type_decl: ppAstPrimitiveTypeDecl, + function_def: ppAstFunctionDef, + asm_function_def: ppAstAsmFunctionDef, + native_function_decl: ppAstNativeFunction, + trait: ppAstTrait, + constant_def: ppAstConstant, + message_decl: ppAstMessage, + }); + +export const ppAstFieldDecl: Printer = + ({ type, initializer, as, name }) => + (c) => { + const asAlias = as ? ` as ${ppAstId(as)}` : ""; + const initializerCode = initializer + ? ` = ${ppAstExpression(initializer)}` : ""; - let attrs = func.attributes.map((attr) => attr.type).join(" "); - attrs = attrs ? attrs + " " : ""; - return `${this.indent()}@name(${this.ppAstFuncId(func.nativeName)})\n${this.indent()}${attrs}native ${this.ppAstId(func.name)}(${argsFormatted})${returnType};`; - } - - ppAstFuncId(func: AstFuncId): string { - return func.text; - } - - ppAstInitFunction(initFunc: AstContractInit): string { - const argsFormatted = initFunc.params - .map( - (arg) => - `${this.ppAstId(arg.name)}: ${this.ppAstType(arg.type)}`, - ) + return c.row( + `${ppAstId(name)}: ${ppAstType(type)}${asAlias}${initializerCode};`, + ); + }; + +export const ppAstReceiver: Printer = + ({ selector, statements }) => + (c) => + c.concat([ + c.row(`${ppAstReceiverHeader(selector)} `), + ppStatementBlock(statements)(c), + ]); + +export const ppAstFunctionDecl: Printer = (f) => (c) => + c.row(`${ppAstFunctionSignature(f)};`); + +export const ppAstConstDecl: Printer = + ({ attributes, name, type }) => + (c) => { + const attrsCode = attributes.map(({ type }) => type + " ").join(""); + return c.row(`${attrsCode}const ${ppAstId(name)}: ${ppAstType(type)};`); + }; + +export const ppTraitBody: Printer = + makeVisitor()({ + function_def: ppAstFunctionDef, + asm_function_def: ppAstAsmFunctionDef, + constant_def: ppAstConstant, + field_decl: ppAstFieldDecl, + receiver: ppAstReceiver, + function_decl: ppAstFunctionDecl, + constant_decl: ppAstConstDecl, + }); + +export const ppAstInitFunction: Printer = + ({ params, statements }) => + (c) => { + const argsCode = params + .map(({ name, type }) => `${ppAstId(name)}: ${ppAstType(type)}`) .join(", "); - - this.increaseIndent(); - const stmtsFormatted = initFunc.statements - .map((stmt) => this.ppAstStatement(stmt)) - .join("\n"); - this.decreaseIndent(); - - return `${this.indent()}init(${argsFormatted}) {${stmtsFormatted == "" ? "" : "\n"}${stmtsFormatted}${stmtsFormatted == "" ? "" : "\n" + this.indent()}}`; - } - - // - // Statements - // - - ppAstStatement(stmt: AstStatement): string { - switch (stmt.kind) { - case "statement_let": - return this.ppAstStatementLet(stmt as AstStatementLet); - case "statement_return": - return this.ppAstStatementReturn(stmt as AstStatementReturn); - case "statement_expression": - return this.ppAstStatementExpression( - stmt as AstStatementExpression, - ); - case "statement_assign": - return this.ppAstStatementAssign(stmt as AstStatementAssign); - case "statement_augmentedassign": - return this.ppAstStatementAugmentedAssign( - stmt as AstStatementAugmentedAssign, - ); - case "statement_condition": - return this.ppAstCondition(stmt as AstCondition); - case "statement_while": - return this.ppAstStatementWhile(stmt as AstStatementWhile); - case "statement_until": - return this.ppAstStatementUntil(stmt as AstStatementUntil); - case "statement_repeat": - return this.ppAstStatementRepeat(stmt as AstStatementRepeat); - case "statement_foreach": - return this.ppAstStatementForEach(stmt as AstStatementForEach); - case "statement_try": - return this.ppAstStatementTry(stmt as AstStatementTry); - case "statement_try_catch": - return this.ppAstStatementTryCatch( - stmt as AstStatementTryCatch, - ); - case "statement_destruct": - return this.ppAstStatementDestruct( - stmt as AstStatementDestruct, - ); + if (statements.length === 0) { + return c.row(`init(${argsCode}) {}`); } - } - - ppStatementBlock(stmts: AstStatement[]): string { - this.increaseIndent(); - const stmtsFormatted = stmts - .map((stmt) => this.ppAstStatement(stmt)) - .join("\n"); - this.decreaseIndent(); - const result = `{\n${stmtsFormatted}\n${this.indent()}}`; - return result; - } - - ppAsmInstructionsBlock(instructions: AstAsmInstruction[]): string { - this.increaseIndent(); - const instructionsFormatted = instructions - .map((instr) => this.ppAstAsmInstruction(instr)) - .join("\n"); - this.decreaseIndent(); - return `{\n${instructionsFormatted}\n${this.indent()}}`; - } - - ppAstAsmInstruction(instruction: AstAsmInstruction): string { - return `${this.indent()}${instruction}`; - } - - ppAstStatementLet(statement: AstStatementLet): string { - const expression = this.ppAstExpression(statement.expression); - const tyAnnotation = - statement.type === null - ? "" - : `: ${this.ppAstType(statement.type)}`; - return `${this.indent()}let ${this.ppAstId(statement.name)}${tyAnnotation} = ${expression};`; - } - - ppAstStatementReturn(statement: AstStatementReturn): string { - const expression = statement.expression - ? this.ppAstExpression(statement.expression) - : ""; - return `${this.indent()}return ${expression};`; - } - - ppAstStatementExpression(statement: AstStatementExpression): string { - return `${this.indent()}${this.ppAstExpression(statement.expression)};`; - } - - ppAstId(id: AstId) { - return id.text; - } - - ppAstStatementAssign(statement: AstStatementAssign): string { - return `${this.indent()}${this.ppAstExpression(statement.path)} = ${this.ppAstExpression(statement.expression)};`; - } - - ppAstStatementAugmentedAssign( - statement: AstStatementAugmentedAssign, - ): string { - return `${this.indent()}${this.ppAstExpression(statement.path)} ${statement.op}= ${this.ppAstExpression(statement.expression)};`; - } - - ppAstCondition(statement: AstCondition): string { - const condition = this.ppAstExpression(statement.condition); - const trueBranch = this.ppStatementBlock(statement.trueStatements); - const falseBranch = statement.falseStatements - ? ` else ${this.ppStatementBlock(statement.falseStatements)}` - : ""; - return `${this.indent()}if (${condition}) ${trueBranch}${falseBranch}`; - } - - ppAstStatementWhile(statement: AstStatementWhile): string { - const condition = this.ppAstExpression(statement.condition); - const stmts = this.ppStatementBlock(statement.statements); - return `${this.indent()}while (${condition}) ${stmts}`; - } - - ppAstStatementRepeat(statement: AstStatementRepeat): string { - const condition = this.ppAstExpression(statement.iterations); - const stmts = this.ppStatementBlock(statement.statements); - return `${this.indent()}repeat (${condition}) ${stmts}`; - } - - ppAstStatementUntil(statement: AstStatementUntil): string { - const condition = this.ppAstExpression(statement.condition); - const stmts = this.ppStatementBlock(statement.statements); - return `${this.indent()}do ${stmts} until (${condition});`; - } - - ppAstStatementForEach(statement: AstStatementForEach): string { - const header = `foreach (${this.ppAstId(statement.keyName)}, ${this.ppAstId(statement.valueName)} in ${this.ppAstExpression(statement.map)})`; - const body = this.ppStatementBlock(statement.statements); - return `${this.indent()}${header} ${body}`; - } - - ppAstStatementTry(statement: AstStatementTry): string { - const body = this.ppStatementBlock(statement.statements); - return `${this.indent()}try ${body}`; - } - - ppAstStatementTryCatch(statement: AstStatementTryCatch): string { - const tryBody = this.ppStatementBlock(statement.statements); - const catchBody = this.ppStatementBlock(statement.catchStatements); - return `${this.indent()}try ${tryBody} catch (${this.ppAstId(statement.catchName)}) ${catchBody}`; - } - - ppAstStatementDestruct(statement: AstStatementDestruct): string { - const ids = statement.identifiers - .values() - .reduce((acc: string[], [field, name]) => { - const id = - field.text === name.text - ? this.ppAstId(name) - : `${this.ppAstId(field)}: ${this.ppAstId(name)}`; - acc.push(id); - return acc; - }, []); - const restPattern = statement.ignoreUnspecifiedFields ? ", .." : ""; - return `${this.indent()}let ${this.ppAstTypeId(statement.type)} {${ids.join(", ")}${restPattern}} = ${this.ppAstExpression(statement.expression)};`; - } -} + return c.concat([ + c.row(`init(${argsCode}) `), + c.braced(c.list(statements, ppAstStatement)), + ]); + }; + +export const ppContractBody: Printer = + makeVisitor()({ + field_decl: ppAstFieldDecl, + function_def: ppAstFunctionDef, + asm_function_def: ppAstAsmFunctionDef, + contract_init: ppAstInitFunction, + receiver: ppAstReceiver, + constant_def: ppAstConstant, + }); + +export const ppAstImport: Printer = + ({ path }) => + (c) => + c.row(`import "${path.value}";`); + +export const ppAstFunctionSignature = ({ + name, + attributes, + return: retTy, + params, +}: A.AstFunctionDef | A.AstAsmFunctionDef | A.AstFunctionDecl): string => { + const argsCode = params + .map(({ name, type }) => `${ppAstId(name)}: ${ppAstType(type)}`) + .join(", "); + const attrsCode = attributes + .map((attr) => ppAstFunctionAttribute(attr) + " ") + .join(""); + const returnType = retTy ? `: ${ppAstType(retTy)}` : ""; + return `${attrsCode}fun ${ppAstId(name)}(${argsCode})${returnType}`; +}; + +export const ppAstFunctionAttribute = ( + attr: A.AstFunctionAttribute, +): string => { + if (attr.type === "get" && attr.methodId !== null) { + return `get(${ppAstExpression(attr.methodId)})`; + } else { + return attr.type; + } +}; + +export const ppAstReceiverHeader = makeVisitor()({ + bounce: ({ param: { name, type } }) => + `bounced(${ppAstId(name)}: ${ppAstType(type)})`, + "internal-simple": ({ param: { name, type } }) => + `receive(${ppAstId(name)}: ${ppAstType(type)})`, + "external-simple": ({ param: { name, type } }) => + `external(${ppAstId(name)}: ${ppAstType(type)})`, + "internal-fallback": () => `receive()`, + "external-fallback": () => `external()`, + "internal-comment": ({ comment: { value } }) => `receive("${value}")`, + "external-comment": ({ comment: { value } }) => `external("${value}")`, +}); + +export const ppAstFuncId = (func: A.AstFuncId): string => func.text; + +// +// Statements +// + +export const ppStatementBlock: Printer = (stmts) => (c) => + c.braced(stmts.length === 0 ? [c.row("")] : c.list(stmts, ppAstStatement)); + +export const ppAsmInstructionsBlock: Printer = + (instructions) => (c) => + c.braced(instructions.map(c.row)); + +export const ppAstStatementLet: Printer = + ({ type, name, expression }) => + (c) => { + const tyAnnotation = type === null ? "" : `: ${ppAstType(type)}`; + return c.row( + `let ${ppAstId(name)}${tyAnnotation} = ${ppAstExpression(expression)};`, + ); + }; + +export const ppAstStatementReturn: Printer = + ({ expression }) => + (c) => + c.row(`return ${expression ? ppAstExpression(expression) : ""};`); + +export const ppAstStatementExpression: Printer = + ({ expression }) => + (c) => + c.row(`${ppAstExpression(expression)};`); + +export const ppAstStatementAssign: Printer = + ({ path, expression }) => + (c) => + c.row(`${ppAstExpression(path)} = ${ppAstExpression(expression)};`); + +export const ppAstStatementAugmentedAssign: Printer< + A.AstStatementAugmentedAssign +> = + ({ path, op, expression }) => + (c) => + c.row( + `${ppAstExpression(path)} ${op}= ${ppAstExpression(expression)};`, + ); + +export const ppAstCondition: Printer = + ({ condition, trueStatements, falseStatements }) => + (c) => { + if (falseStatements) { + return c.concat([ + c.row(`if (${ppAstExpression(condition)}) `), + ppStatementBlock(trueStatements)(c), + c.row(" else "), + ppStatementBlock(falseStatements)(c), + ]); + } else { + return c.concat([ + c.row(`if (${ppAstExpression(condition)}) `), + ppStatementBlock(trueStatements)(c), + ]); + } + }; + +export const ppAstStatementWhile: Printer = + ({ condition, statements }) => + (c) => + c.concat([ + c.row(`while (${ppAstExpression(condition)}) `), + ppStatementBlock(statements)(c), + ]); + +export const ppAstStatementRepeat: Printer = + ({ iterations, statements }) => + (c) => + c.concat([ + c.row(`repeat (${ppAstExpression(iterations)}) `), + ppStatementBlock(statements)(c), + ]); + +export const ppAstStatementUntil: Printer = + ({ condition, statements }) => + (c) => + c.concat([ + c.row(`do `), + ppStatementBlock(statements)(c), + c.row(` until (${ppAstExpression(condition)});`), + ]); + +export const ppAstStatementForEach: Printer = + ({ keyName, valueName, map, statements }) => + (c) => + c.concat([ + c.row( + `foreach (${ppAstId(keyName)}, ${ppAstId(valueName)} in ${ppAstExpression(map)}) `, + ), + ppStatementBlock(statements)(c), + ]); + +export const ppAstStatementTry: Printer = + ({ statements }) => + (c) => + c.concat([c.row(`try `), ppStatementBlock(statements)(c)]); + +export const ppAstStatementTryCatch: Printer = + ({ statements, catchName, catchStatements }) => + (c) => + c.concat([ + c.row(`try `), + ppStatementBlock(statements)(c), + c.row(` catch (${ppAstId(catchName)}) `), + ppStatementBlock(catchStatements)(c), + ]); + +export const ppAstStatementDestruct: Printer = + ({ type, identifiers, ignoreUnspecifiedFields, expression }) => + (c) => { + const ids: string[] = []; + for (const [field, name] of identifiers.values()) { + const id = + field.text === name.text + ? ppAstId(name) + : `${ppAstId(field)}: ${ppAstId(name)}`; + ids.push(id); + } + const restPattern = ignoreUnspecifiedFields ? ", .." : ""; + return c.row( + `let ${ppAstTypeId(type)} {${ids.join(", ")}${restPattern}} = ${ppAstExpression(expression)};`, + ); + }; + +export const ppAstStatement: Printer = + makeVisitor()({ + statement_let: ppAstStatementLet, + statement_return: ppAstStatementReturn, + statement_expression: ppAstStatementExpression, + statement_assign: ppAstStatementAssign, + statement_augmentedassign: ppAstStatementAugmentedAssign, + statement_condition: ppAstCondition, + statement_while: ppAstStatementWhile, + statement_until: ppAstStatementUntil, + statement_repeat: ppAstStatementRepeat, + statement_foreach: ppAstStatementForEach, + statement_try: ppAstStatementTry, + statement_try_catch: ppAstStatementTryCatch, + statement_destruct: ppAstStatementDestruct, + }); + +export const exprNode = + (exprPrinter: (expr: T) => string): Printer => + (node) => + (c) => + c.row(exprPrinter(node)); + +export const ppAstNode: Printer = makeVisitor()({ + op_binary: exprNode(ppAstExpression), + op_unary: exprNode(ppAstExpression), + field_access: exprNode(ppAstExpression), + method_call: exprNode(ppAstExpression), + static_call: exprNode(ppAstExpression), + struct_instance: exprNode(ppAstExpression), + init_of: exprNode(ppAstExpression), + conditional: exprNode(ppAstExpression), + number: exprNode(ppAstExpression), + id: exprNode(ppAstExpression), + boolean: exprNode(ppAstExpression), + string: exprNode(ppAstExpression), + null: exprNode(ppAstExpression), + type_id: exprNode(ppAstType), + optional_type: exprNode(ppAstType), + map_type: exprNode(ppAstType), + bounced_message_type: exprNode(ppAstType), + struct_field_initializer: exprNode(ppAstStructFieldInit), + destruct_mapping: () => { + throw new Error("Not implemented"); + }, + typed_parameter: () => { + throw new Error("Not implemented"); + }, + destruct_end: () => { + throw new Error("Not implemented"); + }, + + module: ppAstModule, + struct_decl: ppAstStruct, + constant_def: ppAstConstant, + constant_decl: ppAstConstDecl, + function_def: ppAstFunctionDef, + contract: ppAstContract, + trait: ppAstTrait, + primitive_type_decl: ppAstPrimitiveTypeDecl, + message_decl: ppAstMessage, + native_function_decl: ppAstNativeFunction, + field_decl: ppAstFieldDecl, + function_decl: ppAstFunctionDecl, + receiver: ppAstReceiver, + contract_init: ppAstInitFunction, + statement_let: ppAstStatementLet, + statement_return: ppAstStatementReturn, + statement_expression: ppAstStatementExpression, + statement_assign: ppAstStatementAssign, + statement_augmentedassign: ppAstStatementAugmentedAssign, + statement_condition: ppAstCondition, + statement_while: ppAstStatementWhile, + statement_until: ppAstStatementUntil, + statement_repeat: ppAstStatementRepeat, + statement_try: ppAstStatementTry, + statement_try_catch: ppAstStatementTryCatch, + statement_foreach: ppAstStatementForEach, + import: ppAstImport, + func_id: exprNode(ppAstFuncId), + statement_destruct: ppAstStatementDestruct, + function_attribute: exprNode(ppAstFunctionAttribute), + asm_function_def: ppAstAsmFunctionDef, +}); /** * Pretty-prints an AST node into a string representation. * @param node The AST node to format. * @returns A string that represents the formatted AST node. */ -export function prettyPrint(node: AstNode): string { - const pp = new PrettyPrinter(); - switch (node.kind) { - case "module": - return pp.ppAstModule(node); - case "op_binary": - case "op_unary": - case "field_access": - case "method_call": - case "static_call": - case "struct_instance": - case "init_of": - case "conditional": - case "number": - case "id": - case "boolean": - case "string": - case "null": - return pp.ppAstExpression(node); - case "struct_decl": - return pp.ppAstStruct(node); - case "constant_def": - return pp.ppAstConstant(node); - case "constant_decl": - return pp.ppAstConstDecl(node); - case "function_def": - return pp.ppAstFunctionDef(node); - case "contract": - return pp.ppAstContract(node); - case "trait": - return pp.ppAstTrait(node); - case "type_id": - case "optional_type": - case "map_type": - case "bounced_message_type": - return pp.ppAstType(node); - case "primitive_type_decl": - return pp.ppAstPrimitiveTypeDecl(node); - case "message_decl": - return pp.ppAstMessage(node); - case "native_function_decl": - return pp.ppAstNativeFunction(node); - case "field_decl": - return pp.ppAstFieldDecl(node); - case "function_decl": - return pp.ppAstFunctionDecl(node); - case "receiver": - return pp.ppAstReceiver(node); - case "contract_init": - return pp.ppAstInitFunction(node); - case "statement_let": - return pp.ppAstStatementLet(node); - case "statement_return": - return pp.ppAstStatementReturn(node); - case "statement_expression": - return pp.ppAstStatementExpression(node); - case "statement_assign": - return pp.ppAstStatementAssign(node); - case "statement_augmentedassign": - return pp.ppAstStatementAugmentedAssign(node); - case "statement_condition": - return pp.ppAstCondition(node); - case "statement_while": - return pp.ppAstStatementWhile(node); - case "statement_until": - return pp.ppAstStatementUntil(node); - case "statement_repeat": - return pp.ppAstStatementRepeat(node); - case "statement_try": - return pp.ppAstStatementTry(node); - case "statement_try_catch": - return pp.ppAstStatementTryCatch(node); - case "statement_foreach": - return pp.ppAstStatementForEach(node); - case "struct_field_initializer": - return pp.ppAstStructFieldInit(node); - case "import": - return pp.ppAstImport(node); - default: - throwInternalCompilerError( - `Unsupported AST type: ${JSONbig.stringify(node, null, 2)}`, - ); - } -} - -export function prettyPrintAsmShuffle(shuffle: AstAsmShuffle): string { - const ppArgShuffle = shuffle.args.map((id) => idText(id)).join(" "); - const ppRetShuffle = - shuffle.ret.length === 0 - ? "" - : ` -> ${shuffle.ret.map((num) => num.value.toString()).join(" ")}`; - return shuffle.args.length === 0 && shuffle.ret.length === 0 - ? "" - : `(${ppArgShuffle}${ppRetShuffle})`; -} +export const prettyPrint = (node: A.AstNode): string => + ppAstNode(node)(createContext(4)) + // Initial level of indentation is 0 + .map((f) => f(0)) + // Lines are terminated with \n + .join("\n"); diff --git a/src/test/contracts/case-priority.tact b/src/test/contracts/case-priority.tact new file mode 100644 index 000000000..0c290c55c --- /dev/null +++ b/src/test/contracts/case-priority.tact @@ -0,0 +1,17 @@ +contract Priority { + x: Bool; + y: Int; + + init() { + self.x = true || true && true == 5 < 6 << 9 + 7 * 8; + self.x = true || true && true != 5 > 6 >> 9 - 7 / 8; + self.x = true || true && true != 5 <= 6 >> 9 - 7 % 8; + self.x = true || true && true != 5 >= 6 >> 9 - 7 % 8; + self.y = 1 | 2 ^ 3 & 6 >> 9 - 7 % 8; + self.x = (true ? true : false) ? 1 : 2; + self.x = true ? (true ? 1 : 2) : 3; + self.x = false ? 1 : false ? 2 : 3; + self.x = +self.x!!; + self.x = (+self.x)!!; + } +} diff --git a/src/test/contracts/renamer-expected/case-priority.tact b/src/test/contracts/renamer-expected/case-priority.tact new file mode 100644 index 000000000..d113b924f --- /dev/null +++ b/src/test/contracts/renamer-expected/case-priority.tact @@ -0,0 +1,17 @@ +contract contract_0 { + x: Bool; + y: Int; + + init() { + self.x = true || true && true == 5 < 6 << 9 + 7 * 8; + self.x = true || true && true != 5 > 6 >> 9 - 7 / 8; + self.x = true || true && true != 5 <= 6 >> 9 - 7 % 8; + self.x = true || true && true != 5 >= 6 >> 9 - 7 % 8; + self.y = 1 | 2 ^ 3 & 6 >> 9 - 7 % 8; + self.x = (true ? true : false) ? 1 : 2; + self.x = true ? (true ? 1 : 2) : 3; + self.x = false ? 1 : false ? 2 : 3; + self.x = +self.x!!; + self.x = (+self.x)!!; + } +} diff --git a/src/utils/array.ts b/src/utils/array.ts new file mode 100644 index 000000000..ab1c33bf0 --- /dev/null +++ b/src/utils/array.ts @@ -0,0 +1,42 @@ +export const isUndefined = (t: T | undefined): t is undefined => + typeof t === "undefined"; + +export const groupBy = ( + items: readonly T[], + f: (t: T) => U, +): readonly (readonly T[])[] => { + const result: T[][] = []; + const [head, ...tail] = items; + if (isUndefined(head)) { + return result; + } + let group: T[] = [head]; + result.push(group); + let tag: U = f(head); + for (const item of tail) { + const nextTag = f(item); + if (tag === nextTag) { + group.push(item); + } else { + group = [item]; + result.push(group); + tag = nextTag; + } + } + return result; +}; + +export const intercalate = ( + items: readonly (readonly T[])[], + value: T, +): readonly T[] => { + const [head, ...tail] = items; + if (isUndefined(head)) { + return []; + } + const result: T[] = [...head]; + for (const item of tail) { + result.push(value, ...item); + } + return result; +}; diff --git a/src/utils/tricks.ts b/src/utils/tricks.ts index f85c7e6dd..7afbfdff2 100644 --- a/src/utils/tricks.ts +++ b/src/utils/tricks.ts @@ -88,3 +88,49 @@ export const match = ( throw new Error("Not exhaustive"); }) as MV, never>; }; + +import { throwInternalCompilerError } from "../errors"; + +/** + * Convert union to intersection. See https://stackoverflow.com/q/50374908 + */ +type Intersect = (T extends unknown ? (x: T) => 0 : never) extends ( + x: infer R, +) => 0 + ? R + : never; + +/** + * Makes types more readable + * Example: Unwrap<{ a: 1 } & { b: 2 }> = { a: 1, b: 2 } + */ +type Unwrap = T extends infer R ? { [K in keyof R]: R[K] } : never; + +type Inputs = I extends { kind: infer K } + ? K extends string + ? Record unknown> + : never + : never; +type Outputs = { [K in keyof O]: (input: never) => O[K] }; +type Handlers = Unwrap>> & Outputs; + +/** + * Make visitor for disjoint union (tagged union, discriminated union) + */ +export const makeVisitor = + () => + (handlers: Handlers) => + (input: Extract): O[keyof O] => { + const handler = (handlers as Record O[keyof O]>)[ + input.kind + ]; + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (handler) { + return handler(input); + } else { + throwInternalCompilerError( + `Reached impossible case: ${input.kind}`, + ); + } + }; From 75637cb97b569b53c5602cd6e8fdeed552fa2e16 Mon Sep 17 00:00:00 2001 From: verytactical <186486509+verytactical@users.noreply.github.com> Date: Sat, 16 Nov 2024 01:03:27 +0400 Subject: [PATCH 17/24] ci: telegram bot (#1049) --- .github/workflows/comment.yml | 23 +++++++++++++++++++++++ .github/workflows/pull.yml | 22 ++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 .github/workflows/comment.yml create mode 100644 .github/workflows/pull.yml diff --git a/.github/workflows/comment.yml b/.github/workflows/comment.yml new file mode 100644 index 000000000..f70ba2a9f --- /dev/null +++ b/.github/workflows/comment.yml @@ -0,0 +1,23 @@ +name: Comment +on: + issue_comment: + types: [created] + pull_request_review_comment: + types: [created] + +jobs: + build: + name: Telegram + runs-on: ubuntu-latest + if: ${{ github.event.issue.pull_request }} + steps: + - name: Send telegram message on push + uses: appleboy/telegram-action@v1.0.0 + with: + to: ${{ secrets.TELEGRAM_ISSUE_CHAT_ID }} + token: ${{ secrets.TELEGRAM_ISSUE_TOKEN }} + format: Markdown + # this might go wrong, because `title` and `body` can have arbitrary markdown + message: | + ✏️ [${{ github.event.comment.user.login }}](${{ github.event.comment.html_url }}): ${{ github.event.comment.body }} + 🔀 ${{ github.event.issue.title }} diff --git a/.github/workflows/pull.yml b/.github/workflows/pull.yml new file mode 100644 index 000000000..9fc6612a2 --- /dev/null +++ b/.github/workflows/pull.yml @@ -0,0 +1,22 @@ +name: Pull request +on: + pull_request: + types: [opened, reopened] + +jobs: + build: + name: Telegram + runs-on: ubuntu-latest + steps: + - name: Send telegram message on push + uses: appleboy/telegram-action@v1.0.0 + with: + to: ${{ secrets.TELEGRAM_ISSUE_CHAT_ID }} + token: ${{ secrets.TELEGRAM_ISSUE_TOKEN }} + format: Markdown + # this might go wrong, because `title` can have arbitrary markdown + message: | + 🍰 [${{ github.event.pull_request.title }}](${{ github.event.pull_request.html_url }}) + 🌲 ${{ github.repository }} + 👤 ${{ github.actor }} + 🔀 ${{ github.event.pull_request.base.label }} <- ${{ github.event.pull_request.head.label }} From 1a1e69a852054fe2e7fe903e97b3fba6a52322ac Mon Sep 17 00:00:00 2001 From: verytactical <186486509+verytactical@users.noreply.github.com> Date: Mon, 18 Nov 2024 11:29:26 +0400 Subject: [PATCH 18/24] fix(ci): don't fail when can't send message to telegram (#1054) --- .github/workflows/pull.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pull.yml b/.github/workflows/pull.yml index 9fc6612a2..f42f44769 100644 --- a/.github/workflows/pull.yml +++ b/.github/workflows/pull.yml @@ -10,6 +10,7 @@ jobs: steps: - name: Send telegram message on push uses: appleboy/telegram-action@v1.0.0 + continue-on-error: true with: to: ${{ secrets.TELEGRAM_ISSUE_CHAT_ID }} token: ${{ secrets.TELEGRAM_ISSUE_TOKEN }} From 83e16815d1a78747a4f551d571cb33e1ccd570db Mon Sep 17 00:00:00 2001 From: Daniil Sedov Date: Mon, 18 Nov 2024 10:44:47 +0300 Subject: [PATCH 19/24] fix: check collisions of method ids with reserved ones not only for explicitly specified ones (#1052) Namely: - `supported_interfaces` - `lazy_deployment_completed` - `get_abi_ipfs` --- CHANGELOG.md | 2 +- .../resolveStatements.spec.ts.snap | 60 ++++++++++++++++ src/types/resolveStatements.ts | 70 ++++++++++--------- .../getter-collision-with-reserved1.tact | 13 ++++ .../getter-collision-with-reserved2.tact | 13 ++++ .../getter-collision-with-reserved3.tact | 13 ++++ .../getter-collision-with-reserved_opt1.tact | 13 ++++ .../getter-collision-with-reserved_opt2.tact | 13 ++++ .../getter-collision-with-reserved_opt3.tact | 13 ++++ 9 files changed, 177 insertions(+), 33 deletions(-) create mode 100644 src/types/stmts-failed/getter-collision-with-reserved1.tact create mode 100644 src/types/stmts-failed/getter-collision-with-reserved2.tact create mode 100644 src/types/stmts-failed/getter-collision-with-reserved3.tact create mode 100644 src/types/stmts-failed/getter-collision-with-reserved_opt1.tact create mode 100644 src/types/stmts-failed/getter-collision-with-reserved_opt2.tact create mode 100644 src/types/stmts-failed/getter-collision-with-reserved_opt3.tact diff --git a/CHANGELOG.md b/CHANGELOG.md index 861dda719..cc1428d97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,7 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- Collisions in getter method ids are now handled and reported properly: PR [#875](https://github.com/tact-lang/tact/pull/875) +- Collisions in getter method ids are now handled and reported properly: PR [#875](https://github.com/tact-lang/tact/pull/875), PR [#1052](https://github.com/tact-lang/tact/pull/1052) - Docs: layout of tables, syntax highlighting, Chinese translations of sidebar separators: PR [#916](https://github.com/tact-lang/tact/pull/916) - Non-null struct fields after null ones are treated correctly in Sandbox tests after updating `@ton/core` to 0.59.0: PR [#933](https://github.com/tact-lang/tact/pull/933) - Prevent inline code snippets from changing their background color: PR [#935](https://github.com/tact-lang/tact/pull/935) diff --git a/src/types/__snapshots__/resolveStatements.spec.ts.snap b/src/types/__snapshots__/resolveStatements.spec.ts.snap index 0a89ef360..7f6b8009a 100644 --- a/src/types/__snapshots__/resolveStatements.spec.ts.snap +++ b/src/types/__snapshots__/resolveStatements.spec.ts.snap @@ -577,6 +577,66 @@ Line 8, col 13: " `; +exports[`resolveStatements should fail statements for getter-collision-with-reserved_opt1 1`] = ` +":10:9: method ids cannot overlap with Tact reserved method ids: 113617, 115390, 121275 +Line 10, col 9: + 9 | +> 10 | get(115390) fun _lazy_deployment_completed(): String{ + ^~~~~~ + 11 | return "not ok"; +" +`; + +exports[`resolveStatements should fail statements for getter-collision-with-reserved_opt2 1`] = ` +":10:9: method ids cannot overlap with Tact reserved method ids: 113617, 115390, 121275 +Line 10, col 9: + 9 | +> 10 | get(121275) fun _get_abi_ipfs(): Int{ + ^~~~~~ + 11 | return 123; +" +`; + +exports[`resolveStatements should fail statements for getter-collision-with-reserved_opt3 1`] = ` +":10:9: method ids cannot overlap with Tact reserved method ids: 113617, 115390, 121275 +Line 10, col 9: + 9 | +> 10 | get(113617) fun _supported_interfaces(): String{ + ^~~~~~ + 11 | return "ok"; +" +`; + +exports[`resolveStatements should fail statements for getter-collision-with-reserved1 1`] = ` +":10:5: method ids cannot overlap with Tact reserved method ids: 113617, 115390, 121275 +Line 10, col 5: + 9 | +> 10 | get fun lazy_deployment_completed(): String{ + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 11 | return "not ok"; +" +`; + +exports[`resolveStatements should fail statements for getter-collision-with-reserved2 1`] = ` +":10:5: method ids cannot overlap with Tact reserved method ids: 113617, 115390, 121275 +Line 10, col 5: + 9 | +> 10 | get fun get_abi_ipfs(): Int{ + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 11 | return 123; +" +`; + +exports[`resolveStatements should fail statements for getter-collision-with-reserved3 1`] = ` +":10:5: method ids cannot overlap with Tact reserved method ids: 113617, 115390, 121275 +Line 10, col 5: + 9 | +> 10 | get fun supported_interfaces(): String{ + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 11 | return "ok"; +" +`; + exports[`resolveStatements should fail statements for getter-collision-with-trait 1`] = ` ":4:13: Method ID collision: getter 'pko' has the same method ID 103057 as getter 'getter1' Pick a different getter name or explicit method ID to avoid collisions diff --git a/src/types/resolveStatements.ts b/src/types/resolveStatements.ts index 1e6e2afe4..760c4b950 100644 --- a/src/types/resolveStatements.ts +++ b/src/types/resolveStatements.ts @@ -978,6 +978,40 @@ export function resolveStatements(ctx: CompilerContext) { return ctx; } +function checkMethodId(methodId: bigint, loc: SrcInfo) { + // method ids are 19-bit signed integers + if (methodId < -(2n ** 18n) || methodId >= 2n ** 18n) { + throwConstEvalError( + "method ids must fit 19-bit signed integer range", + true, + loc, + ); + } + // method ids -4, -3, -2, -1, 0 ... 2^14 - 1 (inclusive) are kind of reserved by TVM + // for the upper bound see F12_n (CALL) TVM instruction + // and many small ids will be taken by internal procedures + // + // also, some ids are taken by the getters generated by Tact: + // supported_interfaces -> 113617 + // lazy_deployment_completed -> 115390 + // get_abi_ipfs -> 121275 + if (-4n <= methodId && methodId < 2n ** 14n) { + throwConstEvalError( + "method ids cannot overlap with the TVM reserved ids: -4, -3, -2, -1, 0 ... 2^14 - 1", + true, + loc, + ); + } + const tactGeneratedGetterMethodIds = [113617n, 115390n, 121275n]; + if (tactGeneratedGetterMethodIds.includes(methodId)) { + throwConstEvalError( + `method ids cannot overlap with Tact reserved method ids: ${tactGeneratedGetterMethodIds.map((n) => n.toString()).join(", ")}`, + true, + loc, + ); + } +} + function getMethodId( funcDescr: FunctionDescription, ctx: CompilerContext, @@ -1001,39 +1035,11 @@ function getMethodId( evalConstantExpression(optMethodId, ctx), optMethodId.loc, ); - // method ids are 19-bit signed integers - if (methodId < -(2n ** 18n) || methodId >= 2n ** 18n) { - throwConstEvalError( - "method ids must fit 19-bit signed integer range", - true, - optMethodId!.loc, - ); - } - // method ids -4, -3, -2, -1, 0 ... 2^14 - 1 (inclusive) are kind of reserved by TVM - // for the upper bound see F12_n (CALL) TVM instruction - // and many small ids will be taken by internal procedures - // - // also, some ids are taken by the getters generated by Tact: - // supported_interfaces -> 113617 - // lazy_deployment_completed -> 115390 - // get_abi_ipfs -> 121275 - if (-4n <= methodId && methodId < 2n ** 14n) { - throwConstEvalError( - "method ids cannot overlap with the TVM reserved ids: -4, -3, -2, -1, 0 ... 2^14 - 1", - true, - optMethodId!.loc, - ); - } - const tactGeneratedGetterMethodIds = [113617n, 115390n, 121275n]; - if (tactGeneratedGetterMethodIds.includes(methodId)) { - throwConstEvalError( - `method ids cannot overlap with Tact reserved method ids: ${tactGeneratedGetterMethodIds.map((n) => n.toString()).join(", ")}`, - true, - optMethodId!.loc, - ); - } + checkMethodId(methodId, optMethodId.loc); return Number(methodId); } else { - return (crc16(funcDescr.name) & 0xffff) | 0x10000; + const methodId = (crc16(funcDescr.name) & 0xffff) | 0x10000; + checkMethodId(BigInt(methodId), funcDescr.ast.loc); + return methodId; } } diff --git a/src/types/stmts-failed/getter-collision-with-reserved1.tact b/src/types/stmts-failed/getter-collision-with-reserved1.tact new file mode 100644 index 000000000..acc248c2d --- /dev/null +++ b/src/types/stmts-failed/getter-collision-with-reserved1.tact @@ -0,0 +1,13 @@ +primitive String; + +trait BaseTrait { } + +contract TestContract { + get fun getter1() { + return; + } + + get fun lazy_deployment_completed(): String{ + return "not ok"; + } +} diff --git a/src/types/stmts-failed/getter-collision-with-reserved2.tact b/src/types/stmts-failed/getter-collision-with-reserved2.tact new file mode 100644 index 000000000..dc4b6f2e4 --- /dev/null +++ b/src/types/stmts-failed/getter-collision-with-reserved2.tact @@ -0,0 +1,13 @@ +primitive Int; + +trait BaseTrait { } + +contract TestContract { + get fun getter1() { + return; + } + + get fun get_abi_ipfs(): Int{ + return 123; + } +} diff --git a/src/types/stmts-failed/getter-collision-with-reserved3.tact b/src/types/stmts-failed/getter-collision-with-reserved3.tact new file mode 100644 index 000000000..5500943b7 --- /dev/null +++ b/src/types/stmts-failed/getter-collision-with-reserved3.tact @@ -0,0 +1,13 @@ +primitive String; + +trait BaseTrait { } + +contract TestContract { + get fun getter1() { + return; + } + + get fun supported_interfaces(): String{ + return "ok"; + } +} diff --git a/src/types/stmts-failed/getter-collision-with-reserved_opt1.tact b/src/types/stmts-failed/getter-collision-with-reserved_opt1.tact new file mode 100644 index 000000000..79062573a --- /dev/null +++ b/src/types/stmts-failed/getter-collision-with-reserved_opt1.tact @@ -0,0 +1,13 @@ +primitive String; + +trait BaseTrait { } + +contract TestContract { + get fun getter1() { + return; + } + + get(115390) fun _lazy_deployment_completed(): String{ + return "not ok"; + } +} diff --git a/src/types/stmts-failed/getter-collision-with-reserved_opt2.tact b/src/types/stmts-failed/getter-collision-with-reserved_opt2.tact new file mode 100644 index 000000000..a972009f1 --- /dev/null +++ b/src/types/stmts-failed/getter-collision-with-reserved_opt2.tact @@ -0,0 +1,13 @@ +primitive Int; + +trait BaseTrait { } + +contract TestContract { + get fun getter1() { + return; + } + + get(121275) fun _get_abi_ipfs(): Int{ + return 123; + } +} diff --git a/src/types/stmts-failed/getter-collision-with-reserved_opt3.tact b/src/types/stmts-failed/getter-collision-with-reserved_opt3.tact new file mode 100644 index 000000000..3898ab008 --- /dev/null +++ b/src/types/stmts-failed/getter-collision-with-reserved_opt3.tact @@ -0,0 +1,13 @@ +primitive String; + +trait BaseTrait { } + +contract TestContract { + get fun getter1() { + return; + } + + get(113617) fun _supported_interfaces(): String{ + return "ok"; + } +} From 5d959941387c19927bd4b401a7e5619a5a3918e7 Mon Sep 17 00:00:00 2001 From: Daniil Sedov Date: Mon, 18 Nov 2024 10:58:50 +0300 Subject: [PATCH 20/24] fix: format error codes in compilation report as a list (#1051) --- CHANGELOG.md | 1 + src/generator/writeReport.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc1428d97..d9e4bfac5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Optional types for `self` argument in `extends mutates` functions are now allowed: PR [#854](https://github.com/tact-lang/tact/pull/854) - Docs: complete overhaul of the exit codes page: PR [#978](https://github.com/tact-lang/tact/pull/978) - Docs: enhanced Jettons Cookbook page: PR [#944](https://github.com/tact-lang/tact/pull/944) +- Error codes in the report are now formatted as a list: PR [#1051](https://github.com/tact-lang/tact/pull/1051) ### Fixed diff --git a/src/generator/writeReport.ts b/src/generator/writeReport.ts index b6f580a85..62250f53d 100644 --- a/src/generator/writeReport.ts +++ b/src/generator/writeReport.ts @@ -45,7 +45,7 @@ export function writeReport(ctx: CompilerContext, pkg: PackageFileFormat) { // Error Codes w.write(`# Error Codes`); Object.entries(abi.errors!).forEach(([t, abiError]) => { - w.write(`${t}: ${abiError.message}`); + w.write(`* ${t}: ${abiError.message}`); }); w.append(); From fbabf31673e439d66d4133f5e3588558769964f6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 13:20:08 +0400 Subject: [PATCH 21/24] chore(deps): bump cross-spawn in the npm_and_yarn group (#1055) Bumps the npm_and_yarn group with 1 update: [cross-spawn](https://github.com/moxystudio/node-cross-spawn). Updates `cross-spawn` from 7.0.3 to 7.0.5 - [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md) - [Commits](https://github.com/moxystudio/node-cross-spawn/compare/v7.0.3...v7.0.5) --- updated-dependencies: - dependency-name: cross-spawn dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index ebf553d9b..b8d42a873 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2254,9 +2254,9 @@ cross-env@^7.0.3: cross-spawn "^7.0.1" cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + version "7.0.5" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz#910aac880ff5243da96b728bc6521a5f6c2f2f82" + integrity sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" From c45f4d6b92d952979459773fe90815957f725b9d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Nov 2024 11:30:21 +0400 Subject: [PATCH 22/24] chore(deps-dev): bump knip from 5.36.7 to 5.37.1 (#1059) --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index b8d42a873..e19095901 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4008,9 +4008,9 @@ kleur@^3.0.3: integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== knip@^5.24.1: - version "5.36.7" - resolved "https://registry.npmjs.org/knip/-/knip-5.36.7.tgz#5a570e89aa5781ea0f98929f558596c2128777e7" - integrity sha512-PSuu62+6wqd1Q1V/ZzbDhvJ3X+RU8wZILon90h2s93+d1OZL118ZE9WihzSqwP29GVt72MTlbS/HHG+O47H68w== + version "5.37.1" + resolved "https://registry.npmjs.org/knip/-/knip-5.37.1.tgz#3c3e91c425dfb35be68b4d12cc0b9ee3cde794e8" + integrity sha512-69gjKj5lLsLXcIPXlHyFfX5AOHgRdh/iXH8gUqvmsHtjqoWhOATeXZDjvvemmgw7KxbWbUzxBNbpjhtJWzgqGA== dependencies: "@nodelib/fs.walk" "1.2.8" "@snyk/github-codeowners" "1.1.0" From 734949aade385016e1763aca976876a56afe6331 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Nov 2024 11:30:56 +0400 Subject: [PATCH 23/24] chore(deps-dev): bump husky from 9.1.6 to 9.1.7 (#1060) --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index e19095901..2432349c3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3147,9 +3147,9 @@ human-signals@^5.0.0: integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== husky@^9.1.5: - version "9.1.6" - resolved "https://registry.npmjs.org/husky/-/husky-9.1.6.tgz#e23aa996b6203ab33534bdc82306b0cf2cb07d6c" - integrity sha512-sqbjZKK7kf44hfdE94EoX8MZNk0n7HeW37O4YrVGCF4wzgQjp+akPAkfUK5LZ6KuR/6sqeAVuXHji+RzQgOn5A== + version "9.1.7" + resolved "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz#d46a38035d101b46a70456a850ff4201344c0b2d" + integrity sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA== iconv-lite@^0.4.24: version "0.4.24" From 6320faa7646a9543e9c8467af91ce43f3de69c4e Mon Sep 17 00:00:00 2001 From: Aliaksandr Bahdanau <122269567+a-bahdanau@users.noreply.github.com> Date: Tue, 19 Nov 2024 21:46:28 +0300 Subject: [PATCH 24/24] feat(docs): add DeDust cookbook (#954) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Novus Nota <68142933+novusnota@users.noreply.github.com> Co-authored-by: Швец Андрей Евгеньевич <91282981+Shvandre@users.noreply.github.com> --- CHANGELOG.md | 3 +- .../content/docs/cookbook/dexes/dedust.mdx | 434 +++++++++++++++++- 2 files changed, 434 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9e4bfac5..572859565 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - New CSpell dictionaries: TVM instructions and adjusted list of Fift words: PR [#881](https://github.com/tact-lang/tact/pull/881) - Docs: the `description` property to the frontmatter of the each page for better SEO: PR [#916](https://github.com/tact-lang/tact/pull/916) - Docs: Google Analytics tags per every page: PR [#921](https://github.com/tact-lang/tact/pull/921) -- Docs: Added NFTs cookbook: PR [#958](https://github.com/tact-lang/tact/pull/958) +- Docs: added NFTs cookbook: PR [#958](https://github.com/tact-lang/tact/pull/958) - Ability to specify a compile-time method ID expression for getters: PR [#922](https://github.com/tact-lang/tact/pull/922) and PR [#932](https://github.com/tact-lang/tact/pull/932) - Destructuring of structs and messages: PR [#856](https://github.com/tact-lang/tact/pull/856), PR [#964](https://github.com/tact-lang/tact/pull/964), PR [#969](https://github.com/tact-lang/tact/pull/969) - Docs: automatic links to Web IDE from all code blocks: PR [#994](https://github.com/tact-lang/tact/pull/994) @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The `replace` and `replaceGet` methods for the `Map` type: PR [#941](https://github.com/tact-lang/tact/pull/941) - Utility for logging errors in code that was supposed to be unreachable: PR [#991](https://github.com/tact-lang/tact/pull/991) - Docs: `preloadRef` method for the `Slice` type: PR [#1044](https://github.com/tact-lang/tact/pull/1044) +- Docs: added DeDust cookbook: PR [#954](https://github.com/tact-lang/tact/pull/954) ### Changed diff --git a/docs/src/content/docs/cookbook/dexes/dedust.mdx b/docs/src/content/docs/cookbook/dexes/dedust.mdx index 9c95527f3..b55d6739a 100644 --- a/docs/src/content/docs/cookbook/dexes/dedust.mdx +++ b/docs/src/content/docs/cookbook/dexes/dedust.mdx @@ -7,8 +7,438 @@ sidebar: [DeDust](https://dedust.io) is a decentralized exchange (DEX) and automated market maker (AMM) built natively on [TON Blockchain](https://ton.org) and [DeDust Protocol 2.0](https://docs.dedust.io/reference/tlb-schemes). DeDust is designed with a meticulous attention to user experience (UX), gas efficiency, and extensibility. -:::danger[Not implemented] +Before going further, familiarize yourself with the following: - This page is a stub until it gets new content in [#146](https://github.com/tact-lang/tact-docs/issues/146). +* [Receiving messages](/book/receive/) +* [Sending messages](/book/send/) +* [Fungible Tokens (Jettons)](/cookbook/jettons/) +* [DeDust Docs: Concepts](https://docs.dedust.io/docs/concepts) + +## Swaps + +Read more about swaps in the [DeDust documentation](https://docs.dedust.io/docs/swaps). + +:::caution + + It's important to ensure that contracts are deployed. Sending funds to an inactive contract could result in irretrievable loss. + +::: + +All kinds of swaps use `SwapStep{:tact}` and `SwapParams{:tact}` structures: + +```tact +/// https://docs.dedust.io/reference/tlb-schemes#swapstep +struct SwapStep { + // The pool that will do the swapping, i.e. pairs like TON/USDT or USDT/DUST + poolAddress: Address; + + // A kind of swap to make, can only be 0 as of now + kind: Int as uint1 = 0; + + // Minimum output of the swap + // If the actual value is less than specified, the swap will be rejected + limit: Int as coins = 0; + + // Reference to the next step, which can be used for multi-hop swaps + // The type here is actually `SwapStep?`, + // but specifying recursive types isn't allowed in Tact yet + nextStep: Cell?; +} + +/// https://docs.dedust.io/reference/tlb-schemes#swapparams +struct SwapParams { + // Specifies a deadline for the swap to reject the swap coming to the pool late + // Accepts the number of seconds passed since the UNIX Epoch + // Defaults to 0, which removes the deadline + deadline: Int as uint32 = 0; + + // Specifies an address where funds will be sent after the swap + // Defaults to `null`, which makes the swap use the sender's address + recipientAddress: Address? = null; + + // Referral address, required for the referral program of DeDust + // Defaults to `null` + referralAddress: Address? = null; + + // Custom payload that will be attached to the fund transfer upon a successful swap + // Defaults to `null` + fulfillPayload: Cell? = null; + + // Custom payload that will be attached to the fund transfer upon a rejected swap + // Defaults to `null` + rejectPayload: Cell? = null; +} +``` + +### Swap Toncoin for any Jetton + +:::note +The guides below use the [Jetton vault](https://docs.dedust.io/docs/concepts#vault). To obtain its address for your Jetton, refer to [this guide](https://docs.dedust.io/docs/swaps#step-1-find-the-vault-scale). +::: + +```tact +/// https://docs.dedust.io/reference/tlb-schemes#message-swap +message(0xea06185d) NativeSwap { + // Unique identifier used to trace transactions across multiple contracts + // Defaults to 0, which means we don't mark messages to trace their chains + queryId: Int as uint64 = 0; + + // Toncoin amount for the swap + amount: Int as coins; + + // Inlined fields of SwapStep Struct + poolAddress: Address; + kind: Int as uint1 = 0; + limit: Int as coins = 0; + nextStep: SwapStep? = null; + + // Set of parameters relevant for the whole swap + swapParams: SwapParams; +} + +// Let's say `swapAmount` is `ton("0.1")`, which is 10000000 nanoToncoins +fun swapToncoinForUSDT(swapAmount: Int) { + send(SendParameters{ + // Address of TON vault to send the message to + to: address("EQDa4VOnTYlLvDJ0gZjNYm5PXfSmmtL6Vs6A_CZEtXCNICq_"), + // Amount to swap plus a trade fee + value: swapAmount + ton("0.2"), + body: NativeSwap{ + amount: swapAmount, + // Address of the swap pool, which is the TON/USDT pair in this case + poolAddress: address("EQA-X_yo3fzzbDbJ_0bzFWKqtRuZFIRa1sJsveZJ1YpViO3r"), + // Set of parameters relevant for the whole swap + swapParams: SwapParams{}, // use defaults + }.toCell(), + }); +} + +// +// Helper Structs described earlier on this page +// + +struct SwapStep { + poolAddress: Address; + kind: Int as uint1 = 0; + limit: Int as coins = 0; + nextStep: Cell?; +} + +struct SwapParams { + deadline: Int as uint32 = 0; + recipientAddress: Address? = null; + referralAddress: Address? = null; + fulfillPayload: Cell? = null; + rejectPayload: Cell? = null; +} +``` + +### Swap a Jetton for another Jetton or Toncoin + +```tact +/// https://docs.dedust.io/reference/tlb-schemes#message-swap-1 +message(0xe3a0d482) JettonSwapPayload { + // Inlined fields of SwapStep Struct + poolAddress: Address; + kind: Int as uint1 = 0; + limit: Int as coins = 0; + nextStep: SwapStep? = null; + + // Set of parameters relevant for the whole swap + swapParams: SwapParams; +} + +/// NOTE: To calculate and provide Jetton wallet address for the target user, +/// make sure to check links after this code snippet +fun swapJetton(targetJettonWalletAddress: Address) { + send(SendParameters{ + to: targetJettonWalletAddress, + value: ton("0.3"), + body: JettonTransfer{ + // Unique identifier used to trace transactions across multiple contracts + // Set to 0, which means we don't mark messages to trace their chains + queryId: 0, + // Jetton amount for the swap + amount: 10, // NOTE: change to yours + // Address of the Jetton vault to the send message to + destination: address("EQAYqo4u7VF0fa4DPAebk4g9lBytj2VFny7pzXR0trjtXQaO"), + // Where to return the exceeding funds + responseDestination: myAddress(), + forwardTonAmount: ton("0.25"), + forwardPayload: JettonSwapPayload{ + // Address of the swap pool, which is the TON/USDT pair in this case + poolAddress: address("EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs"), + // Set of parameters relevant for the whole swap + swapParams: SwapParams{}, // use defaults + }.toCell(), + }.toCell(), + }); +} + +// +// Helper Structs described earlier on this page +// + +struct SwapStep { + poolAddress: Address; + kind: Int as uint1 = 0; + limit: Int as coins = 0; + nextStep: Cell?; +} + +struct SwapParams { + deadline: Int as uint32 = 0; + recipientAddress: Address? = null; + referralAddress: Address? = null; + fulfillPayload: Cell? = null; + rejectPayload: Cell? = null; +} + +// +// Messages from the Jetton standard +// + +message(0xf8a7ea5) JettonTransfer { + queryId: Int as uint64; + amount: Int as coins; + destination: Address; + responseDestination: Address; + customPayload: Cell? = null; + forwardTonAmount: Int as coins; + forwardPayload: Cell?; // slightly adjusted +} +``` + +:::note[Useful links:] + + [Retrieving Jetton wallet address in TON Docs](https://docs.ton.org/develop/dapps/asset-processing/jettons#retrieving-jetton-wallet-addresses-for-a-given-user)\ + [How to calculate user's Jetton wallet address (offline)?](https://docs.ton.org/v3/guidelines/dapps/cookbook#how-to-calculate-users-jetton-wallet-address-offline) + +::: + +## Liquidity Provisioning + +To provide liquidity to a particular DeDust pool, you must provide both assets. The pool will then issue special _LP tokens_ to the depositor's address. + +Read more about liquidity provisioning in the [DeDust documentation](https://docs.dedust.io/docs/liquidity-provisioning). + +```tact +import "@stdlib/deploy"; + +/// https://docs.dedust.io/reference/tlb-schemes#message-deposit_liquidity-1 +message(0x40e108d6) JettonDepositLiquidity { + // Pool type: 0 for volatile, 1 for stable + // Volatile pool is based on the "Constant Product" formula + // Stable-swap pool is optimized for assets of near-equal value, + // e.g. USDT/USDC, TON/stTON, etc. + poolType: Int as uint1; + + // Provided assets + asset0: Asset; + asset1: Asset; + + // Minimal amount of LP tokens to be received + // If there's less liquidity provided, the provisioning will be rejected + // Defaults to 0, makes this value ignored + minimalLpAmount: Int as coins = 0; + + // Target amount of the first asset + targetBalances0: Int as coins; + + // Target amount of the second asset + targetBalances1: Int as coins; + + // Custom payload attached to the transaction if the provisioning is successful + // Defaults to `null`, which means no payload + fulfillPayload: Cell? = null; + + // Custom payload attached to the transaction if the provisioning is rejected + // Defaults to `null`, which means no payload + rejectPayload: Cell? = null; +} + +/// https://docs.dedust.io/reference/tlb-schemes#message-deposit_liquidity +message(0xd55e4686) NativeDepositLiquidity { + // Unique identifier used to trace transactions across multiple contracts + // Defaults to 0, which means we don't mark messages to trace their chains + queryId: Int as uint64 = 0; + + // Toncoin amount for the deposit + amount: Int as coins; + + // Inlined fields of JettonDepositLiquidity Message without the opcode prefix + poolType: Int as uint1; + asset0: Asset; + asset1: Asset; + minimalLpAmount: Int as coins = 0; + targetBalances0: Int as coins; + targetBalances1: Int as coins; + fulfillPayload: Cell? = null; + rejectPayload: Cell? = null; +} + +/// https://docs.dedust.io/reference/tlb-schemes#asset +struct Asset { + // Specify 0 for native (TON) and omit all following fields + // Specify 1 for Jetton and then you must set non-null values for the following fields + type: Int as uint4; + + workchain: Int as uint8 = 0; // Both this zeroes will be removed during .build() function. Only type will remain. + address: Int as uint256 = 0; +} + +const PoolTypeVolatile: Int = 0; +const PoolTypeStable: Int = 1; + +const AssetTypeNative: Int = 0b0000; +const AssetTypeJetton: Int = 0b0001; + +const JettonProvideLpGas: Int = ton("0.5"); +const JettonProvideLpGasFwd: Int = ton("0.4"); +const TonProvideLpGas: Int = ton("0.15"); + +// This example directly uses the provided `myJettonWalletAddress` +// In real-world scenarios, it's more reliable to calculate this address on-chain or save it during initialization to prevent any issues +fun provideLiquidity(myJettonWalletAddress: Address) { + let jettonMasterRaw = parseStdAddress( + address("EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs") + .asSlice() + ); + + // Step 1. Prepare input + let jettonAmount = ton("1"); + let tonAmount = ton("1"); + + let asset0 = Asset{ + type: AssetTypeNative, + }; + let asset1 = Asset{ + type: AssetTypeJetton, + workchain: jettonMasterRaw.workchain, + address: jettonMasterRaw.address, + }; + + // Step 2. Deposit Jetton to Vault + let jettonDepositBody = JettonDepositLiquidity{ + poolType: PoolTypeVolatile, + asset0, + asset1, + targetBalances0: tonAmount, + targetBalances1: jettonAmount, + }.build(); // notice the .build() and not .toCell(), + // since we want some custom serialization logic! + + send(SendParameters{ + to: myJettonWalletAddress, + value: JettonProvideLpGas, + body: JettonTransfer{ + queryId: 42, + amount: jettonAmount, + // Jetton Vault + destination: address("EQAYqo4u7VF0fa4DPAebk4g9lBytj2VFny7pzXR0trjtXQaO"), + responseDestination: myAddress(), + forwardTonAmount: JettonProvideLpGasFwd, + forwardPayload: jettonDepositBody, + }.toCell() + }); + + // Step 3. Deposit TON to Vault + let nativeDepositBody = NativeDepositLiquidity{ + queryId: 42, + amount: tonAmount, + poolType: PoolTypeVolatile, + asset0, + asset1, + targetBalances0: tonAmount, + targetBalances1: jettonAmount, + }.build(); // notice the .build() and not .toCell(), + // since we want some custom serialization logic! + + send(SendParameters{ + to: address("EQDa4VOnTYlLvDJ0gZjNYm5PXfSmmtL6Vs6A_CZEtXCNICq_"), + value: tonAmount + TonProvideLpGas, + body: nativeDepositBody, + }); +} + +// +// Helper extension functions to build respective Structs and Messages +// + +extends fun build(self: Asset): Cell { + let assetBuilder = beginCell() + .storeUint(self.type, 4); + + if (self.type == AssetTypeNative) { + return assetBuilder.endCell(); + } + + if (self.type == AssetTypeJetton) { + return assetBuilder + .storeUint(self.workchain, 8) + .storeUint(self.address, 256) + .endCell(); + } + + // Unknown asset type + return beginCell().endCell(); +} + +extends fun build(self: JettonDepositLiquidity): Cell { + return beginCell() + .storeUint(0x40e108d6, 32) + .storeUint(self.poolType, 1) + .storeSlice(self.asset0.build().asSlice()) + .storeSlice(self.asset1.build().asSlice()) + .storeCoins(self.minimalLpAmount) + .storeCoins(self.targetBalances0) + .storeCoins(self.targetBalances1) + .storeMaybeRef(self.fulfillPayload) + .storeMaybeRef(self.rejectPayload) + .endCell(); +} + +extends fun build(self: NativeDepositLiquidity): Cell { + return beginCell() + .storeUint(0xd55e4686, 32) + .storeUint(self.queryId, 64) + .storeCoins(self.amount) + .storeUint(self.poolType, 1) + .storeSlice(self.asset0.build().asSlice()) + .storeSlice(self.asset1.build().asSlice()) + .storeRef( + beginCell() + .storeCoins(self.minimalLpAmount) + .storeCoins(self.targetBalances0) + .storeCoins(self.targetBalances1) + .endCell() + ) + .storeMaybeRef(self.fulfillPayload) + .storeMaybeRef(self.rejectPayload) + .endCell(); +} + +// +// Messages from the Jetton standard +// + +message(0xf8a7ea5) JettonTransfer { + queryId: Int as uint64; + amount: Int as coins; + destination: Address; + responseDestination: Address?; + customPayload: Cell? = null; + forwardTonAmount: Int as coins; + forwardPayload: Cell?; // slightly adjusted +} +``` + +### Withdraw liquidity + +To withdraw liquidity, burning LP tokens is required. You can refer to examples of Jetton burning in the [respective section of Jettons Cookbook page](/cookbook/jettons#burning-jetton). However, more Toncoins should be added than for the normal burn, since adding too few may result in LP tokens being burned, but no (or only partial) liquidity being sent from the pool. Therefore, consider attaching at least $0.5$ Toncoin — excess amount will be returned. + +:::tip[Hey there!] + +Didn't find your favorite example of DeDust interaction? Have cool implementations in mind? [Contributions are welcome!](https://github.com/tact-lang/tact/issues) :::