/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see .
Copyright 2017-2019 Telegram Systems LLP
*/
#include "vm/continuation.h"
#include "vm/cp0.h"
#include "vm/dict.h"
#include "fift/utils.h"
#include "common/bigint.hpp"
#include "td/utils/tests.h"
#include "td/utils/ScopeGuard.h"
#include "td/utils/StringBuilder.h"
std::string run_vm(td::Ref cell) {
vm::init_op_cp0();
vm::DictionaryBase::get_empty_dictionary();
class Logger : public td::LogInterface {
public:
void append(td::CSlice slice) override {
res.append(slice.data(), slice.size());
}
std::string res;
};
static Logger logger;
logger.res = "";
td::set_log_fatal_error_callback([](td::CSlice message) { td::default_log_interface->append(logger.res); });
vm::VmLog log{&logger, td::LogOptions::plain()};
log.log_options.level = 4;
log.log_options.fix_newlines = true;
log.log_mask |= vm::VmLog::DumpStack;
auto total_data_cells_before = vm::DataCell::get_total_data_cells();
SCOPE_EXIT {
auto total_data_cells_after = vm::DataCell::get_total_data_cells();
ASSERT_EQ(total_data_cells_before, total_data_cells_after);
};
vm::Stack stack;
try {
vm::run_vm_code(vm::load_cell_slice_ref(cell), stack, 0 /*flags*/, nullptr /*data*/, std::move(log) /*VmLog*/);
} catch (...) {
LOG(FATAL) << "catch unhandled exception";
}
return logger.res; // must be a copy
}
td::Ref to_cell(const unsigned char *buff, int bits) {
return vm::CellBuilder().store_bits(buff, bits, 0).finalize();
}
void test_run_vm(td::Ref code) {
auto a = run_vm(code);
auto b = run_vm(code);
ASSERT_EQ(a, b);
REGRESSION_VERIFY(a);
}
void test_run_vm(td::Slice code_hex) {
unsigned char buff[128];
int bits = (int)td::bitstring::parse_bitstring_hex_literal(buff, sizeof(buff), code_hex.begin(), code_hex.end());
CHECK(bits >= 0);
test_run_vm(to_cell(buff, bits));
}
TEST(VM, simple) {
test_run_vm("ABCBABABABA");
}
TEST(VM, memory_leak_old) {
test_run_vm("90787FDB3B");
}
TEST(VM, memory_leak) {
test_run_vm("90707FDB3B");
}
TEST(VM, bug_div_short_any) {
test_run_vm("6883FF73A98D");
}
TEST(VM, assert_pfx_dict_lookup) {
test_run_vm("778B04216D73F43E018B04591277F473");
}
TEST(VM, assert_lookup_prefix) {
test_run_vm("78E58B008B028B04010000016D90ED5272F43A755D77F4A8");
}
TEST(VM, assert_code_not_null) {
test_run_vm("76ED40DE");
}
TEST(VM, bug_exec_dict_getnear) {
test_run_vm("8B048B00006D72F47573655F6D656D6D656D8B007F");
}
TEST(VM, bug_stack_overflow) {
test_run_vm("72A93AF8");
}
TEST(VM, assert_extract_minmax_key) {
test_run_vm("6D6DEB21807AF49C2180EB21807AF41C");
}
TEST(VM, memory_leak_new) {
test_run_vm("72E5ED40DB3603");
}
TEST(VM, unhandled_exception_1) {
test_run_vm("70EDA2ED00");
}
TEST(VM, unhandled_exception_2) {
// infinite loop now
// test_run_vm("EBEDB4");
}
TEST(VM, unhandled_exception_3) {
// infinite loop now
// test_run_vm("EBEDC0");
}
TEST(VM, unhandled_exception_4) {
test_run_vm("7F853EA1C8CB3E");
}
TEST(VM, unhandled_exception_5) {
test_run_vm("738B04016D21F41476A721F49F");
}
TEST(VM, bigint) {
td::StringBuilder sb({}, true);
using word_t = td::BigIntInfo::word_t;
std::vector numbers{1,
-1,
2,
-2,
100,
-100,
std::numeric_limits::max(),
std::numeric_limits::min(),
std::numeric_limits::max() - 1,
std::numeric_limits::min() + 1};
for (auto x : numbers) {
for (auto y : numbers) {
word_t a;
word_t b;
td::BigIntInfo::set_mul(&a, &b, x, y);
sb << "set_mul " << x << " * " << y << " = " << a << " " << b << "\n";
td::BigIntInfo::add_mul(&a, &b, x, y);
sb << "add_mul " << x << " " << y << " = " << a << " " << b << "\n";
td::BigIntInfo::sub_mul(&a, &b, x, y);
sb << "sub_mul " << x << " " << y << " = " << a << " " << b << "\n";
}
}
auto base = td::BigIntInfo::Base;
std::vector lo_numbers{1, -1, 2, -2, 100, -100, base - 1, base - 2, -base + 1, -base + 2};
for (auto x : numbers) {
for (auto y : lo_numbers) {
for (auto z : numbers) {
word_t a;
word_t b;
td::BigIntInfo::dbl_divmod(&a, &b, x, y, z);
sb << "dbl_divmod " << x << " " << y << " / " << z << " = " << a << " " << b << "\n";
}
}
}
REGRESSION_VERIFY(sb.as_cslice());
}
TEST(VM, report3_1) {
//WA: expect (1, 2, 6, 3)
td::Slice test1 =
R"A(
CONT:<{
DEPTH
}>
3 SETNUMARGS
c0 POPCTR
1 INT
2 INT
3 INT
4 INT
5 INT
6 INT
4 RETURNARGS
)A";
test_run_vm(fift::compile_asm(test1).move_as_ok());
}
TEST(VM, report3_2) {
td::Slice test1 =
R"A(
CONT:<{
DEPTH
}>
2 SETNUMARGS
c0 POPCTR
1 INT
2 INT
3 INT
4 INT
2 RETARGS
)A";
test_run_vm(fift::compile_asm(test1).move_as_ok());
}
TEST(VM, report3_3) {
// WA: expect (9)
td::Slice test1 =
R"A(
CONT:<{
8 INT
}>
c0 POPCTR
CONT:<{
9 INT
}>
c1 POPCTR
0 INT
BRANCH
)A";
test_run_vm(fift::compile_asm(test1).move_as_ok());
}
TEST(VM, report3_4) {
td::Slice test1 =
R"A(
CONT:<{
1 INT
2 INT
3 INT
2 RETARGS
}>
CALLX
ADD
)A";
test_run_vm(fift::compile_asm(test1).move_as_ok());
}
TEST(VM, report3_6) {
// WA: expect StackOverflow
td::Slice test1 =
R"A(
10 INT
20 INT
30 INT
CONT:<{
DEPTH
40 INT
SWAP
}>
2 SETNUMARGS
3 1 CALLXARGS
)A";
test_run_vm(fift::compile_asm(test1).move_as_ok());
}
//TEST(VM, report3_ce) {
//td::Slice test1 =
//R"A(
//s16 POP
//s16 PUSH
//s0 s16 XCHG
//)A";
//test_run_vm(fift::compile_asm(test1).move_as_ok());
//}
TEST(VM, report3_int_overflow_1) {
td::Slice test1 =
R"A(
4 INT
16 INT
-115792089237316195423570985008687907853269984665640564039457584007913129639936 INT
MULDIVMOD
)A";
test_run_vm(fift::compile_asm(test1).move_as_ok());
}
TEST(VM, report3_int_overflow_2) {
td::Slice test1 =
R"A(
4 INT
16 INT
-115792089237316195423570985008687907853269984665640564039457584007913129639936 INT
MULDIVR
)A";
test_run_vm(fift::compile_asm(test1).move_as_ok());
}
TEST(VM, report3_qnot) {
td::Slice test1 =
R"A(
PUSHNAN
QNOT
)A";
test_run_vm(fift::compile_asm(test1).move_as_ok());
}
TEST(VM, report3_loop_1) {
//WA
td::Slice test1 =
R"A(
CONT:<{
2 INT
}>
ATEXITALT
CONT:<{
1 INT
RETALT
-1 INT
}>
AGAIN
3 INT
)A";
test_run_vm(fift::compile_asm(test1).move_as_ok());
}
TEST(VM, report3_loop_2) {
//WA
td::Slice test1 =
R"A(
CONT:<{
2 INT
}>
ATEXITALT
CONT:<{
1 INT
RETALT
-1 INT
}>
UNTIL
3 INT
)A";
test_run_vm(fift::compile_asm(test1).move_as_ok());
}
TEST(VM, report3_loop_3) {
//WA
td::Slice test1 =
R"A(
1 INT
CONT:<{
UNTILEND
RET
-1 INT
}>
CALLX
)A";
test_run_vm(fift::compile_asm(test1).move_as_ok());
}
TEST(VM, report3_loop_4) {
//WA
td::Slice test1 =
R"A(
CONT:<{
2 INT
}>
ATEXITALT
CONT:<{
1 INT
RETALT
-1 PUSHINT
}>
CONT:<{
-1 INT
}>
WHILE
3 INT
)A";
test_run_vm(fift::compile_asm(test1).move_as_ok());
}
TEST(VM, report3_loop_5) {
//WA
td::Slice test1 =
R"A(
CONT:<{
1 INT
2 INT
}>
ATEXITALT
3 INT
AGAINEND
DEC
DUP
IFRET
DROP
RETALT
)A";
test_run_vm(fift::compile_asm(test1).move_as_ok());
}
TEST(VM, report3_loop_6) {
//WA
td::Slice test1 =
R"A(
CONT:<{
1 INT
2 INT
}>
3 INT
AGAINEND
DEC
DUP
IFRET
DROP
ATEXITALT
RETALT
)A";
test_run_vm(fift::compile_asm(test1).move_as_ok());
}