1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-02-12 19:22:37 +00:00

Merge branch 'testnet' into accelerator

This commit is contained in:
SpyCheese 2024-11-18 12:37:18 +03:00
commit 62ede1851b
216 changed files with 23301 additions and 246 deletions

View file

@ -17,4 +17,4 @@ ENV CC clang
ENV CXX clang++
ENV CCACHE_DISABLE 1
RUN cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DPORTABLE=1 -DTON_ARCH= -DCMAKE_CXX_FLAGS="-mavx2" ..
RUN ninja storage-daemon storage-daemon-cli tonlibjson blockchain-explorer fift func validator-engine validator-engine-console create-state generate-random-id create-hardfork dht-server lite-client
RUN ninja storage-daemon storage-daemon-cli tonlibjson blockchain-explorer fift func tolk validator-engine validator-engine-console create-state generate-random-id create-hardfork dht-server lite-client

View file

@ -17,4 +17,4 @@ ENV CC clang
ENV CXX clang++
ENV CCACHE_DISABLE 1
RUN cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DPORTABLE=1 -DTON_ARCH= -DCMAKE_CXX_FLAGS="-mavx2" ..
RUN ninja storage-daemon storage-daemon-cli tonlibjson blockchain-explorer fift func validator-engine validator-engine-console create-state generate-random-id create-hardfork dht-server lite-client
RUN ninja storage-daemon storage-daemon-cli tonlibjson blockchain-explorer fift func tolk validator-engine validator-engine-console create-state generate-random-id create-hardfork dht-server lite-client

View file

@ -17,4 +17,4 @@ ENV CC clang
ENV CXX clang++
ENV CCACHE_DISABLE 1
RUN cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DPORTABLE=1 -DTON_ARCH= ..
RUN ninja storage-daemon storage-daemon-cli tonlibjson blockchain-explorer fift func validator-engine validator-engine-console create-state generate-random-id dht-server lite-client
RUN ninja storage-daemon storage-daemon-cli tonlibjson blockchain-explorer fift func tolk validator-engine validator-engine-console create-state generate-random-id dht-server lite-client

View file

@ -17,4 +17,4 @@ ENV CC clang
ENV CXX clang++
ENV CCACHE_DISABLE 1
RUN cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DPORTABLE=1 -DTON_ARCH= ..
RUN ninja storage-daemon storage-daemon-cli tonlibjson blockchain-explorer fift func validator-engine validator-engine-console create-state generate-random-id dht-server lite-client
RUN ninja storage-daemon storage-daemon-cli tonlibjson blockchain-explorer fift func tolk validator-engine validator-engine-console create-state generate-random-id dht-server lite-client

View file

@ -167,6 +167,14 @@ jobs:
asset_name: func.exe
tag: ${{ steps.tag.outputs.TAG }}
- name: Upload Windows 2019 single artifact - tolk
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-win-binaries/tolk.exe
asset_name: tolk.exe
tag: ${{ steps.tag.outputs.TAG }}
- name: Upload Windows 2019 single artifact - lite-client
uses: svenstaro/upload-release-action@v2
with:
@ -257,6 +265,14 @@ jobs:
asset_name: func-mac-x86-64
tag: ${{ steps.tag.outputs.TAG }}
- name: Upload Mac x86-64 single artifact - tolk
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-x86_64-macos-binaries/tolk
asset_name: tolk-mac-x86-64
tag: ${{ steps.tag.outputs.TAG }}
- name: Upload Mac x86-64 single artifact - lite-client
uses: svenstaro/upload-release-action@v2
with:
@ -348,6 +364,14 @@ jobs:
asset_name: func-mac-arm64
tag: ${{ steps.tag.outputs.TAG }}
- name: Upload Mac arm64 single artifact - tolk
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-arm64-macos-binaries/tolk
asset_name: tolk-mac-arm64
tag: ${{ steps.tag.outputs.TAG }}
- name: Upload Mac arm64 single artifact - lite-client
uses: svenstaro/upload-release-action@v2
with:
@ -438,6 +462,14 @@ jobs:
asset_name: func-linux-x86_64
tag: ${{ steps.tag.outputs.TAG }}
- name: Upload Linux x86-64 single artifact - tolk
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-x86_64-linux-binaries/tolk
asset_name: tolk-linux-x86_64
tag: ${{ steps.tag.outputs.TAG }}
- name: Upload Linux x86-64 single artifact - lite-client
uses: svenstaro/upload-release-action@v2
with:

View file

@ -29,6 +29,7 @@ jobs:
artifacts/lite-client -V
artifacts/fift -V
artifacts/func -V
artifacts/tolk -v
- name: Upload artifacts
uses: actions/upload-artifact@master

View file

@ -33,6 +33,7 @@ jobs:
artifacts/lite-client -V
artifacts/fift -V
artifacts/func -V
artifacts/tolk -v
- name: Upload artifacts
uses: actions/upload-artifact@master

View file

@ -29,6 +29,7 @@ jobs:
artifacts/lite-client -V
artifacts/fift -V
artifacts/func -V
artifacts/tolk -v
- name: Upload artifacts
uses: actions/upload-artifact@master

2
.gitignore vendored
View file

@ -13,6 +13,8 @@ test/regression-tests.cache/
**/*build*/
.idea
.vscode
.DS_Store
dev/
zlib/
libsodium/
libmicrohttpd-0.9.77-w32-bin/

View file

@ -413,6 +413,7 @@ add_subdirectory(adnl)
add_subdirectory(crypto)
add_subdirectory(lite-client)
add_subdirectory(emulator)
add_subdirectory(tolk)
#BEGIN tonlib
add_subdirectory(tonlib)
@ -626,6 +627,30 @@ if (NOT NIX)
endif()
endif()
# Tolk tests
if (NOT NIX)
if (MSVC)
set(PYTHON_VER "python")
else()
set(PYTHON_VER "python3")
endif()
add_test(
NAME test-tolk
COMMAND ${PYTHON_VER} tolk-tester.py tests/
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tolk-tester)
if (WIN32)
set_property(TEST test-tolk PROPERTY ENVIRONMENT
"TOLK_EXECUTABLE=${CMAKE_CURRENT_BINARY_DIR}/tolk/tolk.exe"
"FIFT_EXECUTABLE=${CMAKE_CURRENT_BINARY_DIR}/crypto/fift.exe"
"FIFTPATH=${CMAKE_CURRENT_SOURCE_DIR}/crypto/fift/lib/")
else()
set_property(TEST test-tolk PROPERTY ENVIRONMENT
"TOLK_EXECUTABLE=${CMAKE_CURRENT_BINARY_DIR}/tolk/tolk"
"FIFT_EXECUTABLE=${CMAKE_CURRENT_BINARY_DIR}/crypto/fift"
"FIFTPATH=${CMAKE_CURRENT_SOURCE_DIR}/crypto/fift/lib/")
endif()
endif()
#BEGIN internal
if (NOT TON_ONLY_TONLIB)
add_test(test-adnl test-adnl)

View file

@ -153,7 +153,7 @@ test $? -eq 0 || { echo "Can't configure ton"; exit 1; }
if [ "$with_tests" = true ]; then
ninja storage-daemon storage-daemon-cli blockchain-explorer \
tonlib tonlibjson tonlib-cli validator-engine func fift \
tonlib tonlibjson tonlib-cli validator-engine func tolk fift \
lite-client pow-miner validator-engine-console generate-random-id json2tlo dht-server \
http-proxy rldp-http-proxy adnl-proxy create-state create-hardfork tlbc emulator \
test-ed25519 test-ed25519-crypto test-bigint test-vm test-fift test-cells test-smartcont \
@ -162,30 +162,12 @@ if [ "$with_tests" = true ]; then
test $? -eq 0 || { echo "Can't compile ton"; exit 1; }
else
ninja storage-daemon storage-daemon-cli blockchain-explorer \
tonlib tonlibjson tonlib-cli validator-engine func fift \
tonlib tonlibjson tonlib-cli validator-engine func tolk fift \
lite-client pow-miner validator-engine-console generate-random-id json2tlo dht-server \
http-proxy rldp-http-proxy adnl-proxy create-state create-hardfork tlbc emulator
test $? -eq 0 || { echo "Can't compile ton"; exit 1; }
fi
strip -s storage/storage-daemon/storage-daemon
strip -s storage/storage-daemon/storage-daemon-cli
strip -s blockchain-explorer/blockchain-explorer
strip -s crypto/fift
strip -s crypto/func
strip -s crypto/create-state
strip -s crypto/tlbc
strip -s validator-engine-console/validator-engine-console
strip -s tonlib/tonlib-cli
strip -s http/http-proxy
strip -s rldp-http-proxy/rldp-http-proxy
strip -s dht-server/dht-server
strip -s lite-client/lite-client
strip -s validator-engine/validator-engine
strip -s utils/generate-random-id
strip -s utils/json2tlo
strip -s adnl/adnl-proxy
cd ..
if [ "$with_artifacts" = true ]; then
@ -197,6 +179,7 @@ if [ "$with_artifacts" = true ]; then
cp build/blockchain-explorer/blockchain-explorer artifacts/
cp build/crypto/fift artifacts/
cp build/crypto/func artifacts/
cp build/tolk/tolk artifacts/
cp build/crypto/create-state artifacts/
cp build/crypto/tlbc artifacts/
cp build/validator-engine-console/validator-engine-console artifacts/

View file

@ -81,7 +81,7 @@ test $? -eq 0 || { echo "Can't configure ton"; exit 1; }
if [ "$with_tests" = true ]; then
ninja storage-daemon storage-daemon-cli blockchain-explorer \
tonlib tonlibjson tonlib-cli validator-engine func fift \
tonlib tonlibjson tonlib-cli validator-engine func tolk fift \
lite-client pow-miner validator-engine-console generate-random-id json2tlo dht-server \
http-proxy rldp-http-proxy adnl-proxy create-state create-hardfork tlbc emulator \
test-ed25519 test-ed25519-crypto test-bigint test-vm test-fift test-cells test-smartcont \
@ -90,31 +90,12 @@ if [ "$with_tests" = true ]; then
test $? -eq 0 || { echo "Can't compile ton"; exit 1; }
else
ninja storage-daemon storage-daemon-cli blockchain-explorer \
tonlib tonlibjson tonlib-cli validator-engine func fift \
tonlib tonlibjson tonlib-cli validator-engine func tolk fift \
lite-client pow-miner validator-engine-console generate-random-id json2tlo dht-server \
http-proxy rldp-http-proxy adnl-proxy create-state create-hardfork tlbc emulator
test $? -eq 0 || { echo "Can't compile ton"; exit 1; }
fi
strip -s storage/storage-daemon/storage-daemon
strip -s storage/storage-daemon/storage-daemon-cli
strip -s blockchain-explorer/blockchain-explorer
strip -s crypto/fift
strip -s crypto/func
strip -s crypto/create-state
strip -s crypto/tlbc
strip -s validator-engine-console/validator-engine-console
strip -s tonlib/tonlib-cli
strip -s http/http-proxy
strip -s rldp-http-proxy/rldp-http-proxy
strip -s dht-server/dht-server
strip -s lite-client/lite-client
strip -s validator-engine/validator-engine
strip -s utils/generate-random-id
strip -s utils/json2tlo
strip -s adnl/adnl-proxy
cd ..
if [ "$with_artifacts" = true ]; then
@ -126,6 +107,7 @@ if [ "$with_artifacts" = true ]; then
cp build/blockchain-explorer/blockchain-explorer artifacts/
cp build/crypto/fift artifacts/
cp build/crypto/func artifacts/
cp build/tolk/tolk artifacts/
cp build/crypto/create-state artifacts/
cp build/crypto/tlbc artifacts/
cp build/validator-engine-console/validator-engine-console artifacts/

View file

@ -144,7 +144,7 @@ cmake -GNinja .. \
test $? -eq 0 || { echo "Can't configure ton"; exit 1; }
if [ "$with_tests" = true ]; then
ninja storage-daemon storage-daemon-cli fift func tonlib tonlibjson tonlib-cli \
ninja storage-daemon storage-daemon-cli fift func tolk tonlib tonlibjson tonlib-cli \
validator-engine lite-client pow-miner validator-engine-console blockchain-explorer \
generate-random-id json2tlo dht-server http-proxy rldp-http-proxy \
adnl-proxy create-state emulator test-ed25519 test-ed25519-crypto test-bigint \
@ -153,35 +153,13 @@ ninja storage-daemon storage-daemon-cli fift func tonlib tonlibjson tonlib-cli \
test-fec test-tddb test-db test-validator-session-state test-emulator
test $? -eq 0 || { echo "Can't compile ton"; exit 1; }
else
ninja storage-daemon storage-daemon-cli fift func tonlib tonlibjson tonlib-cli \
ninja storage-daemon storage-daemon-cli fift func tolk tonlib tonlibjson tonlib-cli \
validator-engine lite-client pow-miner validator-engine-console blockchain-explorer \
generate-random-id json2tlo dht-server http-proxy rldp-http-proxy \
adnl-proxy create-state emulator
test $? -eq 0 || { echo "Can't compile ton"; exit 1; }
fi
strip -s storage/storage-daemon/storage-daemon \
storage/storage-daemon/storage-daemon-cli \
blockchain-explorer/blockchain-explorer \
crypto/fift \
crypto/tlbc \
crypto/func \
crypto/create-state \
validator-engine-console/validator-engine-console \
tonlib/tonlib-cli \
tonlib/libtonlibjson.so.0.5 \
http/http-proxy \
rldp-http-proxy/rldp-http-proxy \
dht-server/dht-server \
lite-client/lite-client \
validator-engine/validator-engine \
utils/generate-random-id \
utils/json2tlo \
adnl/adnl-proxy \
emulator/libemulator.*
test $? -eq 0 || { echo "Can't strip final binaries"; exit 1; }
# simple binaries' test
./storage/storage-daemon/storage-daemon -V || exit 1
./validator-engine/validator-engine -V || exit 1
@ -195,7 +173,7 @@ if [ "$with_artifacts" = true ]; then
mkdir artifacts
mv build/tonlib/libtonlibjson.so.0.5 build/tonlib/libtonlibjson.so
cp build/storage/storage-daemon/storage-daemon build/storage/storage-daemon/storage-daemon-cli \
build/crypto/fift build/crypto/tlbc build/crypto/func build/crypto/create-state build/blockchain-explorer/blockchain-explorer \
build/crypto/fift build/crypto/tlbc build/crypto/func build/tolk/tolk build/crypto/create-state build/blockchain-explorer/blockchain-explorer \
build/validator-engine-console/validator-engine-console build/tonlib/tonlib-cli \
build/tonlib/libtonlibjson.so build/http/http-proxy build/rldp-http-proxy/rldp-http-proxy \
build/dht-server/dht-server build/lite-client/lite-client build/validator-engine/validator-engine \

View file

@ -52,7 +52,7 @@ cmake -GNinja -DTON_USE_JEMALLOC=ON .. \
test $? -eq 0 || { echo "Can't configure ton"; exit 1; }
if [ "$with_tests" = true ]; then
ninja storage-daemon storage-daemon-cli fift func tonlib tonlibjson tonlib-cli \
ninja storage-daemon storage-daemon-cli fift func tolk tonlib tonlibjson tonlib-cli \
validator-engine lite-client pow-miner validator-engine-console blockchain-explorer \
generate-random-id json2tlo dht-server http-proxy rldp-http-proxy \
adnl-proxy create-state emulator test-ed25519 test-ed25519-crypto test-bigint \
@ -61,35 +61,13 @@ ninja storage-daemon storage-daemon-cli fift func tonlib tonlibjson tonlib-cli \
test-fec test-tddb test-db test-validator-session-state test-emulator
test $? -eq 0 || { echo "Can't compile ton"; exit 1; }
else
ninja storage-daemon storage-daemon-cli fift func tonlib tonlibjson tonlib-cli \
ninja storage-daemon storage-daemon-cli fift func tolk tonlib tonlibjson tonlib-cli \
validator-engine lite-client pow-miner validator-engine-console blockchain-explorer \
generate-random-id json2tlo dht-server http-proxy rldp-http-proxy \
adnl-proxy create-state emulator
test $? -eq 0 || { echo "Can't compile ton"; exit 1; }
fi
strip -s storage/storage-daemon/storage-daemon \
storage/storage-daemon/storage-daemon-cli \
blockchain-explorer/blockchain-explorer \
crypto/fift \
crypto/tlbc \
crypto/func \
crypto/create-state \
validator-engine-console/validator-engine-console \
tonlib/tonlib-cli \
tonlib/libtonlibjson.so.0.5 \
http/http-proxy \
rldp-http-proxy/rldp-http-proxy \
dht-server/dht-server \
lite-client/lite-client \
validator-engine/validator-engine \
utils/generate-random-id \
utils/json2tlo \
adnl/adnl-proxy \
emulator/libemulator.*
test $? -eq 0 || { echo "Can't strip final binaries"; exit 1; }
# simple binaries' test
./storage/storage-daemon/storage-daemon -V || exit 1
./validator-engine/validator-engine -V || exit 1
@ -105,7 +83,7 @@ if [ "$with_artifacts" = true ]; then
mkdir artifacts
mv build/tonlib/libtonlibjson.so.0.5 build/tonlib/libtonlibjson.so
cp build/storage/storage-daemon/storage-daemon build/storage/storage-daemon/storage-daemon-cli \
build/crypto/fift build/crypto/tlbc build/crypto/func build/crypto/create-state build/blockchain-explorer/blockchain-explorer \
build/crypto/fift build/crypto/tlbc build/crypto/func build/tolk/tolk build/crypto/create-state build/blockchain-explorer/blockchain-explorer \
build/validator-engine-console/validator-engine-console build/tonlib/tonlib-cli \
build/tonlib/libtonlibjson.so build/http/http-proxy build/rldp-http-proxy/rldp-http-proxy \
build/dht-server/dht-server build/lite-client/lite-client build/validator-engine/validator-engine \

View file

@ -155,7 +155,7 @@ IF %errorlevel% NEQ 0 (
)
IF "%1"=="-t" (
ninja storage-daemon storage-daemon-cli blockchain-explorer fift func tonlib tonlibjson ^
ninja storage-daemon storage-daemon-cli blockchain-explorer fift func tolk tonlib tonlibjson ^
tonlib-cli validator-engine lite-client pow-miner validator-engine-console generate-random-id ^
json2tlo dht-server http-proxy rldp-http-proxy adnl-proxy create-state create-hardfork emulator ^
test-ed25519 test-ed25519-crypto test-bigint test-vm test-fift test-cells test-smartcont test-net ^
@ -166,7 +166,7 @@ IF %errorlevel% NEQ 0 (
exit /b %errorlevel%
)
) else (
ninja storage-daemon storage-daemon-cli blockchain-explorer fift func tonlib tonlibjson ^
ninja storage-daemon storage-daemon-cli blockchain-explorer fift func tolk tonlib tonlibjson ^
tonlib-cli validator-engine lite-client pow-miner validator-engine-console generate-random-id ^
json2tlo dht-server http-proxy rldp-http-proxy adnl-proxy create-state create-hardfork emulator
IF %errorlevel% NEQ 0 (
@ -204,6 +204,7 @@ build\blockchain-explorer\blockchain-explorer.exe ^
build\crypto\fift.exe ^
build\crypto\tlbc.exe ^
build\crypto\func.exe ^
build\tolk\tolk.exe ^
build\crypto\create-state.exe ^
build\validator-engine-console\validator-engine-console.exe ^
build\tonlib\tonlib-cli.exe ^

View file

@ -156,7 +156,7 @@ IF %errorlevel% NEQ 0 (
)
IF "%1"=="-t" (
ninja storage-daemon storage-daemon-cli blockchain-explorer fift func tonlib tonlibjson ^
ninja storage-daemon storage-daemon-cli blockchain-explorer fift func tolk tonlib tonlibjson ^
tonlib-cli validator-engine lite-client pow-miner validator-engine-console generate-random-id ^
json2tlo dht-server http-proxy rldp-http-proxy adnl-proxy create-state create-hardfork emulator ^
test-ed25519 test-ed25519-crypto test-bigint test-vm test-fift test-cells test-smartcont test-net ^
@ -167,7 +167,7 @@ IF %errorlevel% NEQ 0 (
exit /b %errorlevel%
)
) else (
ninja storage-daemon storage-daemon-cli blockchain-explorer fift func tonlib tonlibjson ^
ninja storage-daemon storage-daemon-cli blockchain-explorer fift func tolk tonlib tonlibjson ^
tonlib-cli validator-engine lite-client pow-miner validator-engine-console generate-random-id ^
json2tlo dht-server http-proxy rldp-http-proxy adnl-proxy create-state create-hardfork emulator
IF %errorlevel% NEQ 0 (
@ -205,6 +205,7 @@ build\blockchain-explorer\blockchain-explorer.exe ^
build\crypto\fift.exe ^
build\crypto\tlbc.exe ^
build\crypto\func.exe ^
build\tolk\tolk.exe ^
build\crypto\create-state.exe ^
build\validator-engine-console\validator-engine-console.exe ^
build\tonlib\tonlib-cli.exe ^

View file

@ -36,22 +36,3 @@ cp ./result/lib/libemulator.so artifacts/
cp ./result/lib/fift/* artifacts/lib/
cp -r ./result/share/ton/smartcont artifacts/
chmod -R +x artifacts
cd artifacts
sudo strip -s storage-daemon \
storage-daemon-cli \
blockchain-explorer \
fift \
tlbc \
func \
create-state \
validator-engine-console \
tonlib-cli \
http-proxy \
rldp-http-proxy \
dht-server \
lite-client \
validator-engine \
generate-random-id \
adnl-proxy \
libemulator.so \
libtonlibjson.so

View file

@ -36,22 +36,3 @@ cp ./result/lib/libemulator.so artifacts/
cp ./result/lib/fift/* artifacts/lib/
cp -r ./result/share/ton/smartcont artifacts/
chmod -R +x artifacts
cd artifacts
sudo strip -s storage-daemon \
storage-daemon-cli \
blockchain-explorer \
fift \
tlbc \
func \
create-state \
validator-engine-console \
tonlib-cli \
http-proxy \
rldp-http-proxy \
dht-server \
lite-client \
validator-engine \
generate-random-id \
adnl-proxy \
libemulator.so \
libtonlibjson.so

View file

@ -36,22 +36,3 @@ cp ./result/lib/libemulator.dylib artifacts/
cp ./result/lib/fift/* artifacts/lib/
cp -r ./result/share/ton/smartcont artifacts/
chmod -R +x artifacts
cd artifacts
sudo strip -xSX storage-daemon \
storage-daemon-cli \
blockchain-explorer \
fift \
tlbc \
func \
create-state \
validator-engine-console \
tonlib-cli \
http-proxy \
rldp-http-proxy \
dht-server \
lite-client \
validator-engine \
generate-random-id \
adnl-proxy \
libemulator.dylib \
libtonlibjson.dylib

View file

@ -1589,6 +1589,9 @@ forget @proclist forget @proccnt
{ }END> b> } : }END>c
{ }END>c <s } : }END>s
// This is the way how FunC assigns method_id for reserved functions.
// Note, that Tolk entrypoints have other names (`onInternalMessage`, etc.),
// but method_id is assigned not by Fift, but by Tolk code generation.
0 constant recv_internal
-1 constant recv_external
-2 constant run_ticktock

View file

@ -114,7 +114,7 @@ class MemoryFileLoader : public fift::FileLoader {
std::map<std::string, std::string, std::less<>> files_;
};
td::Result<fift::SourceLookup> create_source_lookup(std::string main, bool need_preamble = true, bool need_asm = true,
td::Result<fift::SourceLookup> create_source_lookup(std::string&& main, bool need_preamble = true, bool need_asm = true,
bool need_ton_util = true, bool need_lisp = true,
bool need_w3_code = true, bool need_fift_ext = true,
bool need_disasm = true, std::string dir = "") {
@ -189,7 +189,7 @@ td::Result<fift::SourceLookup> run_fift(fift::SourceLookup source_lookup, std::o
} // namespace
td::Result<FiftOutput> mem_run_fift(std::string source, std::vector<std::string> args, std::string fift_dir) {
std::stringstream ss;
TRY_RESULT(source_lookup, create_source_lookup(source, true, true, true, true, true, true, true, fift_dir));
TRY_RESULT(source_lookup, create_source_lookup(std::move(source), true, true, true, true, true, true, true, fift_dir));
TRY_RESULT_ASSIGN(source_lookup, run_fift(std::move(source_lookup), &ss, true, std::move(args)));
FiftOutput res;
res.source_lookup = std::move(source_lookup);
@ -207,19 +207,43 @@ td::Result<FiftOutput> mem_run_fift(SourceLookup source_lookup, std::vector<std:
td::Result<fift::SourceLookup> create_mem_source_lookup(std::string main, std::string fift_dir, bool need_preamble,
bool need_asm, bool need_ton_util, bool need_lisp,
bool need_w3_code) {
return create_source_lookup(main, need_preamble, need_asm, need_ton_util, need_lisp, need_w3_code, false, false,
return create_source_lookup(std::move(main), need_preamble, need_asm, need_ton_util, need_lisp, need_w3_code, false, false,
fift_dir);
}
td::Result<td::Ref<vm::Cell>> compile_asm(td::Slice asm_code, std::string fift_dir, bool is_raw) {
td::Result<td::Ref<vm::Cell>> compile_asm(td::Slice asm_code) {
std::stringstream ss;
TRY_RESULT(source_lookup,
create_source_lookup(PSTRING() << "\"Asm.fif\" include\n " << (is_raw ? "<{" : "") << asm_code << "\n"
<< (is_raw ? "}>c" : "") << " boc>B \"res\" B>file",
true, true, true, false, false, false, false, fift_dir));
std::string sb;
sb.reserve(asm_code.size() + 100);
sb.append("\"Asm.fif\" include\n <{\n");
sb.append(asm_code.data(), asm_code.size());
sb.append("\n}>c boc>B \"res\" B>file");
TRY_RESULT(source_lookup, create_source_lookup(std::move(sb), true, true, true, false, false, false, false));
TRY_RESULT(res, run_fift(std::move(source_lookup), &ss));
TRY_RESULT(boc, res.read_file("res"));
return vm::std_boc_deserialize(std::move(boc.data));
}
td::Result<CompiledProgramOutput> compile_asm_program(std::string&& program_code, const std::string& fift_dir) {
std::string main_fif;
main_fif.reserve(program_code.size() + 100);
main_fif.append(program_code.data(), program_code.size());
main_fif.append(R"( dup hashB B>X $>B "hex" B>file)"); // write codeHashHex to a file
main_fif.append(R"( boc>B B>base64 $>B "boc" B>file)"); // write codeBoc64 to a file
std::stringstream fift_output_stream;
TRY_RESULT(source_lookup, create_source_lookup(std::move(main_fif), true, true, false, false, false, false, false, fift_dir));
TRY_RESULT(res, run_fift(std::move(source_lookup), &fift_output_stream));
TRY_RESULT(boc, res.read_file("boc"));
TRY_RESULT(hex, res.read_file("hex"));
return CompiledProgramOutput{
std::move(program_code),
std::move(boc.data),
std::move(hex.data),
};
}
} // namespace fift

View file

@ -26,11 +26,21 @@ struct FiftOutput {
SourceLookup source_lookup;
std::string output;
};
// given a valid Fift code PROGRAM{ ... }END>c, compile_asm_program() returns this output
// now it's used primarily for wasm output (see tolk-js, for example)
struct CompiledProgramOutput {
std::string fiftCode;
std::string codeBoc64;
std::string codeHashHex;
};
td::Result<fift::SourceLookup> create_mem_source_lookup(std::string main, std::string fift_dir = "",
bool need_preamble = true, bool need_asm = true,
bool need_ton_util = true, bool need_lisp = true,
bool need_w3_code = true);
td::Result<FiftOutput> mem_run_fift(std::string source, std::vector<std::string> args = {}, std::string fift_dir = "");
td::Result<FiftOutput> mem_run_fift(SourceLookup source_lookup, std::vector<std::string> args);
td::Result<td::Ref<vm::Cell>> compile_asm(td::Slice asm_code, std::string fift_dir = "", bool is_raw = true);
td::Result<td::Ref<vm::Cell>> compile_asm(td::Slice asm_code);
td::Result<CompiledProgramOutput> compile_asm_program(std::string&& program_code, const std::string& fift_dir);
} // namespace fift

View file

@ -1,4 +1,4 @@
int foo(int x) method_id(11) {
int foo(int x) {
try {
if (x == 7) {
throw(44);
@ -9,7 +9,7 @@ int foo(int x) method_id(11) {
}
}
int foo_inline(int x) inline method_id(12) {
int foo_inline(int x) inline {
try {
if (x == 7) {
throw(44);
@ -20,7 +20,7 @@ int foo_inline(int x) inline method_id(12) {
}
}
int foo_inlineref(int x) inline_ref method_id(13) {
int foo_inlineref(int x) inline_ref {
try {
if (x == 7) {
throw(44);
@ -31,17 +31,17 @@ int foo_inlineref(int x) inline_ref method_id(13) {
}
}
int test(int x, int y, int z) method_id(1) {
int test(int x, int y, int z) method_id(101) {
y = foo(y);
return x * 100 + y * 10 + z;
}
int test_inline(int x, int y, int z) method_id(2) {
int test_inline(int x, int y, int z) method_id(102) {
y = foo_inline(y);
return x * 100 + y * 10 + z;
}
int test_inlineref(int x, int y, int z) method_id(3) {
int test_inlineref(int x, int y, int z) method_id(103) {
y = foo_inlineref(y);
return x * 100 + y * 10 + z;
}
@ -49,7 +49,7 @@ int test_inlineref(int x, int y, int z) method_id(3) {
int foo_inline_big(
int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8, int x9, int x10,
int x11, int x12, int x13, int x14, int x15, int x16, int x17, int x18, int x19, int x20
) inline method_id(14) {
) inline {
try {
if (x1 == 7) {
throw(44);
@ -60,7 +60,7 @@ int foo_inline_big(
}
}
int test_inline_big(int x, int y, int z) method_id(4) {
int test_inline_big(int x, int y, int z) method_id(104) {
y = foo_inline_big(
y, y + 1, y + 2, y + 3, y + 4, y + 5, y + 6, y + 7, y + 8, y + 9,
y + 10, y + 11, y + 12, y + 13, y + 14, y + 15, y + 16, y + 17, y + 18, y + 19);
@ -70,7 +70,7 @@ int test_inline_big(int x, int y, int z) method_id(4) {
int foo_big(
int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8, int x9, int x10,
int x11, int x12, int x13, int x14, int x15, int x16, int x17, int x18, int x19, int x20
) method_id(15) {
) {
try {
if (x1 == 7) {
throw(44);
@ -81,29 +81,69 @@ int foo_big(
}
}
int test_big(int x, int y, int z) method_id(5) {
int test_big(int x, int y, int z) method_id(105) {
y = foo_big(
y, y + 1, y + 2, y + 3, y + 4, y + 5, y + 6, y + 7, y + 8, y + 9,
y + 10, y + 11, y + 12, y + 13, y + 14, y + 15, y + 16, y + 17, y + 18, y + 19);
return x * 1000000 + y * 1000 + z;
}
() some_throwing(int op) impure {
if (op == 1) {
return ();
} elseif (op == 2) {
return ();
} else {
throw(1);
}
}
int test106() method_id(106) {
try {
some_throwing(1337);
return 1337;
} catch(_, code) {
return code;
}
return -1;
}
global int g_reg;
(int, int) test107() method_id(107) {
int l_reg = 10;
g_reg = 10;
try {
;; note, that regardless of assignment, an exception RESTORES them to previous (to 10)
;; it's very unexpected, but is considered to be a TVM feature, not a bug
g_reg = 999;
l_reg = 999;
some_throwing(999);
} catch(_, _) {
}
;; returns (10,10) because of an exception, see a comment above
return (g_reg, l_reg);
}
() main() {
}
{-
method_id | in | out
TESTCASE | 1 | 1 2 3 | 123
TESTCASE | 1 | 3 8 9 | 389
TESTCASE | 1 | 3 7 9 | 329
TESTCASE | 2 | 1 2 3 | 123
TESTCASE | 2 | 3 8 9 | 389
TESTCASE | 2 | 3 7 9 | 329
TESTCASE | 3 | 1 2 3 | 123
TESTCASE | 3 | 3 8 9 | 389
TESTCASE | 3 | 3 7 9 | 329
TESTCASE | 4 | 4 8 9 | 4350009
TESTCASE | 4 | 4 7 9 | 4001009
TESTCASE | 5 | 4 8 9 | 4350009
TESTCASE | 5 | 4 7 9 | 4001009
method_id | in | out
TESTCASE | 101 | 1 2 3 | 123
TESTCASE | 101 | 3 8 9 | 389
TESTCASE | 101 | 3 7 9 | 329
TESTCASE | 102 | 1 2 3 | 123
TESTCASE | 102 | 3 8 9 | 389
TESTCASE | 102 | 3 7 9 | 329
TESTCASE | 103 | 1 2 3 | 123
TESTCASE | 103 | 3 8 9 | 389
TESTCASE | 103 | 3 7 9 | 329
TESTCASE | 104 | 4 8 9 | 4350009
TESTCASE | 104 | 4 7 9 | 4001009
TESTCASE | 105 | 4 8 9 | 4350009
TESTCASE | 105 | 4 7 9 | 4001009
TESTCASE | 106 | | 1
TESTCASE | 107 | | 10 10
-}

View file

@ -0,0 +1,132 @@
tuple empty_tuple() asm "NIL";
forall X -> (tuple, ()) ~tpush(tuple t, X value) asm "TPUSH";
builder begin_cell() asm "NEWC";
cell end_cell(builder b) asm "ENDC";
slice begin_parse(cell c) asm "CTOS";
_ getBeginCell() {
return begin_cell;
}
_ getBeginParse() {
return begin_parse;
}
(int, int) test101() method_id(101) {
var (_, f_end_cell) = (0, end_cell);
builder b = (getBeginCell())().store_int(1, 32);
b~store_int(2, 32);
var s = (getBeginParse())(f_end_cell(b));
return (s~load_int(32), s~load_int(32));
}
() my_throw_always() inline {
throw(1000);
}
_ get_raiser() inline {
return my_throw_always;
}
int test102() method_id(102) {
try {
var raiser = get_raiser();
raiser(); ;; `some_var()` is always impure, the compiler has no considerations about its runtime value
return 0;
} catch (_, code) {
return code;
}
}
int sum(int a, int b) impure inline {
throw_unless(1000, a + b < 24);
return a + b;
}
int mul(int a, int b) impure inline {
throw_unless(1001, a * b < 24);
return a * b;
}
int sum_pure(int a, int b) inline {
throw_unless(1000, a + b < 24);
return a + b;
}
int mul_pure(int a, int b) inline {
throw_unless(1001, a * b < 24);
return a * b;
}
int demo_handler(int op, int query_id, int a, int b) {
if (op == 0xF2) {
var func = query_id % 2 == 0 ? sum : mul;
int result = func(a, b);
return 0; ;; result not used, we test that func is nevertheless called
}
if (op == 0xF3) {
var func = query_id % 2 == 0 ? sum_pure : mul_pure;
int result = func(a, b);
return 0; ;; the same for sum_pure, since `some_var()` is always impure
}
if (op == 0xF4) {
var func = query_id % 2 == 0 ? sum : mul;
int result = func(a, b);
return result;
}
return -1;
}
tuple test103() method_id(103) {
tuple t = empty_tuple();
try {
t~tpush(demo_handler(0xF2, 122, 100, 200));
} catch(_, code) {
t~tpush(code);
}
try {
t~tpush(demo_handler(0xF4, 122, 100, 200));
} catch(_, code) {
t~tpush(code);
}
try {
t~tpush(demo_handler(0xF3, 122, 10, 10));
} catch(_, code) {
t~tpush(code);
}
try {
t~tpush(demo_handler(0xF3, 123, 10, 10));
} catch(_, code) {
t~tpush(code);
}
return t;
}
() always_throw2(int x) impure {
throw (239 + x);
}
global int -> () global_f;
int test104() method_id(104) {
try {
global_f = always_throw2;
global_f(1);
return 0;
} catch (_, code) {
return code;
}
}
() main() {
}
{-
method_id | in | out
TESTCASE | 101 | | 1 2
TESTCASE | 102 | | 1000
TESTCASE | 103 | | [ 1000 1000 0 1001 ]
TESTCASE | 104 | | 240
-}

View file

@ -830,6 +830,8 @@ bool Op::generate_code_step(Stack& stack) {
catch_stack.push_new_var(left[1]);
stack.rearrange_top(catch_vars, catch_last);
stack.opt_show();
stack.o << "c1 PUSH";
stack.o << "c3 PUSH";
stack.o << "c4 PUSH";
stack.o << "c5 PUSH";
stack.o << "c7 PUSH";
@ -846,6 +848,8 @@ bool Op::generate_code_step(Stack& stack) {
stack.o << "c7 SETCONT";
stack.o << "c5 SETCONT";
stack.o << "c4 SETCONT";
stack.o << "c3 SETCONT";
stack.o << "c1 SETCONT";
for (size_t begin = catch_vars.size(), end = begin; end > 0; end = begin) {
begin = end >= block_size ? end - block_size : 0;
stack.o << std::to_string(end - begin) + " PUSHINT";

View file

@ -45,7 +45,7 @@ extern std::string generated_from;
constexpr int optimize_depth = 20;
const std::string func_version{"0.4.5"};
const std::string func_version{"0.4.6"};
enum Keyword {
_Eof = -1,

View file

@ -380,7 +380,10 @@ std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code, std::vector<std::pair<S
}
res.push_back(tfunc[0]);
auto rvect = new_tmp_vect(code);
code.emplace_back(here, Op::_CallInd, rvect, std::move(res));
auto& op = code.emplace_back(here, Op::_CallInd, rvect, std::move(res));
if (flags & _IsImpure) {
op.flags |= Op::_Impure;
}
return rvect;
}
case _Const: {

View file

@ -427,7 +427,7 @@ Expr* make_func_apply(Expr* fun, Expr* x) {
res->flags = Expr::_IsRvalue | (fun->flags & Expr::_IsImpure);
} else {
res = new Expr{Expr::_VarApply, {fun, x}};
res->flags = Expr::_IsRvalue;
res->flags = Expr::_IsRvalue | Expr::_IsImpure; // for `some_var()`, don't make any considerations about runtime value, it's impure
}
return res;
}

View file

@ -37,10 +37,10 @@
td::Result<std::string> compile_internal(char *config_json) {
TRY_RESULT(input_json, td::json_decode(td::MutableSlice(config_json)))
auto &obj = input_json.get_object();
td::JsonObject& config = input_json.get_object();
TRY_RESULT(opt_level, td::get_json_object_int_field(obj, "optLevel", false));
TRY_RESULT(sources_obj, td::get_json_object_field(obj, "sources", td::JsonValue::Type::Array, false));
TRY_RESULT(opt_level, td::get_json_object_int_field(config, "optLevel", false));
TRY_RESULT(sources_obj, td::get_json_object_field(config, "sources", td::JsonValue::Type::Array, false));
auto &sources_arr = sources_obj.get_array();
@ -52,29 +52,25 @@ td::Result<std::string> compile_internal(char *config_json) {
funC::opt_level = std::max(0, opt_level);
funC::program_envelope = true;
funC::asm_preamble = true;
funC::verbosity = 0;
funC::indent = 1;
std::ostringstream outs, errs;
auto compile_res = funC::func_proceed(sources, outs, errs);
if (compile_res != 0) {
return td::Status::Error(std::string("Func compilation error: ") + errs.str());
int funC_res = funC::func_proceed(sources, outs, errs);
if (funC_res != 0) {
return td::Status::Error("FunC compilation error: " + errs.str());
}
TRY_RESULT(code_cell, fift::compile_asm(outs.str(), "/fiftlib/", false));
TRY_RESULT(boc, vm::std_boc_serialize(code_cell));
TRY_RESULT(fift_res, fift::compile_asm_program(outs.str(), "/fiftlib/"));
td::JsonBuilder result_json;
auto result_obj = result_json.enter_object();
result_obj("status", "ok");
result_obj("codeBoc", td::base64_encode(boc));
result_obj("fiftCode", outs.str());
result_obj("codeHashHex", code_cell->get_hash().to_hex());
result_obj.leave();
outs.clear();
errs.clear();
auto obj = result_json.enter_object();
obj("status", "ok");
obj("fiftCode", std::move(fift_res.fiftCode));
obj("codeBoc", std::move(fift_res.codeBoc64));
obj("codeHashHex", std::move(fift_res.codeHashHex));
obj.leave();
return result_json.string_builder().as_cslice().str();
}

View file

@ -0,0 +1,747 @@
// Standard library for Tolk (LGPL licence).
// It contains common functions that are available out of the box, the user doesn't have to import anything.
// More specific functions are required to be imported explicitly, like "@stdlib/tvm-dicts".
tolk 0.6
/**
Tuple manipulation primitives.
Elements of a tuple can be of arbitrary type.
Note that atomic type `tuple` can't be cast to composite tuple type (e.g. `[int, cell]`) and vise versa.
*/
/// Creates a tuple with zero elements.
@pure
fun createEmptyTuple(): tuple
asm "NIL";
/// Appends a value to tuple, resulting in `Tuple t' = (x1, ..., xn, value)`.
/// If its size exceeds 255, throws a type check exception.
@pure
fun tuplePush<X>(mutate self: tuple, value: X): void
asm "TPUSH";
/// Returns the first element of a non-empty tuple.
@pure
fun tupleFirst<X>(t: tuple): X
asm "FIRST";
/// Returns the [`index`]-th element of a tuple.
@pure
fun tupleAt<X>(t: tuple, index: int): X
builtin;
/// Returns the size of a tuple (elements count in it).
@pure
fun tupleSize(t: tuple): int
asm "TLEN";
/// Returns the last element of a non-empty tuple.
@pure
fun tupleLast(t: tuple): int
asm "LAST";
/**
Mathematical primitives.
*/
/// Computes the minimum of two integers.
@pure
fun min(x: int, y: int): int
asm "MIN";
/// Computes the maximum of two integers.
@pure
fun max(x: int, y: int): int
asm "MAX";
/// Sorts two integers.
@pure
fun minMax(x: int, y: int): (int, int)
asm "MINMAX";
/// Computes the absolute value of an integer.
@pure
fun abs(x: int): int
asm "ABS";
/// Returns the sign of an integer: `-1` if x < 0, `0` if x == 0, `1` if x > 0.
@pure
fun sign(x: int): int
asm "SGN";
/// Computes the quotient and remainder of [x] / [y]. Example: divMod(112,3) = (37,1)
@pure
fun divMod(x: int, y: int): (int, int)
asm "DIVMOD";
/// Computes the remainder and quotient of [x] / [y]. Example: modDiv(112,3) = (1,37)
@pure
fun modDiv(x: int, y: int): (int, int)
asm(-> 1 0) "DIVMOD";
/// Computes multiple-then-divide: floor([x] * [y] / [z]).
/// The intermediate result is stored in a 513-bit integer to prevent precision loss.
@pure
fun mulDivFloor(x: int, y: int, z: int): int
builtin;
/// Similar to `mulDivFloor`, but rounds the result: round([x] * [y] / [z]).
@pure
fun mulDivRound(x: int, y: int, z: int): int
builtin;
/// Similar to `mulDivFloor`, but ceils the result: ceil([x] * [y] / [z]).
@pure
fun mulDivCeil(x: int, y: int, z: int): int
builtin;
/// Computes the quotient and remainder of ([x] * [y] / [z]). Example: mulDivMod(112,3,10) = (33,6)
@pure
fun mulDivMod(x: int, y: int, z: int): (int, int)
builtin;
/**
Global getters of environment and contract state.
*/
const MASTERCHAIN = -1;
const BASECHAIN = 0;
/// Returns current Unix timestamp (in seconds).
@pure
fun now(): int
asm "NOW";
/// Returns the internal address of the current smart contract as a Slice with a `MsgAddressInt`.
/// If necessary, it can be parsed further using primitives such as [parseStandardAddress].
@pure
fun getMyAddress(): slice
asm "MYADDR";
/// Returns the balance (in nanotoncoins) of the smart contract at the start of Computation Phase.
/// Note that RAW primitives such as [sendMessage] do not update this field.
@pure
fun getMyOriginalBalance(): int
asm "BALANCE" "FIRST";
/// Same as [getMyOriginalBalance], but returns a tuple:
/// `int` — balance in nanotoncoins;
/// `cell` — a dictionary with 32-bit keys representing the balance of "extra currencies".
@pure
fun getMyOriginalBalanceWithExtraCurrencies(): [int, cell]
asm "BALANCE";
/// Returns the logical time of the current transaction.
@pure
fun getLogicalTime(): int
asm "LTIME";
/// Returns the starting logical time of the current block.
@pure
fun getCurrentBlockLogicalTime(): int
asm "BLOCKLT";
/// Returns the value of the global configuration parameter with integer index `i` as a `cell` or `null` value.
@pure
fun getBlockchainConfigParam(x: int): cell
asm "CONFIGOPTPARAM";
/// Returns the persistent contract storage cell. It can be parsed or modified with slice and builder primitives later.
@pure
fun getContractData(): cell
asm "c4 PUSH";
/// Sets `cell` [c] as persistent contract data. You can update persistent contract storage with this primitive.
fun setContractData(c: cell): void
asm "c4 POP";
/// Retrieves code of smart-contract from c7
@pure
fun getContractCode(): cell
asm "MYCODE";
/// Creates an output action that would change this smart contract code to that given by cell [newCode].
/// Notice that this change will take effect only after the successful termination of the current run of the smart contract.
fun setContractCodePostponed(newCode: cell): void
asm "SETCODE";
/// Commits the current state of registers `c4` (“persistent data”) and `c5` (“actions”)
/// so that the current execution is considered “successful” with the saved values even if an exception
/// in Computation Phase is thrown later.
fun commitContractDataAndActions(): void
asm "COMMIT";
/**
Signature checks, hashing, cryptography.
*/
/// Computes the representation hash of a `cell` [c] and returns it as a 256-bit unsigned integer `x`.
/// Useful for signing and checking signatures of arbitrary entities represented by a tree of cells.
@pure
fun cellHash(c: cell): int
asm "HASHCU";
/// Computes the hash of a `slice s` and returns it as a 256-bit unsigned integer `x`.
/// The result is the same as if an ordinary cell containing only data and references from `s` had been created
/// and its hash computed by [cellHash].
@pure
fun sliceHash(s: slice): int
asm "HASHSU";
/// Computes sha256 of the data bits of `slice` [s]. If the bit length of `s` is not divisible by eight,
/// throws a cell underflow exception. The hash value is returned as a 256-bit unsigned integer `x`.
@pure
fun stringHash(s: slice): int
asm "SHA256U";
/// Checks the Ed25519-`signature` of a `hash` (a 256-bit unsigned integer, usually computed as the hash of some data)
/// using [publicKey] (also represented by a 256-bit unsigned integer).
/// The signature must contain at least 512 data bits; only the first 512 bits are used.
/// The result is `1` if the signature is valid, `0` otherwise.
/// Note that `CHKSIGNU` creates a 256-bit slice with the hash and calls `CHKSIGNS`.
/// That is, if [hash] is computed as the hash of some data, these data are hashed twice,
/// the second hashing occurring inside `CHKSIGNS`.
@pure
fun isSignatureValid(hash: int, signature: slice, publicKey: int): int
asm "CHKSIGNU";
/// Checks whether [signature] is a valid Ed25519-signature of the data portion of `slice data` using `publicKey`,
/// similarly to [isSignatureValid].
/// If the bit length of [data] is not divisible by eight, throws a cell underflow exception.
/// The verification of Ed25519 signatures is the standard one,
/// with sha256 used to reduce [data] to the 256-bit number that is actually signed.
@pure
fun isSliceSignatureValid(data: slice, signature: slice, publicKey: int): int
asm "CHKSIGNS";
/// Generates a new pseudo-random unsigned 256-bit integer x.
fun random(): int
asm "RANDU256";
/// Generates a new pseudo-random integer z in the range 0..range1 (or range..1, if range < 0).
/// More precisely, an unsigned random value x is generated as in random; then z := x * range / 2^256 is computed.
fun randomRange(range: int): int
asm "RAND";
/// Returns the current random seed as an unsigned 256-bit integer.
@pure
fun randomGetSeed(): int
asm "RANDSEED";
/// Sets the random seed to unsigned 256-bit seed.
fun randomSetSeed(seed: int): void
asm "SETRAND";
/// Initializes (mixes) random seed with unsigned 256-bit integer x.
fun randomizeBy(x: int): void
asm "ADDRAND";
/// Initializes random seed using current time. Don't forget to call this before calling `random`!
fun randomizeByLogicalTime(): void
asm "LTIME" "ADDRAND";
/**
Size computation primitives.
They may be useful for computing storage fees of user-provided data.
*/
/// Returns `(x, y, z, -1)` or `(null, null, null, 0)`.
/// Recursively computes the count of distinct cells `x`, data bits `y`, and cell references `z`
/// in the DAG rooted at `cell` [c], effectively returning the total storage used by this DAG taking into account
/// the identification of equal cells.
/// The values of `x`, `y`, and `z` are computed by a depth-first traversal of this DAG,
/// with a hash table of visited cell hashes used to prevent visits of already-visited cells.
/// The total count of visited cells `x` cannot exceed non-negative [maxCells];
/// otherwise the computation is aborted before visiting the `(maxCells + 1)`-st cell and
/// a zero flag is returned to indicate failure. If [c] is `null`, returns `x = y = z = 0`.
@pure
fun calculateCellSize(c: cell, maxCells: int): (int, int, int, int)
asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
/// Similar to [calculateCellSize], but accepting a `slice` [s] instead of a `cell`.
/// The returned value of `x` does not take into account the cell that contains the `slice` [s] itself;
/// however, the data bits and the cell references of [s] are accounted for in `y` and `z`.
@pure
fun calculateSliceSize(s: slice, maxCells: int): (int, int, int, int)
asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
/// A non-quiet version of [calculateCellSize] that throws a cell overflow exception (`8`) on failure.
fun calculateCellSizeStrict(c: cell, maxCells: int): (int, int, int)
asm "CDATASIZE";
/// A non-quiet version of [calculateSliceSize] that throws a cell overflow exception (`8`) on failure.
fun calculateSliceSizeStrict(s: slice, maxCells: int): (int, int, int)
asm "SDATASIZE";
/// Returns the depth of `cell` [c].
/// If [c] has no references, then return `0`;
/// otherwise the returned value is one plus the maximum of depths of cells referred to from [c].
/// If [c] is a `null` instead of a cell, returns zero.
@pure
fun getCellDepth(c: cell): int
asm "CDEPTH";
/// Returns the depth of `slice` [s].
/// If [s] has no references, then returns `0`;
/// otherwise the returned value is one plus the maximum of depths of cells referred to from [s].
@pure
fun getSliceDepth(s: slice): int
asm "SDEPTH";
/// Returns the depth of `builder` [b].
/// If no cell references are stored in [b], then returns 0;
/// otherwise the returned value is one plus the maximum of depths of cells referred to from [b].
@pure
fun getBuilderDepth(b: builder): int
asm "BDEPTH";
/**
Debug primitives.
Only works for local TVM execution with debug level verbosity.
*/
/// Dump a variable [x] to the debug log.
fun debugPrint<X>(x: X): void
builtin;
/// Dump a string [x] to the debug log.
fun debugPrintString<X>(x: X): void
builtin;
/// Dumps the stack (at most the top 255 values) and shows the total stack depth.
fun debugDumpStack(): void
builtin;
/**
Slice primitives: parsing cells.
When you _load_ some data, you mutate the slice (shifting an internal pointer on the stack).
When you _preload_ some data, you just get the result without mutating the slice.
*/
/// Converts a `cell` [c] into a `slice`. Notice that [c] must be either an ordinary cell,
/// or an exotic cell (see [TVM.pdf](https://ton-blockchain.github.io/docs/tvm.pdf), 3.1.2)
/// which is automatically loaded to yield an ordinary cell `c'`, converted into a `slice` afterwards.
@pure
fun beginParse(c: cell): slice
asm "CTOS";
/// Checks if slice is empty. If not, throws an exception.
fun assertEndOfSlice(self: slice): void
asm "ENDS";
/// Loads the next reference from the slice.
@pure
fun loadRef(mutate self: slice): cell
asm( -> 1 0) "LDREF";
/// Preloads the next reference from the slice.
@pure
fun preloadRef(self: slice): cell
asm "PLDREF";
/// Loads a signed [len]-bit integer from a slice.
@pure
fun loadInt(mutate self: slice, len: int): int
builtin;
/// Loads an unsigned [len]-bit integer from a slice.
@pure
fun loadUint(mutate self: slice, len: int): int
builtin;
/// Loads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate slice `s''`.
@pure
fun loadBits(mutate self: slice, len: int): slice
builtin;
/// Preloads a signed [len]-bit integer from a slice.
@pure
fun preloadInt(self: slice, len: int): int
builtin;
/// Preloads an unsigned [len]-bit integer from a slice.
@pure
fun preloadUint(self: slice, len: int): int
builtin;
/// Preloads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate slice.
@pure
fun preloadBits(self: slice, len: int): slice
builtin;
/// Loads serialized amount of Toncoins (any unsigned integer up to `2^120 - 1`).
@pure
fun loadCoins(mutate self: slice): int
asm( -> 1 0) "LDGRAMS";
/// Loads bool (-1 or 0) from a slice
@pure
fun loadBool(mutate self: slice): int
asm( -> 1 0) "1 LDI";
/// Shifts a slice pointer to [len] bits forward, mutating the slice.
@pure
fun skipBits(mutate self: slice, len: int): self
asm "SDSKIPFIRST";
/// Returns the first `0 ≤ len ≤ 1023` bits of a slice.
@pure
fun getFirstBits(self: slice, len: int): slice
asm "SDCUTFIRST";
/// Returns all but the last `0 ≤ len ≤ 1023` bits of a slice.
@pure
fun removeLastBits(mutate self: slice, len: int): self
asm "SDSKIPLAST";
/// Returns the last `0 ≤ len ≤ 1023` bits of a slice.
@pure
fun getLastBits(self: slice, len: int): slice
asm "SDCUTLAST";
/// Loads a dictionary (TL HashMapE structure, represented as TVM cell) from a slice.
/// Returns `null` if `nothing` constructor is used.
@pure
fun loadDict(mutate self: slice): cell
asm( -> 1 0) "LDDICT";
/// Preloads a dictionary (cell) from a slice.
@pure
fun preloadDict(self: slice): cell
asm "PLDDICT";
/// Loads a dictionary as [loadDict], but returns only the remainder of the slice.
@pure
fun skipDict(mutate self: slice): self
asm "SKIPDICT";
/// Loads (Maybe ^Cell) from a slice.
/// In other words, loads 1 bit: if it's true, loads the first ref, otherwise returns `null`.
@pure
fun loadMaybeRef(mutate self: slice): cell
asm( -> 1 0) "LDOPTREF";
/// Preloads (Maybe ^Cell) from a slice.
@pure
fun preloadMaybeRef(self: slice): cell
asm "PLDOPTREF";
/// Loads (Maybe ^Cell), but returns only the remainder of the slice.
@pure
fun skipMaybeRef(mutate self: slice): self
asm "SKIPOPTREF";
/**
Builder primitives: constructing cells.
When you _store_ some data, you mutate the builder (shifting an internal pointer on the stack).
All the primitives below first check whether there is enough space in the `builder`,
and only then check the range of the value being serialized.
*/
/// Creates a new empty builder.
@pure
fun beginCell(): builder
asm "NEWC";
/// Converts a builder into an ordinary `cell`.
@pure
fun endCell(self: builder): cell
asm "ENDC";
/// Stores a reference to a cell into a builder.
@pure
fun storeRef(mutate self: builder, c: cell): self
asm(c self) "STREF";
/// Stores a signed [len]-bit integer into a builder (`0 ≤ len ≤ 257`).
@pure
fun storeInt(mutate self: builder, x: int, len: int): self
builtin;
/// Stores an unsigned [len]-bit integer into a builder (`0 ≤ len ≤ 256`).
@pure
fun storeUint(mutate self: builder, x: int, len: int): self
builtin;
/// Stores a slice into a builder.
@pure
fun storeSlice(mutate self: builder, s: slice): self
asm "STSLICER";
/// Stores amount of Toncoins into a builder.
@pure
fun storeCoins(mutate self: builder, x: int): self
asm "STGRAMS";
/// Stores bool (-1 or 0) into a builder.
/// Attention: true value is `-1`, not 1! If you pass `1` here, TVM will throw an exception.
@pure
fun storeBool(mutate self: builder, x: int): self
asm(x self) "1 STI";
/// Stores dictionary (represented by TVM `cell` or `null`) into a builder.
/// In other words, stores a `1`-bit and a reference to [c] if [c] is not `null` and `0`-bit otherwise.
@pure
fun storeDict(mutate self: builder, c: cell): self
asm(c self) "STDICT";
/// Stores (Maybe ^Cell) into a builder.
/// In other words, if cell is `null`, store '0' bit; otherwise, store '1' and a ref to [c].
@pure
fun storeMaybeRef(mutate self: builder, c: cell): self
asm(c self) "STOPTREF";
/// Concatenates two builders.
@pure
fun storeBuilder(mutate self: builder, from: builder): self
asm "STBR";
/// Stores a slice representing TL addr_none$00 (two `0` bits).
@pure
fun storeAddressNone(mutate self: builder): self
asm "b{00} STSLICECONST";
/**
Slice size primitives.
*/
/// Returns the number of references in a slice.
@pure
fun getRemainingRefsCount(self: slice): int
asm "SREFS";
/// Returns the number of data bits in a slice.
@pure
fun getRemainingBitsCount(self: slice): int
asm "SBITS";
/// Returns both the number of data bits and the number of references in a slice.
@pure
fun getRemainingBitsAndRefsCount(self: slice): (int, int)
asm "SBITREFS";
/// Checks whether a slice is empty (i.e., contains no bits of data and no cell references).
@pure
fun isEndOfSlice(self: slice): int
asm "SEMPTY";
/// Checks whether a slice has no bits of data.
@pure
fun isEndOfSliceBits(self: slice): int
asm "SDEMPTY";
/// Checks whether a slice has no references.
@pure
fun isEndOfSliceRefs(self: slice): int
asm "SREMPTY";
/// Checks whether data parts of two slices coinside.
@pure
fun isSliceBitsEqual(self: slice, b: slice): int
asm "SDEQ";
/// Returns the number of cell references already stored in a builder.
@pure
fun getBuilderRefsCount(self: builder): int
asm "BREFS";
/// Returns the number of data bits already stored in a builder.
@pure
fun getBuilderBitsCount(self: builder): int
asm "BBITS";
/**
Address manipulation primitives.
The address manipulation primitives listed below serialize and deserialize values according to the following TL-B scheme:
```TL-B
addr_none$00 = MsgAddressExt;
addr_extern$01 len:(## 8) external_address:(bits len)
= MsgAddressExt;
anycast_info$_ depth:(#<= 30) { depth >= 1 }
rewrite_pfx:(bits depth) = Anycast;
addr_std$10 anycast:(Maybe Anycast)
workchain_id:int8 address:bits256 = MsgAddressInt;
addr_var$11 anycast:(Maybe Anycast) addr_len:(## 9)
workchain_id:int32 address:(bits addr_len) = MsgAddressInt;
_ _:MsgAddressInt = MsgAddress;
_ _:MsgAddressExt = MsgAddress;
int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
src:MsgAddress dest:MsgAddressInt
value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams
created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed;
ext_out_msg_info$11 src:MsgAddress dest:MsgAddressExt
created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed;
```
A deserialized `MsgAddress` is represented by a tuple `t` as follows:
- `addr_none` is represented by `t = (0)`,
i.e., a tuple containing exactly one integer equal to zero.
- `addr_extern` is represented by `t = (1, s)`,
where slice `s` contains the field `external_address`. In other words, `
t` is a pair (a tuple consisting of two entries), containing an integer equal to one and slice `s`.
- `addr_std` is represented by `t = (2, u, x, s)`,
where `u` is either a `null` (if `anycast` is absent) or a slice `s'` containing `rewrite_pfx` (if anycast is present).
Next, integer `x` is the `workchain_id`, and slice `s` contains the address.
- `addr_var` is represented by `t = (3, u, x, s)`,
where `u`, `x`, and `s` have the same meaning as for `addr_std`.
*/
/// Loads from slice [s] the only prefix that is a valid `MsgAddress`,
/// and returns both this prefix `s'` and the remainder `s''` of [s] as slices.
@pure
fun loadAddress(mutate self: slice): slice
asm( -> 1 0) "LDMSGADDR";
/// Decomposes slice [s] containing a valid `MsgAddress` into a `tuple t` with separate fields of this `MsgAddress`.
/// If [s] is not a valid `MsgAddress`, a cell deserialization exception is thrown.
@pure
fun parseAddress(s: slice): tuple
asm "PARSEMSGADDR";
/// Parses slice [s] containing a valid `MsgAddressInt` (usually a `msg_addr_std`),
/// applies rewriting from the anycast (if present) to the same-length prefix of the address,
/// and returns both the workchain and the 256-bit address as integers.
/// If the address is not 256-bit, or if [s] is not a valid serialization of `MsgAddressInt`,
/// throws a cell deserialization exception.
@pure
fun parseStandardAddress(s: slice): (int, int)
asm "REWRITESTDADDR";
/// Creates a slice representing TL addr_none$00 (two `0` bits).
@pure
fun createAddressNone(): slice
asm "b{00} PUSHSLICE";
/// Returns if a slice pointer contains an empty address (`-1` for true, `0` for false, as always).
/// In other words, a slice starts with two `0` bits (TL addr_none$00).
@pure
fun addressIsNone(s: slice): int
asm "2 PLDU" "0 EQINT";
/**
Reserving Toncoins on balance and its flags.
*/
/// mode = 0: Reserve exact amount of nanotoncoins
const RESERVE_MODE_EXACT_AMOUNT = 0;
/// +1: Actually reserves all but amount, meaning `currentContractBalance - amount`
const RESERVE_MODE_ALL_BUT_AMOUNT = 1;
/// +2: Actually set `min(amount, currentContractBalance)` (without this mode, if amount is greater, the action will fail)
const RESERVE_MODE_AT_MOST = 2;
/// +4: [amount] is increased by the _original_ balance of the current account (before the compute phase).
const RESERVE_MODE_INCREASE_BY_ORIGINAL_BALANCE = 4;
/// +8: Actually sets `amount = -amount` before performing any further actions.
const RESERVE_MODE_NEGATE_AMOUNT = 8;
/// +16: If this action fails, the transaction will be bounced.
const RESERVE_MODE_BOUNCE_ON_ACTION_FAIL = 16;
/// Creates an output action which would reserve Toncoins on balance.
/// For [reserveMode] consider constants above.
fun reserveToncoinsOnBalance(nanoTonCoins: int, reserveMode: int): void
asm "RAWRESERVE";
/// Similar to [reserveToncoinsOnBalance], but also accepts a dictionary extraAmount (represented by a cell or null)
/// with extra currencies. In this way currencies other than Toncoin can be reserved.
fun reserveExtraCurrenciesOnBalance(nanoTonCoins: int, extraAmount: cell, reserveMode: int): void
asm "RAWRESERVEX";
/**
Messages sending and parsing primitives.
Working with messages is low-level right now, but still, every contract should do that.
`Message` structure, its header and so on are specified in TL-B scheme, particularly:
int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool ... = CommonMsgInfo;
*/
/// 0b011000 tag - 0, ihr_disabled - 1, bounce - 1, bounced - 0, src = adr_none$00
const BOUNCEABLE = 0x18;
/// 0b010000 tag - 0, ihr_disabled - 1, bounce - 0, bounced - 0, src = adr_none$00
const NON_BOUNCEABLE = 0x10;
/// Load msgFlags from incoming message body (4 bits).
@pure
fun loadMessageFlags(mutate self: slice): int
asm( -> 1 0) "4 LDU";
/// Having msgFlags (4 bits), check that a message is bounced.
/// Effectively, it's `msgFlags & 1` (the lowest bit present).
@pure
fun isMessageBounced(msgFlags: int): int
asm "1 PUSHINT" "AND";
/// Skip 0xFFFFFFFF prefix (when a message is bounced).
@pure
fun skipBouncedPrefix(mutate self: slice): self
asm "32 PUSHINT" "SDSKIPFIRST";
/// The guideline recommends to start the body of an internal message with uint32 `op` and uint64 `queryId`.
@pure
fun loadMessageOp(mutate self: slice): int
asm( -> 1 0) "32 LDU";
@pure
fun skipMessageOp(mutate self: slice): self
asm "32 PUSHINT" "SDSKIPFIRST";
@pure
fun storeMessageOp(mutate self: builder, op: int): self
asm(op self) "32 STU";
/// The guideline recommends that uint64 `queryId` should follow uint32 `op`.
@pure
fun loadMessageQueryId(mutate self: slice): int
asm( -> 1 0) "64 LDU";
@pure
fun skipMessageQueryId(mutate self: slice): self
asm "64 PUSHINT" "SDSKIPFIRST";
@pure
fun storeMessageQueryId(mutate self: builder, queryId: int): self
asm(queryId self) "64 STU";
/// SEND MODES - https://docs.ton.org/tvm.pdf page 137, SENDRAWMSG
/// mode = 0 is used for ordinary messages; the gas fees are deducted from the senging amount; action phaes should NOT be ignored.
const SEND_MODE_REGULAR = 0;
/// +1 means that the sender wants to pay transfer fees separately.
const SEND_MODE_PAY_FEES_SEPARATELY = 1;
/// +2 means that any errors arising while processing this message during the action phase should be ignored.
const SEND_MODE_IGNORE_ERRORS = 2;
/// in the case of action fail - bounce transaction. No effect if SEND_MODE_IGNORE_ERRORS (+2) is used. TVM UPGRADE 2023-07. https://docs.ton.org/learn/tvm-instructions/tvm-upgrade-2023-07#sending-messages
const SEND_MODE_BOUNCE_ON_ACTION_FAIL = 16;
/// mode = 32 means that the current account must be destroyed if its resulting balance is zero.
const SEND_MODE_DESTROY = 32;
/// mode = 64 is used for messages that carry all the remaining value of the inbound message in addition to the value initially indicated in the new message.
const SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE = 64;
/// mode = 128 is used for messages that are to carry all the remaining balance of the current smart contract (instead of the value originally indicated in the message).
const SEND_MODE_CARRY_ALL_BALANCE = 128;
/// do not create an action, only estimate fee. TVM UPGRADE 2023-07. https://docs.ton.org/learn/tvm-instructions/tvm-upgrade-2023-07#sending-messages
const SEND_MODE_ESTIMATE_FEE_ONLY = 1024;
/// Other modes affect the fee calculation as follows:
/// +64 substitutes the entire balance of the incoming message as an outcoming value (slightly inaccurate, gas expenses that cannot be estimated before the computation is completed are not taken into account).
/// +128 substitutes the value of the entire balance of the contract before the start of the computation phase (slightly inaccurate, since gas expenses that cannot be estimated before the completion of the computation phase are not taken into account).
/// Sends a raw message — a correctly serialized TL object `Message X`.
/// For `mode`, see constants above (except SEND_MODE_ESTIMATE_FEE_ONLY).
/// This function is still available, but deprecated: consider using [sendMessage].
@deprecated
fun sendRawMessage(msg: cell, mode: int): void
asm "SENDRAWMSG";
/// Creates an output action and returns a fee for creating a message.
/// Mode has the same effect as in the case of SENDRAWMSG.
/// For mode including SEND_MODE_ESTIMATE_FEE_ONLY it just returns estimated fee without sending a message.
fun sendMessage(msg: cell, mode: int): int
asm "SENDMSG";

View file

@ -0,0 +1,63 @@
// A part of standard library for Tolk
tolk 0.6
/**
Gas and payment related primitives.
*/
/// Returns amount of gas (in gas units) consumed in current Computation Phase.
fun getGasConsumedAtTheMoment(): int
asm "GASCONSUMED";
/// This function is required to be called when you process an external message (from an outer world)
/// and "accept" it to blockchain.
/// Without calling this function, an external message would be discarded.
/// As an effect, the current smart contract agrees to buy some gas to finish the current transaction.
/// For more details, check [accept_message effects](https://ton.org/docs/#/smart-contracts/accept).
fun acceptExternalMessage(): void
asm "ACCEPT";
/// When processing an internal message, by default, the limit of gas consumption is determined by incoming message.
/// Functions [setGasLimit] and [setGasLimitToMaximum] allow you to change this behavior.
/// Sets current gas limit `gl` to its maximal allowed value `gm`, and resets the gas credit `gc` to zero,
/// decreasing the value of `gr` by `gc` in the process.
fun setGasLimitToMaximum(): void
asm "ACCEPT";
/// When processing an internal message, by default, the limit of gas consumption is determined by incoming message.
/// Functions [setGasLimit] and [setGasLimitToMaximum] allow you to change this behavior.
/// Sets current gas limit `gl` to the minimum of limit and `gm`, and resets the gas credit `gc` to zero.
/// If the gas consumed so far (including the present instruction) exceeds the resulting value of `gl`,
/// an (unhandled) out of gas exception is thrown before setting new gas limits.
fun setGasLimit(limit: int): void
asm "SETGASLIMIT";
/// Calculates fee (amount in nanotoncoins to be paid) for a transaction which consumed [gasUsed] gas units.
fun calculateGasFee(workchain: int, gasUsed: int): int
asm(gasUsed workchain) "GETGASFEE";
/// Same as [calculateGasFee], but without flat price (you have supposed to read https://docs.ton.org/develop/howto/fees-low-level)
fun calculateGasFeeWithoutFlatPrice(workchain: int, gasUsed: int): int
asm(gasUsed workchain) "GETGASFEESIMPLE";
/// Calculates amount of nanotoncoins you should pay for storing a contract of provided size for [seconds].
/// [bits] and [cells] represent contract state (code + data).
fun calculateStorageFee(workchain: int, seconds: int, bits: int, cells: int): int
asm(cells bits seconds workchain) "GETSTORAGEFEE";
/// Calculates amount of nanotoncoins you should pay to send a message of specified size.
fun calculateMessageFee(workchain: int, bits: int, cells: int): int
asm(cells bits workchain) "GETFORWARDFEE";
/// Same as [calculateMessageFee], but without lump price (you have supposed to read https://docs.ton.org/develop/howto/fees-low-level)
fun calculateMessageFeeWithoutLumpPrice(workchain: int, bits: int, cells: int): int
asm(cells bits workchain) "GETFORWARDFEESIMPLE";
/// Calculates fee that was paid by the sender of an incoming internal message.
fun calculateOriginalMessageFee(workchain: int, incomingFwdFee: int): int
asm(incomingFwdFee workchain) "GETORIGINALFWDFEE";
/// Returns the amount of nanotoncoins current contract debts for storage. ("due" and "debt" are synonyms)
/// If it has no debt, `0` is returned.
fun getMyStorageDuePayment(): int
asm "DUEPAYMENT";

View file

@ -0,0 +1,38 @@
// A part of standard library for Tolk
tolk 0.6
/**
Lisp-style lists are nested 2-elements tuples: `(1, (2, (3, null)))` represents list `[1, 2, 3]`.
Elements of a list can be of different types.
Empty list is conventionally represented as TVM `null` value.
*/
@pure
fun createEmptyList(): tuple
asm "PUSHNULL";
/// Adds an element to the beginning of lisp-style list.
/// Note, that it does not mutate the list: instead, it returns a new one (it's a lisp pattern).
@pure
fun listPrepend<X>(head: X, tail: tuple): tuple
asm "CONS";
/// Extracts the head and the tail of lisp-style list.
@pure
fun listSplit<X>(list: tuple): (X, tuple)
asm "UNCONS";
/// Extracts the tail and the head of lisp-style list.
@pure
fun listNext<X>(mutate self: tuple): X
asm( -> 1 0) "UNCONS";
/// Returns the head of lisp-style list.
@pure
fun listGetHead<X>(list: tuple): X
asm "CAR";
/// Returns the tail of lisp-style list.
@pure
fun listGetTail(list: tuple): tuple
asm "CDR";

View file

@ -0,0 +1,311 @@
// A part of standard library for Tolk
tolk 0.6
/**
Dictionaries are represented as `cell` data type (cells can store anything, dicts in particular).
Currently, they have very low-level API very close to TVM internals.
Most of functions are duplicated for three common cases:
- iDict* - dicts with signed integer keys
- uDict* - dicts with unsigned integer keys
- sDict* - dicts with arbitrary slice keys
When accessing a dict element, you should not only provide a key, but provide keyLen,
since for optimization, for optimization, key length is not stored in the dictionary itself.
*/
/// Creates an empty dictionary, which is actually a null value. Equivalent to PUSHNULL
@pure
fun createEmptyDict(): cell
asm "NEWDICT";
/// Checks whether a dictionary is empty.
@pure
fun dictIsEmpty(self: cell): int
asm "DICTEMPTY";
@pure
fun iDictGet(self: cell, keyLen: int, key: int): (slice, int)
asm(key self keyLen) "DICTIGET" "NULLSWAPIFNOT";
@pure
fun uDictGet(self: cell, keyLen: int, key: int): (slice, int)
asm(key self keyLen) "DICTUGET" "NULLSWAPIFNOT";
@pure
fun sDictGet(self: cell, keyLen: int, key: slice): (slice, int)
asm(key self keyLen) "DICTGET" "NULLSWAPIFNOT";
@pure
fun iDictSet(mutate self: cell, keyLen: int, key: int, value: slice): void
asm(value key self keyLen) "DICTISET";
@pure
fun uDictSet(mutate self: cell, keyLen: int, key: int, value: slice): void
asm(value key self keyLen) "DICTUSET";
@pure
fun sDictSet(mutate self: cell, keyLen: int, key: slice, value: slice): void
asm(value key self keyLen) "DICTSET";
@pure
fun iDictSetRef(mutate self: cell, keyLen: int, key: int, value: cell): void
asm(value key self keyLen) "DICTISETREF";
@pure
fun uDictSetRef(mutate self: cell, keyLen: int, key: int, value: cell): void
asm(value key self keyLen) "DICTUSETREF";
@pure
fun sDictSetRef(mutate self: cell, keyLen: int, key: slice, value: cell): void
asm(value key self keyLen) "DICTSETREF";
@pure
fun iDictSetIfNotExists(mutate self: cell, keyLen: int, key: int, value: slice): int
asm(value key self keyLen) "DICTIADD";
@pure
fun uDictSetIfNotExists(mutate self: cell, keyLen: int, key: int, value: slice): int
asm(value key self keyLen) "DICTUADD";
@pure
fun iDictSetIfExists(mutate self: cell, keyLen: int, key: int, value: slice): int
asm(value key self keyLen) "DICTIREPLACE";
@pure
fun uDictSetIfExists(mutate self: cell, keyLen: int, key: int, value: slice): int
asm(value key self keyLen) "DICTUREPLACE";
@pure
fun iDictGetRef(self: cell, keyLen: int, key: int): (cell, int)
asm(key self keyLen) "DICTIGETREF" "NULLSWAPIFNOT";
@pure
fun uDictGetRef(self: cell, keyLen: int, key: int): (cell, int)
asm(key self keyLen) "DICTUGETREF" "NULLSWAPIFNOT";
@pure
fun sDictGetRef(self: cell, keyLen: int, key: slice): (cell, int)
asm(key self keyLen) "DICTGETREF" "NULLSWAPIFNOT";
@pure
fun iDictGetRefOrNull(self: cell, keyLen: int, key: int): cell
asm(key self keyLen) "DICTIGETOPTREF";
@pure
fun uDictGetRefOrNull(self: cell, keyLen: int, key: int): cell
asm(key self keyLen) "DICTUGETOPTREF";
@pure
fun sDictGetRefOrNull(self: cell, keyLen: int, key: slice): cell
asm(key self keyLen) "DICTGETOPTREF";
@pure
fun iDictDelete(mutate self: cell, keyLen: int, key: int): int
asm(key self keyLen) "DICTIDEL";
@pure
fun uDictDelete(mutate self: cell, keyLen: int, key: int): int
asm(key self keyLen) "DICTUDEL";
@pure
fun sDictDelete(mutate self: cell, keyLen: int, key: slice): int
asm(key self keyLen) "DICTDEL";
@pure
fun iDictSetAndGet(mutate self: cell, keyLen: int, key: int, value: slice): (slice, int)
asm(value key self keyLen) "DICTISETGET" "NULLSWAPIFNOT";
@pure
fun uDictSetAndGet(mutate self: cell, keyLen: int, key: int, value: slice): (slice, int)
asm(value key self keyLen) "DICTUSETGET" "NULLSWAPIFNOT";
@pure
fun sDictSetAndGet(mutate self: cell, keyLen: int, key: slice, value: slice): (slice, int)
asm(value key self keyLen) "DICTSETGET" "NULLSWAPIFNOT";
@pure
fun iDictSetAndGetRefOrNull(mutate self: cell, keyLen: int, key: int, value: cell): cell
asm(value key self keyLen) "DICTISETGETOPTREF";
@pure
fun uDictSetAndGetRefOrNull(mutate self: cell, keyLen: int, key: int, value: cell): cell
asm(value key self keyLen) "DICTUSETGETOPTREF";
@pure
fun iDictDeleteAndGet(mutate self: cell, keyLen: int, key: int): (slice, int)
asm(key self keyLen) "DICTIDELGET" "NULLSWAPIFNOT";
@pure
fun uDictDeleteAndGet(mutate self: cell, keyLen: int, key: int): (slice, int)
asm(key self keyLen) "DICTUDELGET" "NULLSWAPIFNOT";
@pure
fun sDictDeleteAndGet(mutate self: cell, keyLen: int, key: slice): (slice, int)
asm(key self keyLen) "DICTDELGET" "NULLSWAPIFNOT";
@pure
fun iDictSetBuilder(mutate self: cell, keyLen: int, key: int, value: builder): void
asm(value key self keyLen) "DICTISETB";
@pure
fun uDictSetBuilder(mutate self: cell, keyLen: int, key: int, value: builder): void
asm(value key self keyLen) "DICTUSETB";
@pure
fun sDictSetBuilder(mutate self: cell, keyLen: int, key: slice, value: builder): void
asm(value key self keyLen) "DICTSETB";
@pure
fun iDictSetBuilderIfNotExists(mutate self: cell, keyLen: int, key: int, value: builder): int
asm(value key self keyLen) "DICTIADDB";
@pure
fun uDictSetBuilderIfNotExists(mutate self: cell, keyLen: int, key: int, value: builder): int
asm(value key self keyLen) "DICTUADDB";
@pure
fun iDictSetBuilderIfExists(mutate self: cell, keyLen: int, key: int, value: builder): int
asm(value key self keyLen) "DICTIREPLACEB";
@pure
fun uDictSetBuilderIfExists(mutate self: cell, keyLen: int, key: int, value: builder): int
asm(value key self keyLen) "DICTUREPLACEB";
@pure
fun iDictDeleteFirstAndGet(mutate self: cell, keyLen: int): (int, slice, int)
asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
@pure
fun uDictDeleteFirstAndGet(mutate self: cell, keyLen: int): (int, slice, int)
asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
@pure
fun sDictDeleteFirstAndGet(mutate self: cell, keyLen: int): (slice, slice, int)
asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
@pure
fun iDictDeleteLastAndGet(mutate self: cell, keyLen: int): (int, slice, int)
asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
@pure
fun uDictDeleteLastAndGet(mutate self: cell, keyLen: int): (int, slice, int)
asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
@pure
fun sDictDeleteLastAndGet(mutate self: cell, keyLen: int): (slice, slice, int)
asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
@pure
fun iDictGetFirst(self: cell, keyLen: int): (int, slice, int)
asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2";
@pure
fun uDictGetFirst(self: cell, keyLen: int): (int, slice, int)
asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2";
@pure
fun sDictGetFirst(self: cell, keyLen: int): (slice, slice, int)
asm (-> 1 0 2) "DICTMIN" "NULLSWAPIFNOT2";
@pure
fun iDictGetFirstAsRef(self: cell, keyLen: int): (int, cell, int)
asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2";
@pure
fun uDictGetFirstAsRef(self: cell, keyLen: int): (int, cell, int)
asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2";
@pure
fun sDictGetFirstAsRef(self: cell, keyLen: int): (slice, cell, int)
asm (-> 1 0 2) "DICTMINREF" "NULLSWAPIFNOT2";
@pure
fun iDictGetLast(self: cell, keyLen: int): (int, slice, int)
asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2";
@pure
fun uDictGetLast(self: cell, keyLen: int): (int, slice, int)
asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2";
@pure
fun sDictGetLast(self: cell, keyLen: int): (slice, slice, int)
asm (-> 1 0 2) "DICTMAX" "NULLSWAPIFNOT2";
@pure
fun iDictGetLastAsRef(self: cell, keyLen: int): (int, cell, int)
asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2";
@pure
fun uDictGetLastAsRef(self: cell, keyLen: int): (int, cell, int)
asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2";
@pure
fun sDictGetLastAsRef(self: cell, keyLen: int): (slice, cell, int)
asm (-> 1 0 2) "DICTMAXREF" "NULLSWAPIFNOT2";
@pure
fun iDictGetNext(self: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot self keyLen -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2";
@pure
fun uDictGetNext(self: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot self keyLen -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2";
@pure
fun iDictGetNextOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot self keyLen -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2";
@pure
fun uDictGetNextOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot self keyLen -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2";
@pure
fun iDictGetPrev(self: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot self keyLen -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2";
@pure
fun uDictGetPrev(self: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot self keyLen -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2";
@pure
fun iDictGetPrevOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot self keyLen -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2";
@pure
fun uDictGetPrevOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot self keyLen -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2";
/**
Prefix dictionary primitives.
*/
@pure
fun prefixDictGet(self: cell, keyLen: int, key: slice): (slice, slice, slice, int)
asm(key self keyLen) "PFXDICTGETQ" "NULLSWAPIFNOT2";
@pure
fun prefixDictSet(mutate self: cell, keyLen: int, key: slice, value: slice): int
asm(value key self keyLen) "PFXDICTSET";
@pure
fun prefixDictDelete(mutate self: cell, keyLen: int, key: slice): int
asm(key self keyLen) "PFXDICTDEL";

View file

@ -0,0 +1,25 @@
// A part of standard library for Tolk
tolk 0.6
/// Usually `c3` has a continuation initialized by the whole code of the contract. It is used for function calls.
/// The primitive returns the current value of `c3`.
@pure
fun getTvmRegisterC3(): continuation
asm "c3 PUSH";
/// Updates the current value of `c3`. Usually, it is used for updating smart contract code in run-time.
/// Note that after execution of this primitive the current code
/// (and the stack of recursive function calls) won't change,
/// but any other function call will use a function from the new code.
fun setTvmRegisterC3(c: continuation): void
asm "c3 POP";
/// Transforms a `slice` [s] into a simple ordinary continuation `c`, with `c.code = s` and an empty stack and savelist.
@pure
fun transformSliceToContinuation(s: slice): continuation
asm "BLESS";
/// Moves a variable or a value [x] to the top of the stack.
@pure
fun stackMoveToTop<X>(mutate self: X): void
asm "NOP";

View file

@ -909,7 +909,7 @@ bool TestNode::show_help(std::string command) {
"saveaccount[code|data] <filename> <addr> [<block-id-ext>]\tSaves into specified file the most recent state "
"(StateInit) or just the code or data of specified account; <addr> is in "
"[<workchain>:]<hex-or-base64-addr> format\n"
"runmethod[full] <addr> [<block-id-ext>] <method-id> <params>...\tRuns GET method <method-id> of account "
"runmethod[full] <addr> [<block-id-ext>] <name> <params>...\tRuns GET method <name> of account "
"<addr> "
"with specified parameters\n"
"dnsresolve [<block-id-ext>] <domain> [<category>]\tResolves a domain starting from root dns smart contract\n"

View file

@ -564,7 +564,7 @@ td::Result<std::shared_ptr<Certificate>> Certificate::create(tl_object_ptr<ton_a
}
BroadcastCheckResult Certificate::check(PublicKeyHash node, OverlayIdShort overlay_id, td::int32 unix_time,
td::uint32 size, bool is_fec) const {
td::uint32 size, bool is_fec, bool skip_check_signature) const {
if (size > max_size_) {
return BroadcastCheckResult::Forbidden;
}
@ -575,16 +575,16 @@ BroadcastCheckResult Certificate::check(PublicKeyHash node, OverlayIdShort overl
return BroadcastCheckResult::Forbidden;
}
auto R1 = issued_by_.get<PublicKey>().create_encryptor();
if (R1.is_error()) {
return BroadcastCheckResult::Forbidden;
}
auto E = R1.move_as_ok();
auto B = to_sign(overlay_id, node);
if (E->check_signature(B.as_slice(), signature_.as_slice()).is_error()) {
return BroadcastCheckResult::Forbidden;
if (!skip_check_signature) {
auto R1 = issued_by_.get<PublicKey>().create_encryptor();
if (R1.is_error()) {
return BroadcastCheckResult::Forbidden;
}
auto E = R1.move_as_ok();
auto B = to_sign(overlay_id, node);
if (E->check_signature(B.as_slice(), signature_.as_slice()).is_error()) {
return BroadcastCheckResult::Forbidden;
}
}
return (flags_ & CertificateFlags::Trusted) ? BroadcastCheckResult::Allowed : BroadcastCheckResult::NeedCheck;

View file

@ -503,37 +503,44 @@ td::Status OverlayImpl::check_date(td::uint32 date) {
return td::Status::OK();
}
BroadcastCheckResult OverlayImpl::check_source_eligible(const PublicKeyHash &source, const Certificate *cert,
BroadcastCheckResult OverlayImpl::check_source_eligible(const PublicKeyHash& source, const Certificate* cert,
td::uint32 size, bool is_fec) {
if (size == 0) {
return BroadcastCheckResult::Forbidden;
}
auto r = rules_.check_rules(source, size, is_fec);
if (!cert || r == BroadcastCheckResult::Allowed) {
return r;
}
td::Bits256 cert_hash = get_tl_object_sha_bits256(cert->tl());
auto cached_cert = checked_certificates_cache_.find(source);
bool cached = cached_cert != checked_certificates_cache_.end() && cached_cert->second->cert_hash == cert_hash;
auto r2 = cert->check(source, overlay_id_, static_cast<td::int32>(td::Clocks::system()), size, is_fec);
auto r2 = cert->check(source, overlay_id_, static_cast<td::int32>(td::Clocks::system()), size, is_fec,
/* skip_check_signature = */ cached);
if (r2 != BroadcastCheckResult::Forbidden) {
if (cached_cert == checked_certificates_cache_.end()) {
cached_cert = checked_certificates_cache_.emplace(
source, std::make_unique<CachedCertificate>(source, cert_hash)).first;
} else {
cached_cert->second->cert_hash = cert_hash;
cached_cert->second->remove();
}
checked_certificates_cache_lru_.put(cached_cert->second.get());
while (checked_certificates_cache_.size() > max_checked_certificates_cache_size_) {
auto to_remove = (CachedCertificate*)checked_certificates_cache_lru_.get();
CHECK(to_remove);
to_remove->remove();
checked_certificates_cache_.erase(to_remove->source);
}
}
r2 = broadcast_check_result_min(r2, rules_.check_rules(cert->issuer_hash(), size, is_fec));
return broadcast_check_result_max(r, r2);
}
BroadcastCheckResult OverlayImpl::check_source_eligible(PublicKey source, const Certificate *cert, td::uint32 size,
BroadcastCheckResult OverlayImpl::check_source_eligible(PublicKey source, const Certificate* cert, td::uint32 size,
bool is_fec) {
if (size == 0) {
return BroadcastCheckResult::Forbidden;
}
auto short_id = source.compute_short_id();
auto r = rules_.check_rules(short_id, size, is_fec);
if (!cert || r == BroadcastCheckResult::Allowed) {
return r;
}
auto r2 = cert->check(short_id, overlay_id_, static_cast<td::int32>(td::Clocks::system()), size, is_fec);
r2 = broadcast_check_result_min(r2, rules_.check_rules(cert->issuer_hash(), size, is_fec));
return broadcast_check_result_max(r, r2);
return check_source_eligible(source.compute_short_id(), cert, size, is_fec);
}
td::Status OverlayImpl::check_delivered(BroadcastHash hash) {

View file

@ -466,6 +466,19 @@ class OverlayImpl : public Overlay {
TrafficStats total_traffic_responses, total_traffic_responses_ctr;
OverlayOptions opts_;
struct CachedCertificate : td::ListNode {
CachedCertificate(PublicKeyHash source, td::Bits256 cert_hash)
: source(source)
, cert_hash(cert_hash) {
}
PublicKeyHash source;
td::Bits256 cert_hash;
};
std::map<PublicKeyHash, std::unique_ptr<CachedCertificate>> checked_certificates_cache_;
td::ListNode checked_certificates_cache_lru_;
size_t max_checked_certificates_cache_size_ = 1000;
};
} // namespace overlay

View file

@ -149,7 +149,7 @@ class Certificate {
td::BufferSlice to_sign(OverlayIdShort overlay_id, PublicKeyHash issued_to) const;
BroadcastCheckResult check(PublicKeyHash node, OverlayIdShort overlay_id, td::int32 unix_time, td::uint32 size,
bool is_fec) const;
bool is_fec, bool skip_check_signature = false) const;
tl_object_ptr<ton_api::overlay_Certificate> tl() const;
const PublicKey &issuer() const;
const PublicKeyHash issuer_hash() const;

130
tolk-tester/tests/a10.tolk Normal file
View file

@ -0,0 +1,130 @@
import "@stdlib/tvm-lowlevel"
fun pair_first<X, Y>(p: [X, Y]): X asm "FIRST";
fun one(dummy: tuple) {
return 1;
}
fun main(a: int, x: int) {
var y: int = 0;
var z: int = 0;
while ((y = x * x) > a) {
x -= 1;
z = one(null);
}
return (y, z);
}
fun throwIfLt10(x: int): void {
if (x > 10) {
return;
}
throw 234;
return;
}
@method_id(88)
fun test88(x: int) {
try {
var x: void = throwIfLt10(x);
return 0;
} catch(code) {
return code;
}
}
@method_id(89)
fun test89(last: int) {
var t: tuple = createEmptyTuple();
t.tuplePush(1);
t.tuplePush(2);
t.tuplePush(3);
t.tuplePush(last);
return (t.tupleAt(0), t.tupleAt(t.tupleSize() - 1), t.tupleFirst(), t.tupleLast());
}
@pure fun get10() { return 10; }
@method_id(91)
fun touchCodegen2() {
var f = get10();
f.stackMoveToTop();
return f;
}
@method_id(92)
fun testDumpDontPolluteStack() {
var f = get10();
f.debugPrint();
debugPrint(10);
var s = "asdf";
s.debugPrintString();
debugDumpStack();
debugPrintString("my");
return (f, getRemainingBitsCount(s));
}
@method_id(93)
fun testStartBalanceCodegen1() {
var t = getMyOriginalBalanceWithExtraCurrencies();
var first = t.pair_first();
return first;
}
@method_id(94)
fun testStartBalanceCodegen2() {
var first = getMyOriginalBalance();
return first;
}
/**
method_id | in | out
@testcase | 0 | 101 15 | 100 1
@testcase | 0 | 101 14 | 100 1
@testcase | 0 | 101 10 | 100 0
@testcase | 0 | 100 10 | 100 0
@testcase | 0 | 100 10 | 100 0
@testcase | 88 | 5 | 234
@testcase | 88 | 50 | 0
@testcase | 89 | 4 | 1 4 1 4
@testcase | 91 | | 10
@testcase | 92 | | 10 32
@fif_codegen
"""
touchCodegen2 PROC:<{
//
get10 CALLDICT // f
}>
"""
@fif_codegen
"""
testDumpDontPolluteStack PROC:<{
...
DUMPSTK
x{6d79} PUSHSLICE // f s _9
STRDUMP DROP
SBITS // f _11
}>
"""
@fif_codegen
"""
testStartBalanceCodegen1 PROC:<{
//
BALANCE // t
FIRST // first
}>
"""
@fif_codegen
"""
testStartBalanceCodegen2 PROC:<{
//
BALANCE
FIRST // first
}>
"""
*/

79
tolk-tester/tests/a6.tolk Normal file
View file

@ -0,0 +1,79 @@
fun f(a: int, b: int, c: int, d: int, e: int, f: int): (int, int) {
// solve a 2x2 linear equation
var D: int = a*d - b*c;;;; var Dx: int = e*d-b*f ;;;; var Dy: int = a * f - e * c;
return (Dx/D,Dy/D);
};;;;
fun calc_phi(): int {
var n = 1;
repeat (70) { n*=10; };
var p= 1;
var `q`=1;
do {
(p,q)=(q,p+q);
} while (q <= n); //;;
return mulDivRound(p, n, q);
}
fun calc_sqrt2(): int {
var n = 1;
repeat (70) { n *= 10; }
var p = 1;
var q = 1;
do {
var t = p + q;
(p, q) = (q, t + q);
} while (q <= n);
return mulDivRound(p, n, q);
}
fun calc_root(m: auto): auto {
var base: int=1;
repeat(70) { base *= 10; }
var (a, b, c) = (1,0,-m);
var (p1, q1, p2, q2) = (1, 0, 0, 1);
do {
var k: int=-1;
var (a1, b1, c1) = (0, 0, 0);
do {
k+=1;
(a1, b1, c1) = (a, b, c);
c+=b;
c += b += a;
} while (c <= 0);
(a, b, c) = (-c1, -b1, -a1);
(p1, q1) = (k * p1+q1, p1);
(p2, q2) = (k * p2+q2, p2);
} while (p1 <= base);
return (p1, q1, p2, q2);
}
fun ataninv(base: int, q: int): int { // computes base*atan(1/q)
base=base~/q;
q*=-q;
var sum: int = 0;
var n: int = 1;
do {
sum += base~/n;
base = base~/q;
n += 2;
} while (base != 0);
return sum;
}
fun calc_pi(): int {
var base: int = 64;
repeat (70) { base *= 10; }
return (ataninv(base << 2, 5) - ataninv(base, 239))~>>4;
}
fun main(): int {
return calc_pi();
}
/**
method_id | in | out
@testcase | 0 | | 31415926535897932384626433832795028841971693993751058209749445923078164
@code_hash 84337043972311674339187056298873613816389434478842780265748859098303774481976
*/

View file

@ -0,0 +1,22 @@
fun main(a: int, b: int, c: int, d: int, e: int, f: int): (int, int) {
var D: int = a * d - b * c;
var Dx: int = e * d - b * f;
var Dy: int = a * f - e * c;
return (Dx / D, Dy / D);
}
@method_id(101)
fun testDivMod(x: int, y: int) {
return [divMod(x, y), modDiv(x, y), mulDivMod(x, y, 10)];
}
/**
method_id | in | out
@testcase | 0 | 1 1 1 -1 10 6 | 8 2
@testcase | 0 | 817 -31 624 -241 132272 272276 | 132 -788
@testcase | 0 | -886 562 498 -212 -36452 -68958 | -505 -861
@testcase | 0 | 448 -433 -444 792 150012 -356232 | -218 -572
@testcase | 0 | -40 -821 433 -734 -721629 -741724 | -206 889
@testcase | 0 | -261 -98 -494 868 -166153 733738 | 263 995
@testcase | 101 | 112 3 | [ 37 1 1 37 33 6 ]
*/

View file

@ -0,0 +1,26 @@
@deprecated
fun twice(f: auto, x: auto): auto {
return f (f (x));
}
fun sqr(x: int) {
return x * x;
}
fun main(x: int): int {
var f = sqr;
return twice(f, x) * f(x);
}
@method_id(4)
fun pow6(x: int): int {
return twice(sqr, x) * sqr(x);
}
/**
method_id | in | out
@testcase | 0 | 3 | 729
@testcase | 0 | 10 | 1000000
@testcase | 4 | 3 | 729
@testcase | 4 | 10 | 1000000
*/

24
tolk-tester/tests/a7.tolk Normal file
View file

@ -0,0 +1,24 @@
fun main() { }
@method_id(1)
fun steps(x: int): int {
var n = 0;
while (x > 1) {
n += 1;
if (x & 1) {
x = 3 * x + 1;
} else {
x >>= 1;
}
}
return n;
}
/**
method_id | in | out
@testcase | 1 | 1 | 0
@testcase | 1 | 2 | 1
@testcase | 1 | 5 | 5
@testcase | 1 | 19 | 20
@testcase | 1 | 27 | 111
@testcase | 1 | 100 | 25
*/

View file

@ -0,0 +1,111 @@
fun unsafe_tuple<X>(x: X): tuple
asm "NOP";
fun inc(x: int, y: int): (int, int) {
return (x + y, y * 10);
}
fun `~inc`(mutate self: int, y: int): int {
val (newX, newY) = inc(self, y);
self = newX;
return newY;
}
@method_id(11)
fun test_return(x: int): (int, int, int, int, int, int, int) {
return (x, x.`~inc`(x / 20), x, x = x * 2, x, x += 1, x);
}
@method_id(12)
fun test_assign(x: int): (int, int, int, int, int, int, int) {
var (x1: int, x2: int, x3: int, x4: int, x5: int, x6: int, x7: int) = (x, x.`~inc`(x / 20), x, x=x*2, x, x+=1, x);
return (x1, x2, x3, x4, x5, x6, x7);
}
@method_id(13)
fun test_tuple(x: int): tuple {
var t: tuple = unsafe_tuple([x, x.`~inc`(x / 20), x, x = x * 2, x, x += 1, x]);
return t;
}
@method_id(14)
fun test_tuple_assign(x: int): (int, int, int, int, int, int, int) {
var [x1: int, x2: int, x3: int, x4: int, x5: int, x6: int, x7: int] = [x, x.`~inc`(x / 20), x, x = x * 2, x, x += 1, x];
return (x1, x2, x3, x4, x5, x6, x7);
}
fun foo1(x1: int, x2: int, x3: int, x4: int, x5: int, x6: int, x7: int): (int, int, int, int, int, int, int) {
return (x1, x2, x3, x4, x5, x6, x7);
}
@method_id(15)
fun test_call_1(x: int): (int, int, int, int, int, int, int) {
return foo1(x, x.`~inc`(x / 20), x, x = x * 2, x, x += 1, x);
}
fun foo2(x1: int, x2: int, x3456: (int, int, int, int), x7: int): (int, int, int, int, int, int, int) {
var (x3: int, x4: int, x5: int, x6: int) = x3456;
return (x1, x2, x3, x4, x5, x6, x7);
}
@method_id(16)
fun test_call_2(x: int): (int, int, int, int, int, int, int) {
return foo2(x, x.`~inc`(x / 20), (x, x = x * 2, x, x += 1), x);
}
fun asm_func(x1: int, x2: int, x3: int, x4: int, x5: int, x6: int, x7: int): (int, int, int, int, int, int, int)
asm
(x4 x5 x6 x7 x1 x2 x3->0 1 2 3 4 5 6) "NOP";
@method_id(17)
fun test_call_asm_old(x: int): (int, int, int, int, int, int, int) {
return asm_func(x, x += 1, x, x, x.`~inc`(x / 20), x, x = x * 2);
}
@method_id(18)
fun test_call_asm_new(x: int): (int, int, int, int, int, int, int) {
return asm_func(x, x.`~inc`(x / 20), x, x = x * 2, x, x += 1, x);
}
global xx: int;
@method_id(19)
fun test_global(x: int): (int, int, int, int, int, int, int) {
xx = x;
return (xx, xx.`~inc`(xx / 20), xx, xx = xx * 2, xx, xx += 1, xx);
}
@method_id(20)
fun test_if_else(x: int): (int, int, int, int, int) {
if (x > 10) {
return (x.`~inc`(8), x + 1, x = 1, x <<= 3, x);
} else {
xx = 9;
return (x, x.`~inc`(-4), x.`~inc`(-1), x >= 1, x = x + xx);
}
}
fun main() {
}
/**
method_id | in | out
@testcase | 11 | 100 | 100 50 105 210 210 211 211
@testcase | 12 | 100 | 100 50 105 210 210 211 211
@testcase | 13 | 100 | [ 100 50 105 210 210 211 211 ]
@testcase | 14 | 100 | 100 50 105 210 210 211 211
@testcase | 15 | 100 | 100 50 105 210 210 211 211
@testcase | 16 | 100 | 100 50 105 210 210 211 211
@testcase | 17 | 100 | 101 50 106 212 100 101 101
@testcase | 18 | 100 | 210 210 211 211 100 50 105
@testcase | 19 | 100 | 100 50 105 210 210 211 211
@testcase | 20 | 80 | 80 89 1 8 8
@testcase | 20 | 9 | 9 -40 -10 -1 13
@fif_codegen
"""
~inc PROC:<{
// self y
inc CALLDICT // self newY
}>
"""
@code_hash 97139400653362069936987769894397430077752335662822462908581556703209313861576
*/

View file

@ -0,0 +1,139 @@
@pure
fun empty_tuple2(): tuple
asm "NIL";
@pure
fun tpush2<X>(mutate self: tuple, x: X): void
asm "TPUSH";
@pure
fun asm_func_1(x: int, y: int, z: int): tuple
asm "3 TUPLE";
@pure
fun asm_func_2(x: int, y: int, z: int): tuple
asm (z y x -> 0) "3 TUPLE";
@pure
fun asm_func_3(x: int, y: int, z: int): tuple
asm (y z x -> 0) "3 TUPLE";
@pure
fun asm_func_4(a: int, b: (int, (int, int)), c: int): tuple
asm (b a c -> 0) "5 TUPLE";
@pure
fun asm_func_modify(mutate self: tuple, b: int, c: int): void
asm (c b self) "SWAP TPUSH SWAP TPUSH";
global t: tuple;
fun foo(x: int): int {
t.tpush2(x);
return x * 10;
}
@method_id(11)
fun test_old_1(): (tuple, tuple) {
t = empty_tuple2();
var t2: tuple = asm_func_1(foo(11), foo(22), foo(33));
return (t, t2);
}
@method_id(12)
fun test_old_2(): (tuple, tuple) {
t = empty_tuple2();
var t2: tuple = asm_func_2(foo(11), foo(22), foo(33));
return (t, t2);
}
@method_id(13)
fun test_old_3(): (tuple, tuple) {
t = empty_tuple2();
var t2: tuple = asm_func_3(foo(11), foo(22), foo(33));
return (t, t2);
}
@method_id(14)
fun test_old_4(): (tuple, tuple) {
t = empty_tuple2();
var t2: tuple = empty_tuple2();
// This actually computes left-to-right even without compute-asm-ltr
t2 = asm_func_4(foo(11), (foo(22), (foo(33), foo(44))), foo(55));
return (t, t2);
}
@method_id(15)
fun test_old_modify(): (tuple, tuple) {
t = empty_tuple2();
var t2: tuple = empty_tuple2();
t2.asm_func_modify(foo(22), foo(33));
return (t, t2);
}
@method_id(16)
fun test_old_dot(): (tuple, tuple) {
t = empty_tuple2();
var t2: tuple = foo(11).asm_func_3(foo(22), foo(33));
return (t, t2);
}
@method_id(21)
fun test_new_1(): (tuple, tuple) {
t = empty_tuple2();
var t2: tuple = asm_func_1(foo(11), foo(22), foo(33));
return (t, t2);
}
@method_id(22)
fun test_new_2(): (tuple, tuple) {
t = empty_tuple2();
var t2: tuple = asm_func_2(foo(11), foo(22), foo(33));
return (t, t2);
}
@method_id(23)
fun test_new_3(): (tuple, tuple) {
t = empty_tuple2();
var t2: tuple = asm_func_3(foo(11), foo(22), foo(33));
return (t, t2);
}
@method_id(24)
fun test_new_4(): (tuple, tuple) {
t = empty_tuple2();
var t2: tuple = asm_func_4(foo(11), (foo(22), (foo(33), foo(44))), foo(55));
return (t, t2);
}
@method_id(25)
fun test_new_modify(): (tuple, tuple) {
t = empty_tuple2();
var t2: tuple = empty_tuple2();
t2.asm_func_modify(foo(22), foo(33));
return (t, t2);
}
@method_id(26)
fun test_new_dot(): (tuple, tuple) {
t = empty_tuple2();
var t2: tuple = foo(11).asm_func_3(foo(22), foo(33));
return (t, t2);
}
fun main() {
}
/**
method_id | in | out
@testcase | 11 | | [ 11 22 33 ] [ 110 220 330 ]
@testcase | 12 | | [ 11 22 33 ] [ 330 220 110 ]
@testcase | 13 | | [ 11 22 33 ] [ 220 330 110 ]
@testcase | 14 | | [ 11 22 33 44 55 ] [ 220 330 440 110 550 ]
@testcase | 15 | | [ 22 33 ] [ 220 330 ]
@testcase | 16 | | [ 11 22 33 ] [ 220 330 110 ]
@testcase | 21 | | [ 11 22 33 ] [ 110 220 330 ]
@testcase | 22 | | [ 11 22 33 ] [ 330 220 110 ]
@testcase | 23 | | [ 11 22 33 ] [ 220 330 110 ]
@testcase | 24 | | [ 11 22 33 44 55 ] [ 220 330 440 110 550 ]
@testcase | 25 | | [ 22 33 ] [ 220 330 ]
@testcase | 26 | | [ 11 22 33 ] [ 220 330 110 ]
@code_hash 93068291567112337250118419287631047120002003622184251973082208096953112184588
*/

View file

@ -0,0 +1,53 @@
fun lshift(): int {
return (1 << 0) == 1;
}
fun rshift(): int {
return (1 >> 0) == 1;
}
fun lshift_var(i: int): int {
return (1 << i) == 1;
}
fun rshift_var(i: int): int {
return (1 >> i) == 1;
}
fun main(x: int): int {
if (x == 0) {
return lshift();
} else if (x == 1) {
return rshift();
} else if (x == 2) {
return lshift_var(0);
} else if (x == 3) {
return rshift_var(0);
} else if (x == 4) {
return lshift_var(1);
} else {
return rshift_var(1);
}
}
@method_id(11)
fun is_claimed(index: int): int {
var claim_bit_index: int = index % 256;
var mask: int = 1 << claim_bit_index;
return (255 & mask) == mask;
}
/**
method_id | in | out
@testcase | 0 | 0 | -1
@testcase | 0 | 1 | -1
@testcase | 0 | 2 | -1
@testcase | 0 | 3 | -1
@testcase | 0 | 4 | 0
@testcase | 0 | 5 | 0
@testcase | 11 | 0 | -1
@testcase | 11 | 1 | -1
@testcase | 11 | 256 | -1
@testcase | 11 | 8 | 0
*/

27
tolk-tester/tests/c2.tolk Normal file
View file

@ -0,0 +1,27 @@
global op: (int, int) -> int;
fun check_assoc(a: int, b: int, c: int): int {
return op(op(a, b), c) == op(a, op(b, c));
}
fun unnamed_args(_: int, _: slice, _: auto): auto {
return true;
}
fun main(x: int, y: int, z: int): int {
op = `_+_`;
return check_assoc(x, y, z);
}
@method_id(101)
fun test101(x: int, z: int): auto {
return unnamed_args(x, "asdf", z);
}
/**
method_id | in | out
@testcase | 0 | 2 3 9 | -1
@testcase | 0 | 11 22 44 | -1
@testcase | 0 | -1 -10 -20 | -1
@testcase | 101 | 1 10 | -1
*/

View file

@ -0,0 +1,14 @@
fun check_assoc(op: auto, a: int, b: int, c: int) {
return op(op(a, b), c) == op(a, op(b, c));
}
fun main(x: int, y: int, z: int): int {
return check_assoc(`_+_`, x, y, z);
}
/**
method_id | in | out
@testcase | 0 | 2 3 9 | -1
@testcase | 0 | 11 22 44 | -1
@testcase | 0 | -1 -10 -20 | -1
*/

View file

@ -0,0 +1,231 @@
fun store_u32(mutate self: builder, value: int): self {
return self.storeUint(value, 32);
}
fun load_u32(mutate self: slice): int {
return self.loadUint(32);
}
fun myLoadInt(mutate self: slice, len: int): int
asm(-> 1 0) "LDIX";
fun myStoreInt(mutate self: builder, x: int, len: int): self
asm(x self len) "STIX";
@method_id(101)
fun test1(): [int,int,int,int,int] {
var b: builder = beginCell().storeUint(1, 32);
b = b.storeUint(2, 32);
b.storeUint(3, 32);
b = b.store_u32(4);
b.store_u32(5);
var cs: slice = b.endCell().beginParse();
var one: int = cs.loadUint(32);
var (two: int, three: int) = (cs.loadUint(32), cs.load_u32());
var four: int = cs.load_u32();
var five: int = cs.load_u32();
return [one,two,three,four,five];
}
@method_id(102)
fun test2(): [int,int,int] {
var b: builder = beginCell().myStoreInt(1, 32);
b = b.myStoreInt(2, 32);
b.myStoreInt(3, 32);
var cs: slice = b.endCell().beginParse();
var one: int = cs.myLoadInt(32);
var (two: int, three: int) = (cs.myLoadInt(32), cs.myLoadInt(32));
return [one,two,three];
}
@method_id(103)
fun test3(ret: int): int {
val same: int = beginCell().storeUint(ret,32).endCell().beginParse().loadUint(32);
return same;
}
@method_id(104)
fun test4(): [int,int] {
var b: builder = beginCell().myStoreInt(1, 32);
b = b.storeInt(2, 32).storeInt(3, 32);
var cs: slice = b.endCell().beginParse();
var (one, _, three) = (cs.getFirstBits(32).loadUint(32), cs.skipBits(64), cs.load_u32());
return [one,three];
}
@method_id(105)
fun test5(): [int,int] {
var cref: cell = endCell(beginCell().store_u32(105));
var c: cell = beginCell().storeRef(cref).storeRef(cref).store_u32(1).endCell();
var cs: slice = beginParse(c);
var sto5x2: int = cs.loadRef().beginParse().load_u32() + cs.loadRef().beginParse().loadUint(32);
return [sto5x2, cs.load_u32()];
}
@method_id(106)
fun test6() {
return beginCell().storeUint(1, 32).storeUint(2, 32).storeUint(3, 32);
}
@method_id(107)
fun test7() {
// since .store() methods now mutate, this piece of code works not as earlier (mutates uri_builder)
var uri_builder = beginCell();
var uri_slice = uri_builder.storeSlice(".json").endCell().beginParse();
var image_slice = uri_builder.storeSlice(".png").endCell().beginParse();
return (uri_builder.getBuilderBitsCount(), uri_slice.getRemainingBitsCount(), image_slice.getRemainingBitsCount());
}
@method_id(108)
fun test8() {
var uri_builder = beginCell();
var fresh = uri_builder;
var uri_slice = fresh.storeSlice(".json").endCell().beginParse();
var fresh redef = uri_builder;
var image_slice = fresh.storeSlice(".png").endCell().beginParse();
return (uri_builder.getBuilderBitsCount(), uri_slice.getRemainingBitsCount(), image_slice.getRemainingBitsCount());
}
fun sumNumbersInSlice(mutate self: slice): int {
var result = 0;
while (!self.isEndOfSliceBits()) {
result += self.loadUint(32);
}
return result;
}
@method_id(110)
fun test10() {
var ref = beginCell().storeInt(100, 32).endCell();
var s: slice = beginCell().storeInt(1, 32).storeInt(2, 32).storeRef(ref).endCell().beginParse();
var result = (getRemainingBitsCount(s), s.sumNumbersInSlice(), getRemainingBitsCount(s), isEndOfSlice(s), isEndOfSliceBits(s), isEndOfSliceRefs(s));
var ref2: cell = s.loadRef();
var s2: slice = ref2.beginParse();
s.assertEndOfSlice();
return (result, s2.loadInt(32), s2.isEndOfSlice());
}
@method_id(111)
fun test11() {
var s: slice = beginCell().storeInt(1, 32).storeInt(2, 32).storeInt(3, 32).storeInt(4, 32).storeInt(5, 32).storeInt(6, 32).storeInt(7, 32).endCell().beginParse();
var size1 = getRemainingBitsCount(s);
s.skipBits(32);
var s1: slice = s.getFirstBits(64);
var n1 = s1.loadInt(32);
var size2 = getRemainingBitsCount(s);
s.loadInt(32);
var size3 = getRemainingBitsCount(s);
s.removeLastBits(32);
var size4 = getRemainingBitsCount(s);
var n2 = s.loadInt(32);
var size5 = getRemainingBitsCount(s);
return (n1, n2, size1, size2, size3, size4, size5);
}
@method_id(112)
fun test12() {
var (result1, result2) = (0, 0);
try {
beginCell().storeRef(beginCell().endCell()).endCell().beginParse().assertEndOfSlice();
result1 = 100;
} catch (code) {
result1 = code;
}
try {
beginCell().endCell().beginParse().assertEndOfSlice();
result2 = 100;
} catch (code) {
result2 = code;
}
return (result1, result2);
}
@method_id(113)
fun test13() {
var ref2 = beginCell().storeInt(1, 32).endCell();
var ref1 = beginCell().storeInt(1, 32).storeRef(ref2).endCell();
var c = beginCell().storeInt(444, 32).storeRef(ref1).storeRef(ref1).storeRef(ref1).storeRef(ref2).storeInt(4, 32).endCell();
var (n_cells1, n_bits1, n_refs1) = c.calculateCellSizeStrict(10);
var s = c.beginParse();
s.loadRef();
s.loadRef();
var n = s.loadInt(32);
var (n_cells2, n_bits2, n_refs2) = s.calculateSliceSizeStrict(10);
return ([n_cells1, n_bits1, n_refs1], [n_cells2, n_bits2, n_refs2], n);
}
@method_id(114)
fun test110(x: int) {
var s = beginCell().storeBool(x < 0).storeBool(0).storeBool(x).endCell().beginParse();
return (s.loadBool(), s.loadBool(), s.loadBool());
}
@method_id(115)
fun test111() {
var s = beginCell().storeMessageOp(123).storeMessageQueryId(456)
.storeAddressNone().storeAddressNone()
.storeUint(0, 32)
.storeUint(123, 32).storeUint(456, 64).storeUint(789, 64)
.endCell().beginParse();
var op1 = s.loadUint(32);
var q1 = s.loadUint(64);
if (s.addressIsNone()) {
s.skipBits(2);
}
if (s.loadBool() == 0) {
assert(s.loadBool() == 0) throw 444;
s.skipBouncedPrefix();
}
var op2 = s.loadMessageOp();
var q2 = s.loadMessageQueryId();
s.skipBits(64);
s.assertEndOfSlice();
assert(isMessageBounced(0x001)) throw 444;
return (op1, q1, op2, q2);
}
fun main(): int {
return 0;
}
/**
@testcase | 101 | | [ 1 2 3 4 5 ]
@testcase | 102 | | [ 1 2 3 ]
@testcase | 103 | 103 | 103
@testcase | 104 | | [ 1 3 ]
@testcase | 105 | | [ 210 1 ]
@testcase | 107 | | 72 40 72
@testcase | 108 | | 0 40 32
@testcase | 110 | | 64 3 0 0 -1 0 100 -1
@testcase | 111 | | 2 3 224 192 160 128 96
@testcase | 112 | | 9 100
@testcase | 113 | | [ 3 128 5 ] [ 2 96 3 ] 444
@testcase | 114 | -1 | -1 0 -1
@testcase | 114 | 0 | 0 0 0
@testcase | 115 | | 123 456 123 456
Note, that since 'compute-asm-ltr' became on be default, chaining methods codegen is not quite optimal.
@fif_codegen
"""
test6 PROC:<{
//
NEWC // _1
1 PUSHINT // _1 _2=1
SWAP // _2=1 _1
32 STU // _0
2 PUSHINT // _0 _6=2
SWAP // _6=2 _0
32 STU // _0
3 PUSHINT // _0 _10=3
SWAP // _10=3 _0
32 STU // _0
}>
"""
*/

View file

@ -0,0 +1,72 @@
const int1 = 1;
const int2 = 2;
const int101: int = 101;
const int111: int = 111;
const int1r = int1;
const str1 = "const1";
const str2 = "aabbcc"s;
const str2r: slice = str2;
const str1int = 0x636f6e737431;
const str2int = 0xAABBCC;
const nibbles: int = 4;
fun iget1(): int { return int1; }
fun iget2(): int { return int2; }
fun iget3(): int { return int1+int2; }
fun iget1r(): int { return int1r; }
fun sget1(): slice { return str1; }
fun sget2(): slice { return str2; }
fun sget2r(): slice { return str2r; }
const int240: int = ((int1+int2)*10)<<3;
fun iget240(): int { return int240; }
@pure
fun newc(): builder
asm "NEWC";
@pure
fun endcs(b: builder): slice
asm "ENDC" "CTOS";
@pure
fun sdeq(s1: slice, s2: slice): int
asm "SDEQ";
@pure
fun stslicer(b: builder, s: slice): builder
asm "STSLICER";
fun main() {
var i1: int = iget1();
var i2: int = iget2();
var i3: int = iget3();
assert(i1 == 1) throw int101;
assert(i2 == 2) throw 102;
assert(i3 == 3) throw 103;
var s1: slice = sget1();
var s2: slice = sget2();
var s3: slice = newc().stslicer(str1).stslicer(str2r).endcs();
assert(sdeq(s1, newc().storeUint(str1int, 12 * nibbles).endcs())) throw int111;
assert(sdeq(s2, newc().storeUint(str2int, 6 * nibbles).endcs())) throw 112;
assert(sdeq(s3, newc().storeUint(0x636f6e737431AABBCC, 18 * nibbles).endcs())) throw 113;
var i4: int = iget240();
assert(i4 == 240) throw ((104));
return 0;
}
/**
@testcase | 0 | | 0
@code_hash 61273295789179921867241079778489100375537711211918844448475493726205774530743
*/

View file

@ -0,0 +1,41 @@
fun elseif(cond: int) {
if (cond > 0) {
throw(cond);
}
}
@inline
@method_id(101)
fun foo(x: int): int {
if (x==1) {
return 111;
} else {
x *= 2;
}
return x + 1;
}
fun main(x: int): (int, int) {
return (foo(x), 222);
}
@method_id(102)
fun test2(x: int) {
try {
if (x < 0) { return -1; }
elseif (x);
} catch(excNo) {
return excNo * 1000;
}
return 0;
}
/**
method_id | in | out
@testcase | 0 | 1 | 111 222
@testcase | 0 | 3 | 7 222
@testcase | 101 | 1 | 111
@testcase | 101 | 3 | 7
@testcase | 102 | -5 | -1
@testcase | 102 | 5 | 5000
*/

View file

@ -0,0 +1,96 @@
@method_id(101)
fun test1(): int {
var x = false;
if (x == true) {
x= 100500;
}
return x;
}
fun main(s: int) {
var (z, t) = (17, s);
while (z > 0) {
t = s;
z -= 1;
}
return ~ t;
}
/**
method_id | in | out
@testcase | 0 | 1 | -2
@testcase | 0 | 5 | -6
@testcase | 101 | | 0
Below, I just give examples of @fif_codegen tag:
* a pattern can be single-line (after the tag), or multi-line, surrounded with """
* there may be multiple @fif_codegen, they all will be checked
* identation (spaces) is not checked intentionally
* "..." means any number of any lines
* lines not divided with "..." are expected to be consecutive in fif output
* //comments can be omitted, but if present, they are also expected to be equal
* there is also a tag @fif_codegen_avoid to check a pattern does not occur
@fif_codegen
"""
main PROC:<{
// s
17 PUSHINT // s _3=17
OVER // s z=17 t
WHILE:<{
...
}>DO<{ // s z t
...
s1 s(-1) PUXC // s t z
...
2 1 BLKDROP2
...
}>
"""
@fif_codegen
"""
main PROC:<{
...
WHILE:<{
...
}>DO<{
...
}>
}END>c
"""
@fif_codegen
"""
OVER
0 GTINT // s z t _5
"""
@fif_codegen
"""
"Asm.fif" include
...
PROGRAM{
...
}END>c
"""
@fif_codegen
"""
test1 PROC:<{
//
FALSE
}>
"""
@fif_codegen NOT // _8
@fif_codegen main PROC:<{
@fif_codegen_avoid PROCINLINE
@fif_codegen_avoid END c
@fif_codegen_avoid
"""
multiline
can also be
"""
*/

View file

@ -0,0 +1,31 @@
fun main(): int
// inside a comment, /* doesn't start a new one
/* but if // is inside, a comment may end at this line*/ {
var cc = "a string may contain /* or // or /*, not parsed";
// return 1;
return get10() + /*
traditional comment /* may not be nested
// line comment
// ends */1 +
1;
/* moreover, different comment styles
may be used for opening and closing
*/
}
/***
first line
//two-lined*/
@method_id(10)
fun get10(): int {
return 10;
}
/**
@testcase | 0 | | 12
@testcase | 10 | | 10
*/

View file

@ -0,0 +1,105 @@
import "@stdlib/tvm-dicts"
fun addIntToIDict(mutate self: cell, key: int, number: int): void {
return self.iDictSetBuilder(32, key, beginCell().storeInt(number, 32));
}
fun calculateDictLen(d: cell) {
var len = 0;
var (k, v, f) = d.uDictGetFirst(32);
while (f) {
len += 1;
(k, v, f) = d.uDictGetNext(32, k);
}
return len;
}
fun loadTwoDigitNumberFromSlice(mutate self: slice): int {
var n1 = self.loadInt(8);
var n2 = self.loadInt(8);
return (n1 - 48) * 10 + (n2 - 48);
}
@method_id(101)
fun test101(getK1: int, getK2: int, getK3: int) {
var dict = createEmptyDict();
dict.uDictSetBuilder(32, 1, beginCell().storeUint(1, 32));
var (old1: slice, found1) = dict.uDictSetAndGet(32, getK1, beginCell().storeUint(2, 32).endCell().beginParse());
var (old2: slice, found2) = dict.uDictSetAndGet(32, getK2, beginCell().storeUint(3, 32).endCell().beginParse());
var (cur3: slice, found3) = dict.uDictGet(32, getK3);
return (
found1 ? old1.loadUint(32) : -1,
found2 ? old2.loadUint(32) : -1,
found3 ? cur3.loadUint(32) : -1
);
}
@method_id(102)
fun test102() {
var dict = createEmptyDict();
dict.addIntToIDict(2, 102);
dict.addIntToIDict(1, 101);
dict.addIntToIDict(4, 104);
dict.addIntToIDict(3, 103);
var deleted = createEmptyTuple();
var shouldBreak = false;
while (!shouldBreak) {
var (kDel, kVal, wasDel) = dict.iDictDeleteLastAndGet(32);
if (wasDel) {
deleted.tuplePush([kDel, kVal.loadInt(32)]);
} else {
shouldBreak = true;
}
}
return deleted;
}
@method_id(103)
fun test103() {
var dict = createEmptyDict();
dict.uDictSetBuilderIfNotExists(32, 1,beginCell().storeInt(1, 32));
dict.uDictSetBuilderIfNotExists(32, 1,beginCell().storeInt(1, 32));
var len1 = calculateDictLen(dict);
dict.uDictSetBuilderIfExists(32, 2,beginCell().storeInt(1, 32));
dict.uDictSetBuilderIfExists(32, 2,beginCell().storeInt(1, 32));
var len2 = calculateDictLen(dict);
dict.uDictSetBuilder(32, 3,beginCell().storeInt(1, 32));
dict.uDictSetBuilderIfExists(32, 3,beginCell().storeInt(1, 32));
var len3 = calculateDictLen(dict);
var (delK1, _, _) = dict.uDictDeleteFirstAndGet(32);
var (delK2, _, _) = dict.uDictDeleteFirstAndGet(32);
var (delK3, _, _) = dict.uDictDeleteFirstAndGet(32);
return (len1, len2, len3, delK1, delK2, delK3);
}
@method_id(104)
fun test104() {
var dict = createEmptyDict();
dict.sDictSetBuilder(32, "7800", beginCell().storeUint(5 + 48, 8).storeUint(6 + 48, 8));
dict.sDictSet(32, "key1", "12");
var (old1, _) = dict.sDictSetAndGet(32, "key1", "34");
var (old2, _) = dict.sDictDeleteAndGet(32, "key1");
var (restK, restV, _) = dict.sDictGetFirst(32);
var (restK1, restV1, _) = dict.sDictDeleteLastAndGet(32);
assert (restK.isSliceBitsEqual(restK1)) throw 123;
assert (restV.isSliceBitsEqual(restV1)) throw 123;
return (
old1.loadTwoDigitNumberFromSlice(),
old2.loadTwoDigitNumberFromSlice(),
restV.loadTwoDigitNumberFromSlice(),
restK.loadTwoDigitNumberFromSlice(),
restK.loadTwoDigitNumberFromSlice()
);
}
fun main() {}
/**
@testcase | 101 | 1 1 1 | 1 2 3
@testcase | 101 | 1 2 1 | 1 -1 2
@testcase | 101 | 1 2 3 | 1 -1 -1
@testcase | 102 | | [ [ 4 104 ] [ 3 103 ] [ 2 102 ] [ 1 101 ] ]
@testcase | 103 | | 1 1 2 1 3 (null)
@testcase | 104 | | 12 34 56 78 0
*/

View file

@ -0,0 +1,66 @@
@method_id(101)
fun test1(x: int): int {
if (x > 200) {
return 200;
} else if (x > 100) {
return 100;
} else if (!(x <= 50)) {
if (!(x > 90)) {
return x;
} else {
return 90;
}
} else {
return 0;
}
}
@method_id(102)
fun test2(x: int) {
if (x == 20) { return 20; }
if (x != 50) { return 50; }
if (x == 0) { return 0; }
return -1;
}
@method_id(103)
fun test3(x: int) {
if (!(x != 20)) { return 20; }
if (!(x == 50)) { return 50; }
if (!x) { return 0; }
return -1;
}
fun main() {
}
/**
@testcase | 101 | 0 | 0
@testcase | 101 | 1000 | 200
@testcase | 101 | 150 | 100
@testcase | 101 | -1 | 0
@testcase | 101 | 87 | 87
@testcase | 101 | 94 | 90
@testcase | 102 | 20 | 20
@testcase | 102 | 40 | 50
@testcase | 102 | 50 | -1
@testcase | 103 | 20 | 20
@testcase | 103 | 40 | 50
@testcase | 103 | 50 | -1
@fif_codegen
"""
test3 PROC:<{
// x
DUP // x x
20 NEQINT // x _2
IFNOTJMP:<{ // x
DROP //
20 PUSHINT // _3=20
}> // x
DUP // x x
50 EQINT // x _5
IFNOTJMP:<{ // x
"""
*/

View file

@ -0,0 +1,4 @@
fun demoOfInvalid(): (int) {
var f = someAdd;
return f(1, 2);
}

View file

@ -0,0 +1,3 @@
fun someAdd(a: int, b: int): int {
return a + b + 0;
}

View file

@ -0,0 +1,21 @@
fun prepareDict_3_30_4_40_5_x(valueAt5: int): cell {
var dict: cell = createEmptyDict();
dict.idict_set_builder(32, 3, begin_cell().store_int(30, 32));
dict.idict_set_builder(32, 4, begin_cell().store_int(40, 32));
dict.idict_set_builder(32, 5, begin_cell().store_int(valueAt5, 32));
return dict;
}
fun lookupIdxByValue(idict32: cell, value: int): int {
var cur_key = -1;
do {
var (cur_key redef, cs: slice, found: int) = idict32.idictGetNext(32, cur_key);
// one-line condition (via &) doesn't work, since right side is calculated immediately
if (found) {
if (cs.loadInt(32) == value) {
return cur_key;
}
}
} while (found);
return -1;
}

View file

@ -0,0 +1,23 @@
import "@stdlib/tvm-dicts"
fun prepareDict_3_30_4_40_5_x(valueAt5: int): cell {
var dict: cell = createEmptyDict();
dict.iDictSetBuilder(32, 3, beginCell().storeInt(30, 32));
dict.iDictSetBuilder(32, 4, beginCell().storeInt(40, 32));
dict.iDictSetBuilder(32, 5, beginCell().storeInt(valueAt5, 32));
return dict;
}
fun lookupIdxByValue(idict32: cell, value: int): int {
var cur_key = -1;
do {
var (cur_key redef, cs: slice, found: int) = idict32.iDictGetNext(32, cur_key);
// one-line condition (via &) doesn't work, since right side is calculated immediately
if (found) {
if (cs.loadInt(32) == value) {
return cur_key;
}
}
} while (found);
return -1;
}

View file

@ -0,0 +1,62 @@
@inline
fun foo(x: int): int {
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
x = x * 10 + 1;
return x;
}
fun main(x: int): int {
return foo(x) * 10 + 5;
}
/**
method_id | in | out
@testcase | 0 | 9 | 9111111111111111111111111111111111111111111111111115
*/

View file

@ -0,0 +1,28 @@
fun foo1(x: int): int {
if (x == 1) {
return 1;
}
return 2;
}
@inline
fun foo2(x: int): int {
if (x == 1) {
return 11;
}
return 22;
}
@inline_ref
fun foo3(x: int): int {
if (x == 1) {
return 111;
}
return 222;
}
fun main(x: int): (int, int, int) {
return (foo1(x)+1, foo2(x)+1, foo3(x)+1);
}
/**
method_id | in | out
@testcase | 0 | 1 | 2 12 112
@testcase | 0 | 2 | 3 23 223
*/

View file

@ -0,0 +1,48 @@
global g: int;
@inline
fun foo_repeat() {
g = 1;
repeat(5) {
g *= 2;
}
}
@inline
fun foo_until(): int {
g = 1;
var i: int = 0;
do {
g *= 2;
i += 1;
} while (i < 8);
return i;
}
@inline
fun foo_while(): int {
g = 1;
var i: int = 0;
while (i < 10) {
g *= 2;
i += 1;
}
return i;
}
fun main() {
foo_repeat();
var x: int = g;
foo_until();
var y: int = g;
foo_while();
var z: int = g;
return (x, y, z);
}
/**
method_id | in | out
@testcase | 0 | | 32 256 1024
@code_hash 102749806552989901976653997041637095139193406161777448419603700344770997608788
*/

View file

@ -0,0 +1,9 @@
fun main(flags: int): int {
return flags&0xFF!=0;
}
/**
@compilation_should_fail
@stderr & has lower precedence than !=
@stderr Use parenthesis
*/

View file

@ -0,0 +1,8 @@
fun justTrue(): int { return true; }
const a = justTrue() | 1 < 9;
/**
@compilation_should_fail
@stderr | has lower precedence than <
*/

View file

@ -0,0 +1,8 @@
fun justTrue(): int { return true; }
const a = justTrue() | (1 < 9) | justTrue() != true;
/**
@compilation_should_fail
@stderr | has lower precedence than !=
*/

View file

@ -0,0 +1,6 @@
const a = (1) <=> (0) ^ 8;
/**
@compilation_should_fail
@stderr ^ has lower precedence than <=>
*/

View file

@ -0,0 +1,11 @@
const MAX_SLIPAGE = 100;
fun main(jetton_amount: int, msg_value: int, slippage: int) {
if ((0 == jetton_amount) | (msg_value == 0) | true | false | slippage > MAX_SLIPAGE) {
}
}
/**
@compilation_should_fail
@stderr | has lower precedence than >
*/

View file

@ -0,0 +1,9 @@
fun main() {
if ((1==1)|(2==2)&(3==3)) {
}
}
/**
@compilation_should_fail
@stderr mixing | with & without parenthesis
*/

View file

@ -0,0 +1,8 @@
fun main() {
var c = x && y || x && y;
}
/**
@compilation_should_fail
@stderr mixing && with || without parenthesis
*/

View file

@ -0,0 +1,10 @@
fun moddiv2(x: int, y: int): (int, int) builtin;
/**
@compilation_should_fail
@stderr
"""
`builtin` used for non-builtin function
fun moddiv2
"""
*/

View file

@ -0,0 +1,9 @@
fun main() {
return true();
}
/**
@compilation_should_fail
The message is weird now, but later I'll rework error messages anyway.
@stderr cannot apply expression of type int to an expression of type (): cannot unify type () -> ??3 with int
*/

View file

@ -0,0 +1,14 @@
fun add1(x: int) {
return x + 1;
}
fun main() {
val adder_fn = add1;
var x = 10;
return adder_fn(mutate x);
}
/**
@compilation_should_fail
@stderr `mutate` used for non-mutate argument
*/

View file

@ -0,0 +1,12 @@
fun with2Params(x: int, y: int) {
}
fun main() {
return with2Params(1);
}
/**
@compilation_should_fail
@stderr too few arguments in call to `with2Params`, expected 2, have 1
*/

View file

@ -0,0 +1,13 @@
fun methodWith1Param(self: int, param: int) {
}
fun main() {
val x = 10;
x.methodWith1Param(2, "asdf");
}
/**
@compilation_should_fail
@stderr too many arguments in call to `methodWith1Param`, expected 1, have 2
*/

View file

@ -0,0 +1,13 @@
fun inc(x: int) {
return x + 1;
}
fun main() {
return inc(_);
}
/**
@compilation_should_fail
@stderr rvalue expected
@stderr inc(_)
*/

View file

@ -0,0 +1,12 @@
fun nothing() {
}
fun main() {
val x = 0;
return x.nothing();
}
/**
@compilation_should_fail
@stderr `nothing` has no parameters and can not be called as method
*/

View file

@ -0,0 +1,14 @@
fun main() {
beginCell()
.storeAddressNone()
.storeUint(3, 32)
.storeUnexisting()
.storeInt(1, 32)
.endCell();
}
/**
@compilation_should_fail
@stderr undefined symbol `storeUnexisting`
@stderr .storeUnexisting()
*/

View file

@ -0,0 +1,8 @@
fun main() {
var incoming_ton: int = get_incoming_value().3();
}
/**
@compilation_should_fail
@stderr expected method name, got `3`
*/

View file

@ -0,0 +1,12 @@
fun main() {
try {
} catch(int, arg) {}
return 0;
}
/**
@compilation_should_fail
@stderr expected identifier, got `int`
@stderr catch(int
*/

View file

@ -0,0 +1,9 @@
fun main() {
try {}
catch(err, arg, more) {}
}
/**
@compilation_should_fail
@stderr expected `)`, got `,`
*/

View file

@ -0,0 +1,11 @@
/*
in tolk we decided to drop nested comments support
/*
not nested
*/
*/
/**
@compilation_should_fail
@stderr error: expected fun or get, got `*`
*/

View file

@ -0,0 +1,8 @@
fun main(): int {
;; here is not a comment
}
/**
@compilation_should_fail
@stderr error: expected `;`, got `is`
*/

View file

@ -0,0 +1,8 @@
const ONE = TWO - 1;
const TWO = ONE + 1;
/**
@compilation_should_fail
@stderr const ONE
@stderr undefined symbol `TWO`
*/

View file

@ -0,0 +1,6 @@
const a = 10, b = 20;
/**
@compilation_should_fail
@stderr multiple declarations are not allowed
*/

View file

@ -0,0 +1,8 @@
get fun onInternalMessage() {
return 0;
}
/**
@compilation_should_fail
@stderr invalid declaration of a reserved function
*/

View file

@ -0,0 +1,8 @@
fun main(int): int {
}
/**
@compilation_should_fail
@stderr expected parameter name, got `int`
*/

View file

@ -0,0 +1,8 @@
int main() {
}
/**
@compilation_should_fail
@stderr expected fun or get, got `int`
*/

View file

@ -0,0 +1,8 @@
fun main() {
int x = 0;
}
/**
@compilation_should_fail
@stderr probably, you use FunC-like declarations; valid syntax is `var x: int = ...`
*/

View file

@ -0,0 +1,6 @@
enum MyKind { }
/**
@compilation_should_fail
@stderr `enum` is not supported yet
*/

View file

@ -0,0 +1,8 @@
get seqno(self: int) {
return 0;
}
/**
@compilation_should_fail
@stderr get methods can't have `mutate` and `self` params
*/

View file

@ -0,0 +1,8 @@
fun main() {
var a = 10, b = 20;
}
/**
@compilation_should_fail
@stderr multiple declarations are not allowed
*/

View file

@ -0,0 +1,8 @@
fun someDemo() {
return 0;
}
/**
@compilation_should_fail
@stderr the contract has no entrypoint
*/

View file

@ -0,0 +1,9 @@
fun recv_internal() {
return 0;
}
/**
@compilation_should_fail
@stderr this is a reserved FunC/Fift identifier
@stderr you need `onInternalMessage`
*/

View file

@ -0,0 +1,9 @@
@method_id(123)
get fun hello(x: int, y: int): (int, int) {
return (x, y);
}
/**
@compilation_should_fail
@stderr @method_id can be specified only for regular functions
*/

View file

@ -0,0 +1,17 @@
@pure
get fun secret(): int {
return 0;
}
@pure
get fun balanced(): int {
return 1;
}
fun main(): int {
return secret() + balanced();
}
/**
@compilation_should_fail
@stderr GET methods hash collision: `secret` and `balanced` produce the same hash
*/

View file

@ -0,0 +1,11 @@
// line1
/* */ import "unexisting.tolk";
// line3
/**
@compilation_should_fail
On Linux/Mac, `realpath()` returns an error, and the error message is `cannot find file`
On Windows, it fails after, on reading, with a message "cannot open file"
@stderr invalid-import.tolk:2:7: error: Failed to import: cannot
@stderr import "unexisting.tolk";
*/

View file

@ -0,0 +1,11 @@
fun f(x: int) {}
fun cantAssignToVal() {
val x = 10;
f(x += 1);
}
/**
@compilation_should_fail
@stderr modifying an immutable variable `x`
*/

View file

@ -0,0 +1,16 @@
fun increment(mutate x: int) {
x = x + 1;
}
fun cantCallMutatingAsAMember() {
var x = 0;
x.increment();
return x;
}
/**
@compilation_should_fail
@stderr function `increment` mutates parameter `x`
@stderr consider calling `increment(mutate x)`, not `x.increment`()
@stderr alternatively, rename parameter to `self` to make it a method
*/

View file

@ -0,0 +1,8 @@
fun load32(self: slice): int {
return self.loadUint(32);
}
/**
@compilation_should_fail
@stderr modifying `self` (call a mutating method), which is immutable by default
*/

Some files were not shown because too many files have changed in this diff Show more