1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

Merge pull request #480 from ton-blockchain/testnet

Merge dev branch
This commit is contained in:
EmelyanenkoK 2022-10-03 09:25:27 +03:00 committed by GitHub
commit 4b940f8bad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
106 changed files with 3403 additions and 478 deletions

View file

@ -179,6 +179,7 @@ set(FUNC_LIB_SOURCE
func/stack-transform.cpp
func/optimize.cpp
func/codegen.cpp
func/func.cpp
)
set(TLB_BLOCK_AUTO
@ -266,6 +267,8 @@ set(BIGINT_TEST_SOURCE
PARENT_SCOPE
)
set(USE_EMSCRIPTEN ${USE_EMSCRIPTEN} PARENT_SCOPE)
add_library(ton_crypto STATIC ${TON_CRYPTO_SOURCE})
target_include_directories(ton_crypto PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
@ -305,13 +308,30 @@ target_include_directories(ton_block PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SO
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/block> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
target_link_libraries(ton_block PUBLIC ton_crypto tdutils tdactor tl_api)
add_executable(func func/func.cpp ${FUNC_LIB_SOURCE})
add_executable(func func/func-main.cpp ${FUNC_LIB_SOURCE})
target_include_directories(func PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
target_link_libraries(func PUBLIC ton_crypto src_parser git ton_block)
if (WINGETOPT_FOUND)
target_link_libraries_system(func wingetopt)
endif()
if (USE_EMSCRIPTEN)
add_executable(funcfiftlib funcfiftlib/funcfiftlib.cpp ${FUNC_LIB_SOURCE})
target_include_directories(funcfiftlib PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
target_link_libraries(funcfiftlib PUBLIC fift-lib src_parser git)
target_link_options(funcfiftlib PRIVATE -sEXPORTED_RUNTIME_METHODS=FS,ccall,cwrap,_malloc,free,UTF8ToString,stringToUTF8)
target_link_options(funcfiftlib PRIVATE -sEXPORTED_FUNCTIONS=_func_compile,_version)
target_link_options(funcfiftlib PRIVATE -sEXPORT_NAME=CompilerModule)
target_link_options(funcfiftlib PRIVATE -sERROR_ON_UNDEFINED_SYMBOLS=0)
target_link_options(funcfiftlib PRIVATE -sFILESYSTEM=1)
target_link_options(funcfiftlib PRIVATE -Oz)
target_link_options(funcfiftlib PRIVATE -sIGNORE_MISSING_MAIN=1)
target_link_options(funcfiftlib PRIVATE -sAUTO_NATIVE_LIBRARIES=0)
target_link_options(funcfiftlib PRIVATE -sMODULARIZE=1)
target_link_options(funcfiftlib PRIVATE --embed-file ${CMAKE_CURRENT_SOURCE_DIR}/fift/lib@/fiftlib)
target_compile_options(funcfiftlib PRIVATE -sDISABLE_EXCEPTION_CATCHING=0)
endif()
add_executable(tlbc tl/tlbc.cpp)
target_include_directories(tlbc PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
target_link_libraries(tlbc PUBLIC ton_crypto src_parser)
@ -337,16 +357,26 @@ if (TON_USE_ASAN AND NOT WIN32)
endif()
file(MAKE_DIRECTORY smartcont/auto)
if (NOT CMAKE_CROSSCOMPILING)
if (NOT CMAKE_CROSSCOMPILING OR USE_EMSCRIPTEN)
set(GENERATE_TLB_CMD tlbc)
add_custom_command(
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/block
COMMAND ${TURN_OFF_LSAN}
COMMAND ${GENERATE_TLB_CMD} -o block-auto -n block::gen -z block.tlb
COMMENT "Generate block tlb source files"
OUTPUT ${TLB_BLOCK_AUTO}
DEPENDS tlbc block/block.tlb
)
if (NOT USE_EMSCRIPTEN)
add_custom_command(
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/block
COMMAND ${TURN_OFF_LSAN}
COMMAND ${GENERATE_TLB_CMD} -o block-auto -n block::gen -z block.tlb
COMMENT "Generate block tlb source files"
OUTPUT ${TLB_BLOCK_AUTO}
DEPENDS tlbc block/block.tlb
)
else()
add_custom_command(
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/block
COMMAND ${TURN_OFF_LSAN}
COMMENT "Generate block tlb source files"
OUTPUT ${TLB_BLOCK_AUTO}
DEPENDS tlbc block/block.tlb
)
endif()
add_custom_target(tlb_generate_block DEPENDS ${TLB_BLOCK_AUTO})
add_dependencies(ton_block tlb_generate_block)

View file

@ -982,7 +982,7 @@ struct ShardIdent::Record {
int shard_pfx_bits;
int workchain_id;
unsigned long long shard_prefix;
Record() : shard_pfx_bits(-1) {
Record() : shard_pfx_bits(-1), workchain_id(ton::workchainInvalid), shard_prefix(0) {
}
Record(int _pfxlen, int _wcid, unsigned long long _pfx)
: shard_pfx_bits(_pfxlen), workchain_id(_wcid), shard_prefix(_pfx) {

View file

@ -451,7 +451,7 @@ block_extra in_msg_descr:^InMsgDescr
created_by:bits256
custom:(Maybe ^McBlockExtra) = BlockExtra;
//
value_flow ^[ from_prev_blk:CurrencyCollection
value_flow#b8e48dfb ^[ from_prev_blk:CurrencyCollection
to_next_blk:CurrencyCollection
imported:CurrencyCollection
exported:CurrencyCollection ]

View file

@ -327,8 +327,7 @@ bool Account::unpack(Ref<vm::CellSlice> shard_account, Ref<vm::CellSlice> extra,
block::gen::t_ShardAccount.print(std::cerr, *shard_account);
}
block::gen::ShardAccount::Record acc_info;
if (!(block::gen::t_ShardAccount.validate_csr(shard_account) &&
block::tlb::t_ShardAccount.validate_csr(shard_account) && tlb::unpack_exact(shard_account.write(), acc_info))) {
if (!(block::tlb::t_ShardAccount.validate_csr(shard_account) && tlb::unpack_exact(shard_account.write(), acc_info))) {
LOG(ERROR) << "account " << addr.to_hex() << " state is invalid";
return false;
}
@ -2013,7 +2012,6 @@ bool Transaction::compute_state() {
std::cerr << "new account state: ";
block::gen::t_Account.print_ref(std::cerr, new_total_state);
}
CHECK(block::gen::t_Account.validate_ref(new_total_state));
CHECK(block::tlb::t_Account.validate_ref(new_total_state));
return true;
}

View file

@ -176,6 +176,7 @@ class AnyIntView {
public:
enum { word_bits = Tr::word_bits, word_shift = Tr::word_shift };
typedef typename Tr::word_t word_t;
typedef typename Tr::uword_t uword_t;
int& n_;
PropagateConstSpan<word_t> digits;
@ -320,7 +321,7 @@ class BigIntG {
digits[0] = x;
normalize_bool();
} else {
digits[0] = ((x + Tr::Half) & (Tr::Base - 1)) - Tr::Half;
digits[0] = ((x ^ Tr::Half) & (Tr::Base - 1)) - Tr::Half;
digits[n++] = (x >> Tr::word_shift) + (digits[0] < 0);
}
}
@ -675,7 +676,7 @@ class BigIntG {
return n > 0 && !(digits[0] & 1);
}
word_t mod_pow2_short(int pow) const {
return n > 0 ? digits[0] & ((1 << pow) - 1) : 0;
return n > 0 ? digits[0] & ((1ULL << pow) - 1) : 0;
}
private:
@ -764,7 +765,7 @@ bool AnyIntView<Tr>::add_pow2_any(int exponent, int factor) {
while (size() <= k) {
digits[inc_size()] = 0;
}
digits[k] += ((word_t)factor << dm.rem);
digits[k] += factor * ((word_t)1 << dm.rem);
return true;
}
@ -969,7 +970,7 @@ bool AnyIntView<Tr>::add_mul_any(const AnyIntView<Tr>& yp, const AnyIntView<Tr>&
if (hi && hi != -1) {
return invalidate_bool();
}
digits[size() - 1] += (hi << word_shift);
digits[size() - 1] += ((uword_t)hi << word_shift);
}
return true;
}
@ -1014,7 +1015,7 @@ int AnyIntView<Tr>::sgn_un_any() const {
}
int i = size() - 2;
do {
v <<= word_shift;
v *= Tr::Base;
word_t w = digits[i];
if (w >= -v + Tr::MaxDenorm) {
return 1;
@ -1059,7 +1060,7 @@ typename Tr::word_t AnyIntView<Tr>::to_long_any() const {
} else if (size() == 1) {
return digits[0];
} else {
word_t v = digits[0] + (digits[1] << word_shift); // approximation mod 2^64
word_t v = (uword_t)digits[0] + ((uword_t)digits[1] << word_shift); // approximation mod 2^64
word_t w = (v & (Tr::Base - 1)) - digits[0];
w >>= word_shift;
w += (v >> word_shift); // excess of approximation divided by Tr::Base
@ -1120,7 +1121,7 @@ int AnyIntView<Tr>::cmp_un_any(const AnyIntView<Tr>& yp) const {
return -1;
}
while (xn > yn) {
v <<= word_shift;
v *= Tr::Base;
word_t w = T::eval(digits[--xn]);
if (w >= -v + Tr::MaxDenorm) {
return 1;
@ -1137,7 +1138,7 @@ int AnyIntView<Tr>::cmp_un_any(const AnyIntView<Tr>& yp) const {
return -1;
}
while (yn > xn) {
v <<= word_shift;
v *= Tr::Base;
word_t w = yp.digits[--yn];
if (w <= v - Tr::MaxDenorm) {
return 1;
@ -1150,7 +1151,7 @@ int AnyIntView<Tr>::cmp_un_any(const AnyIntView<Tr>& yp) const {
v = 0;
}
while (--xn >= 0) {
v <<= word_shift;
v *= Tr::Base;
word_t w = T::eval(digits[xn]) - yp.digits[xn];
if (w >= -v + Tr::MaxDenorm) {
return 1;
@ -1197,7 +1198,7 @@ int AnyIntView<Tr>::divmod_tiny_any(int y) {
}
int rem = 0;
for (int i = size() - 1; i >= 0; i--) {
auto divmod = std::div(digits[i] + ((word_t)rem << word_shift), (word_t)y);
auto divmod = std::div(digits[i] + ((uword_t)rem << word_shift), (word_t)y);
digits[i] = divmod.quot;
rem = (int)divmod.rem;
if ((rem ^ y) < 0 && rem) {
@ -1267,7 +1268,7 @@ bool AnyIntView<Tr>::mul_add_short_any(word_t y, word_t z) {
z += (digits[size() - 1] >> word_shift);
digits[size() - 1] &= Tr::Base - 1;
if (!z || z == -1) {
digits[size() - 1] += (z << word_shift);
digits[size() - 1] += ((uword_t)z << word_shift);
return true;
} else {
return false;
@ -1338,7 +1339,7 @@ bool AnyIntView<Tr>::mod_div_any(const AnyIntView<Tr>& yp, AnyIntView<Tr>& quot,
while (--i >= 0) {
Tr::sub_mul(&digits[k + i + 1], &digits[k + i], q, yp.digits[i]);
}
digits[size() - 1] += (hi << word_shift);
digits[size() - 1] += ((uword_t)hi << word_shift);
}
} else {
quot.set_size(1);
@ -1351,7 +1352,7 @@ bool AnyIntView<Tr>::mod_div_any(const AnyIntView<Tr>& yp, AnyIntView<Tr>& quot,
Tr::sub_mul(&digits[k + i + 1], &digits[k + i], q, yp.digits[i]);
}
dec_size();
digits[size() - 1] += (digits[size()] << word_shift);
digits[size() - 1] += ((uword_t)digits[size()] << word_shift);
}
if (size() >= yp.size() - 1) {
assert(size() <= yp.size());
@ -1455,7 +1456,7 @@ bool AnyIntView<Tr>::mod_pow2_any(int exponent) {
dec_size();
q += word_shift;
}
word_t pow = ((word_t)1 << q);
uword_t pow = ((uword_t)1 << q);
word_t v = digits[size() - 1] & (pow - 1);
if (!v) {
int k = size() - 1;
@ -1485,7 +1486,7 @@ bool AnyIntView<Tr>::mod_pow2_any(int exponent) {
return true;
} else if (v >= Tr::Half && size() < max_size()) {
word_t w = (((v >> (word_shift - 1)) + 1) >> 1);
digits[size() - 1] = v - (w << word_shift);
digits[size() - 1] = (uword_t)v - ((uword_t)w << word_shift);
digits[inc_size()] = w;
return true;
} else {
@ -1623,7 +1624,7 @@ bool AnyIntView<Tr>::lshift_any(int exponent) {
} else if (v != -1) {
return invalidate_bool();
} else {
digits[size() - 1] += (v << word_shift);
digits[size() - 1] += ((uword_t)v << word_shift);
}
}
if (q) {
@ -1750,7 +1751,7 @@ int AnyIntView<Tr>::bit_size_any(bool sgnd) const {
int k = size() - 1;
word_t q = digits[k];
if (k > 0 && q < Tr::MaxDenorm / 2) {
q <<= word_shift;
q *= Tr::Base;
q += digits[--k];
}
if (!k) {
@ -1766,7 +1767,7 @@ int AnyIntView<Tr>::bit_size_any(bool sgnd) const {
} else if (q <= -Tr::MaxDenorm / 2) {
return s;
}
q <<= word_shift;
q *= Tr::Base;
q += digits[--k];
}
return q >= 0 ? s + 1 : s;
@ -1774,7 +1775,7 @@ int AnyIntView<Tr>::bit_size_any(bool sgnd) const {
int k = size() - 1;
word_t q = digits[k];
if (k > 0 && q > -Tr::MaxDenorm / 2) {
q <<= word_shift;
q *= Tr::Base;
q += digits[--k];
}
if (!k) {
@ -1790,7 +1791,7 @@ int AnyIntView<Tr>::bit_size_any(bool sgnd) const {
} else if (q <= -Tr::MaxDenorm / 2) {
return s + 1;
}
q <<= word_shift;
q *= Tr::Base;
q += digits[--k];
}
return q >= 0 ? s : s + 1;
@ -1817,7 +1818,7 @@ bool AnyIntView<Tr>::export_bytes_any(unsigned char* buff, std::size_t buff_size
for (int i = 0; i < size(); i++) {
if ((word_shift & 7) && word_shift + 8 >= word_bits && k >= word_bits - word_shift - 1) {
int k1 = 8 - k;
v += (digits[i] << k) & 0xff;
v += ((uword_t)digits[i] << k) & 0xff;
if (ptr > buff) {
*--ptr = (unsigned char)(v & 0xff);
} else if ((unsigned char)(v & 0xff) != s) {
@ -1827,7 +1828,7 @@ bool AnyIntView<Tr>::export_bytes_any(unsigned char* buff, std::size_t buff_size
v += (digits[i] >> k1);
k += word_shift - 8;
} else {
v += (digits[i] << k);
v += ((uword_t)digits[i] << k);
k += word_shift;
}
while (k >= 8) {
@ -1868,7 +1869,7 @@ bool AnyIntView<Tr>::export_bytes_lsb_any(unsigned char* buff, std::size_t buff_
for (int i = 0; i < size(); i++) {
if ((word_shift & 7) && word_shift + 8 >= word_bits && k >= word_bits - word_shift - 1) {
int k1 = 8 - k;
v += (digits[i] << k) & 0xff;
v += ((uword_t)digits[i] << k) & 0xff;
if (buff < end) {
*buff++ = (unsigned char)(v & 0xff);
} else if ((unsigned char)(v & 0xff) != s) {
@ -1878,7 +1879,7 @@ bool AnyIntView<Tr>::export_bytes_lsb_any(unsigned char* buff, std::size_t buff_
v += (digits[i] >> k1);
k += word_shift - 8;
} else {
v += (digits[i] << k);
v += ((uword_t)digits[i] << k);
k += word_shift;
}
while (k >= 8) {
@ -1922,7 +1923,7 @@ bool AnyIntView<Tr>::export_bits_any(unsigned char* buff, int offs, unsigned bit
return false;
}
}
td::bitstring::bits_store_long_top(buff, offs, v << (64 - bits), bits);
td::bitstring::bits_store_long_top(buff, offs, (unsigned long long)v << (64 - bits), bits);
} else {
if (!sgnd && v < 0) {
return false;
@ -1945,7 +1946,7 @@ bool AnyIntView<Tr>::export_bits_any(unsigned char* buff, int offs, unsigned bit
for (int i = 0; i < size(); i++) {
if (word_shift + 8 >= word_bits && k >= word_bits - word_shift - 1) {
int k1 = 8 - k;
v += (digits[i] << k) & 0xff;
v += ((uword_t)digits[i] << k) & 0xff;
if (ptr > buff) {
if (--ptr > buff) {
*ptr = (unsigned char)(v & 0xff);
@ -1963,7 +1964,7 @@ bool AnyIntView<Tr>::export_bits_any(unsigned char* buff, int offs, unsigned bit
v += (digits[i] >> k1);
k += word_shift - 8;
} else {
v += (digits[i] << k);
v += ((uword_t)digits[i] << k);
k += word_shift;
}
while (k >= 8) {
@ -2028,7 +2029,7 @@ bool AnyIntView<Tr>::import_bytes_any(const unsigned char* buff, std::size_t buf
return invalidate_bool();
}
}
v |= (((word_t) * --ptr) << k);
v |= (((uword_t) * --ptr) << k);
k += 8;
}
if (s) {
@ -2043,7 +2044,9 @@ bool AnyIntView<Tr>::import_bits_any(const unsigned char* buff, int offs, unsign
if (bits < word_shift) {
set_size(1);
unsigned long long val = td::bitstring::bits_load_long_top(buff, offs, bits);
if (sgnd) {
if (bits == 0) {
digits[0] = 0;
} else if (sgnd) {
digits[0] = ((long long)val >> (64 - bits));
} else {
digits[0] = (val >> (64 - bits));

View file

@ -191,7 +191,7 @@ void bits_memcpy(unsigned char* to, int to_offs, const unsigned char* from, int
*to++ = (unsigned char)(acc >> b);
}
if (b > 0) {
*to = (unsigned char)((*to & (0xff >> b)) | ((int)acc << (8 - b)));
*to = (unsigned char)((*to & (0xff >> b)) | ((unsigned)acc << (8 - b)));
}
}
}
@ -301,7 +301,7 @@ std::size_t bits_memscan(const unsigned char* ptr, int offs, std::size_t bit_cou
ptr++;
}
while (rem >= 8 && !td::is_aligned_pointer<8>(ptr)) {
v = ((*ptr++ ^ xor_val) << 24);
v = ((unsigned)(*ptr++ ^ xor_val) << 24);
// std::cerr << "[B] rem=" << rem << " ptr=" << (const void*)(ptr - 1) << " v=" << std::hex << v << std::dec << std::endl;
if (v) {
return bit_count - rem + td::count_leading_zeroes_non_zero32(v);
@ -319,7 +319,7 @@ std::size_t bits_memscan(const unsigned char* ptr, int offs, std::size_t bit_cou
rem -= 64;
}
while (rem >= 8) {
v = ((*ptr++ ^ xor_val) << 24);
v = ((unsigned)(*ptr++ ^ xor_val) << 24);
// std::cerr << "[D] rem=" << rem << " ptr=" << (const void*)(ptr - 1) << " v=" << std::hex << v << std::dec << std::endl;
if (v) {
return bit_count - rem + td::count_leading_zeroes_non_zero32(v);
@ -327,7 +327,7 @@ std::size_t bits_memscan(const unsigned char* ptr, int offs, std::size_t bit_cou
rem -= 8;
}
if (rem > 0) {
v = ((*ptr ^ xor_val) << 24);
v = ((unsigned)(*ptr ^ xor_val) << 24);
// std::cerr << "[E] rem=" << rem << " ptr=" << (const void*)ptr << " v=" << std::hex << v << std::dec << std::endl;
c = td::count_leading_zeroes32(v);
return c < rem ? bit_count - rem + c : bit_count;
@ -505,7 +505,7 @@ unsigned long long bits_load_long_top(ConstBitPtr from, unsigned top_bits) {
}
unsigned long long bits_load_ulong(ConstBitPtr from, unsigned bits) {
return bits_load_long_top(from, bits) >> (64 - bits);
return bits == 0 ? 0 : bits_load_long_top(from, bits) >> (64 - bits);
}
long long bits_load_long(ConstBitPtr from, unsigned bits) {

View file

@ -268,3 +268,114 @@ def? config-valid? {
config-valid?
} cond } cond } cond
} : is-valid-config?
// Get anycast depth / rewrite_pfx or return 0
// ( S -- x y S )
{
// maybe
1 u@+ swap 0 >
{
// anycast_info$_ depth:(#<= 30) { depth >= 1 }
// rewrite_pfx:(bits depth) = Anycast;
30 u@+ swap // get depth
dup 1 > {
dup 2 roll swap u@+ // get rewrite_pfx
// return depth, rewrite_pfx, slice
}
{
drop // drop depth (<=1)
0 0 2 roll // set anycast to none
} cond
}
{
0 0 2 roll // set anycast to none
} cond
} : maybe-anycast
// Rewrite first bits of addr with anycast info
{ // input: anycast depth, rewrite_pfx, workchain, slice, address length
4 -roll
3 roll dup dup 0 = { 2drop 2 roll drop }
{
rot swap u@+ swap drop
3 roll
<b swap 3 roll u, b> <s swap |+
} cond // rewrite first bits of address with rewrite_pfx
2 roll
u@+ // get address
} : parse-address-with-anycast
// Parse Slice S and return:
// 0 `addr_none S - if addr_none$00 is parsed
// addr `addr_extern S - if addr_extern$01 is parsed
// wc addr `addr_std S - if addr_std$10 is parsed
// wc addr `addr_var S - if addr_var$11 is parsed
// ( S -- 0 A S or addr A S or wc addr A S )
{ 2 u@+ swap dup 0> // Get addr: addr_none$00 / addr_extern$01 / addr_std$10 / addr_var$11
{ // if greater that zero
dup 1 >
{
2 =
{
// if addr_std$10
// anycast:(Maybe Anycast)
// workchain_id:int8
// address:bits256 = MsgAddressInt;
maybe-anycast // get anycast depth, bits, slice
8 i@+ // get workchain
256 parse-address-with-anycast
`addr-std swap
}
{
// if addr_var$11
// anycast:(Maybe Anycast)
// addr_len:(## 9)
// workchain_id:int32
// address:(bits addr_len) = MsgAddressInt;
maybe-anycast // get anycast depth, bits, slice
9 u@+ // get addr_len
32 i@+ // get workchain
swap 2 -roll // move workchain to neede position
swap parse-address-with-anycast
`addr-var swap
} cond
}
{
drop // drop header (dup for statment upper)
// if addr_extern$01
// addr_extern$01 len:(## 9)
// external_address:(bits len)
9 u@+ swap // bit len
u@+ // external_address
`addr-extern swap
} cond
}
{
swap
// if addr_none$00
`addr-none swap
} cond
} : addr@+
{ addr@+ drop } : addr@
// User-friendly prints output of addr@
// (0 A or addr A or wc addr A -- )
{
dup `addr-none eq?
{ 2drop ."addr_none" }
{
`addr-extern eq?
{ (dump) type }
{ (x.) swap (dump) ":" $+ swap $+ type }
cond
}
cond
} : print-addr // print addr with workchain
forget maybe-anycast
forget parse-address-with-anycast

View file

@ -0,0 +1,91 @@
import os
import os.path
import subprocess
import sys
import tempfile
def getenv(name, default=None):
if name in os.environ:
return os.environ[name]
if default is None:
print("Environment variable", name, "is not set", file=sys.stderr)
exit(1)
return default
FUNC_EXECUTABLE = getenv("FUNC_EXECUTABLE", "func")
FIFT_EXECUTABLE = getenv("FIFT_EXECUTABLE", "fift")
#FUNC_STDLIB = getenv("FUNC_STDLIB")
FIFT_LIBS = getenv("FIFT_LIBS")
TMP_DIR = tempfile.mkdtemp()
COMPILED_FIF = os.path.join(TMP_DIR, "compiled.fif")
RUNNER_FIF = os.path.join(TMP_DIR, "runner.fif")
if len(sys.argv) != 2:
print("Usage : run_tests.py tests_dir", file=sys.stderr)
exit(1)
TESTS_DIR = sys.argv[1]
class ExecutionError(Exception):
pass
def compile_func(f):
res = subprocess.run([FUNC_EXECUTABLE, "-o", COMPILED_FIF, "-SPA", f], capture_output=True, timeout=10)
if res.returncode != 0:
raise ExecutionError(str(res.stderr, "utf-8"))
def run_runner():
res = subprocess.run([FIFT_EXECUTABLE, "-I", FIFT_LIBS, RUNNER_FIF], capture_output=True, timeout=10)
if res.returncode != 0:
raise ExecutionError(str(res.stderr, "utf-8"))
s = str(res.stdout, "utf-8")
s = [x.strip() for x in s.split("\n")]
return [x for x in s if x != ""]
tests = [s for s in os.listdir(TESTS_DIR) if s.endswith(".fc")]
tests.sort()
print("Found", len(tests), "tests", file=sys.stderr)
for ti, tf in enumerate(tests):
print("Running test %d/%d: %s" % (ti + 1, len(tests), tf), file=sys.stderr)
tf = os.path.join(TESTS_DIR, tf)
try:
compile_func(tf)
except ExecutionError as e:
print(file=sys.stderr)
print("Compilation error", file=sys.stderr)
print(e, file=sys.stderr)
exit(2)
with open(tf, "r") as fd:
lines = fd.readlines()
cases = []
for s in lines:
s = [x.strip() for x in s.split("|")]
if len(s) == 4 and s[0].strip() == "TESTCASE":
cases.append(s[1:])
if len(cases) == 0:
print(file=sys.stderr)
print("Error: no test cases", file=sys.stderr)
exit(2)
with open(RUNNER_FIF, "w") as f:
print("\"%s\" include <s constant code" % COMPILED_FIF, file=f)
for function, test_in, _ in cases:
print(test_in, function, "code 1 runvmx abort\"exitcode is not 0\" .s cr { drop } depth 1- times", file=f)
try:
func_out = run_runner()
if len(func_out) != len(cases):
raise ExecutionError("Unexpected number of lines")
for i in range(len(func_out)):
if func_out[i] != cases[i][2]:
raise ExecutionError("Error on case %d: expected '%s', found '%s'" % (i + 1, cases[i][2], func_out[i]))
except ExecutionError as e:
print(file=sys.stderr)
print("Error:", file=sys.stderr)
print(e, file=sys.stderr)
print(file=sys.stderr)
print("Compiled:", file=sys.stderr)
with open(COMPILED_FIF, "r") as f:
print(f.read(), file=sys.stderr)
exit(2)
print(" OK, %d cases" % len(cases), file=sys.stderr)
print("Done", file=sys.stderr)

View file

@ -0,0 +1,239 @@
import os
import os.path
import random
import subprocess
import sys
import tempfile
def getenv(name, default=None):
if name in os.environ:
return os.environ[name]
if default is not None:
return default
print("Environemnt variable", name, "is not set", file=sys.stderr)
exit(1)
VAR_CNT = 5
TMP_DIR = tempfile.mkdtemp()
FUNC_EXECUTABLE = getenv("FUNC_EXECUTABLE", "func")
FIFT_EXECUTABLE = getenv("FIFT_EXECUTABLE", "fift")
FIFT_LIBS = getenv("FIFT_LIBS")
MAGIC = 123456789
var_idx = 0
def gen_var_name():
global var_idx
var_idx += 1
return "i%d" % var_idx
class State:
def __init__(self, x):
self.x = x
self.vs = [0] * VAR_CNT
class Code:
pass
class CodeEmpty(Code):
def execute(self, state):
return None
def write(self, f, indent=0):
pass
class CodeReturn(Code):
def __init__(self, value):
self.value = value
def execute(self, state):
return [self.value] + state.vs
def write(self, f, indent=0):
print(" " * indent + "return (%d, %s);" % (self.value, ", ".join("v%d" % i for i in range(VAR_CNT))), file=f)
class CodeAdd(Code):
def __init__(self, i, value):
self.i = i
self.value = value
def execute(self, state):
state.vs[self.i] += self.value
return None
def write(self, f, indent=0):
print(" " * indent + "v%d += %d;" % (self.i, self.value), file=f)
class CodeBlock(Code):
def __init__(self, code):
self.code = code
def execute(self, state):
for c in self.code:
res = c.execute(state)
if res is not None:
return res
return None
def write(self, f, indent=0):
for c in self.code:
c.write(f, indent)
class CodeIfRange(Code):
def __init__(self, l, r, c1, c2):
self.l = l
self.r = r
self.c1 = c1
self.c2 = c2
def execute(self, state):
if self.l <= state.x < self.r:
return self.c1.execute(state)
else:
return self.c2.execute(state)
def write(self, f, indent=0):
print(" " * indent + "if (in(x, %d, %d)) {" % (self.l, self.r), file=f)
self.c1.write(f, indent + 1)
if isinstance(self.c2, CodeEmpty):
print(" " * indent + "}", file=f)
else:
print(" " * indent + "} else {", file=f)
self.c2.write(f, indent + 1)
print(" " * indent + "}", file=f)
class CodeRepeat(Code):
def __init__(self, n, c, loop_type):
if loop_type == 2:
n = max(n, 1)
self.n = n
self.c = c
self.loop_type = loop_type
def execute(self, state):
for _ in range(self.n):
res = self.c.execute(state)
if res is not None:
return res
return None
def write(self, f, indent=0):
if self.loop_type == 0:
print(" " * indent + "repeat (%d) {" % self.n, file=f)
self.c.write(f, indent + 1)
print(" " * indent + "}", file=f)
elif self.loop_type == 1:
var = gen_var_name()
print(" " * indent + "int %s = 0;" % var, file=f)
print(" " * indent + "while (%s < %d) {" % (var, self.n), file=f)
self.c.write(f, indent + 1)
print(" " * (indent + 1) + "%s += 1;" % var, file=f)
print(" " * indent + "}", file=f)
else:
var = gen_var_name()
print(" " * indent + "int %s = 0;" % var, file=f)
print(" " * indent + "do {", file=f)
self.c.write(f, indent + 1)
print(" " * (indent + 1) + "%s += 1;" % var, file=f)
print(" " * indent + "} until (%s >= %d);" % (var, self.n), file=f)
def write_function(f, name, body, inline=False, inline_ref=False, method_id=None):
print("_ %s(int x)" % name, file=f, end="")
if inline:
print(" inline", file=f, end="")
if inline_ref:
print(" inline_ref", file=f, end="")
if method_id is not None:
print(" method_id(%d)" % method_id, file=f, end="")
print(" {", file=f)
for i in range(VAR_CNT):
print(" int v%d = 0;" % i, file=f)
body.write(f, 1);
print("}", file=f)
def gen_code(xl, xr, with_return, loop_depth=0):
code = []
for _ in range(random.randint(0, 2)):
if random.randint(0, 3) == 0 and loop_depth < 3:
c = gen_code(xl, xr, False, loop_depth + 1)
code.append(CodeRepeat(random.randint(0, 3), c, random.randint(0, 2)))
elif xr - xl > 1:
xmid = random.randrange(xl + 1, xr)
ret = random.choice((0, 0, 0, 0, 0, 1, 2))
c1 = gen_code(xl, xmid, ret == 1, loop_depth)
if random.randrange(5) == 0:
c2 = CodeEmpty()
else:
c2 = gen_code(xmid, xr, ret == 2, loop_depth)
code.append(CodeIfRange(xl, xmid, c1, c2))
if with_return:
if xr - xl == 1:
code.append(CodeReturn(random.randrange(10**9)))
else:
xmid = random.randrange(xl + 1, xr)
c1 = gen_code(xl, xmid, True, loop_depth)
c2 = gen_code(xmid, xr, True, loop_depth)
code.append(CodeIfRange(xl, xmid, c1, c2))
for _ in range(random.randint(0, 3)):
pos = random.randint(0, len(code))
code.insert(pos, CodeAdd(random.randrange(VAR_CNT), random.randint(0, 10**6)))
if len(code) == 0:
return CodeEmpty()
return CodeBlock(code)
class ExecutionError(Exception):
pass
def compile_func(fc, fif):
res = subprocess.run([FUNC_EXECUTABLE, "-o", fif, "-SPA", fc], capture_output=True)
if res.returncode != 0:
raise ExecutionError(str(res.stderr, "utf-8"))
def runvm(compiled_fif, xl, xr):
runner = os.path.join(TMP_DIR, "runner.fif")
with open(runner, "w") as f:
print("\"%s\" include <s constant code" % compiled_fif, file=f)
for x in range(xl, xr):
print("%d 0 code 1 runvmx abort\"exitcode is not 0\" .s cr { drop } depth 1- times" % x, file=f)
res = subprocess.run([FIFT_EXECUTABLE, "-I", FIFT_LIBS, runner], capture_output=True)
if res.returncode != 0:
raise ExecutionError(str(res.stderr, "utf-8"))
output = []
for s in str(res.stdout, "utf-8").split("\n"):
if s.strip() != "":
output.append(list(map(int, s.split())))
return output
cnt_ok = 0
cnt_fail = 0
for test_id in range(0, 1000000):
random.seed(test_id)
inline = random.randint(0, 2)
xr = random.randint(1, 15)
var_idx = 0
code = gen_code(0, xr, True)
fc = os.path.join(TMP_DIR, "code.fc")
fif = os.path.join(TMP_DIR, "compiled.fif")
with open(fc, "w") as f:
print("int in(int x, int l, int r) impure { return (l <= x) & (x < r); }", file=f)
write_function(f, "foo", code, inline=(inline == 1), inline_ref=(inline == 2))
print("_ main(int x) {", file=f)
print(" (int ret, %s) = foo(x);" % ", ".join("int v%d" % i for i in range(VAR_CNT)), file=f)
print(" return (ret, %s, %d);" % (", ".join("v%d" % i for i in range(VAR_CNT)), MAGIC), file=f)
print("}", file=f)
compile_func(fc, fif)
ok = True
try:
output = runvm(fif, 0, xr)
for x in range(xr):
my_out = code.execute(State(x)) + [MAGIC]
fc_out = output[x]
if my_out != fc_out:
ok = False
break
except ExecutionError:
ok = False
if ok:
cnt_ok += 1
else:
cnt_fail += 1
print("Test %-6d %-6s ok:%-6d fail:%-6d" % (test_id, "OK" if ok else "FAIL", cnt_ok, cnt_fail), file=sys.stderr)

View file

@ -0,0 +1,17 @@
_ main(int a, int x) {
int y = 0;
int z = 0;
while ((y = x * x) > a) {
x -= 1;
z = 1;
}
return (y, z);
}
{-
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
-}

View file

@ -0,0 +1,89 @@
(int, int) f(int a, int b, int c, int d, int e, int f) {
;; solve a 2x2 linear equation
int D = a * d - b * c;
int Dx = e * d - b * f;
int Dy = a * f - e * c;
return (Dx / D, Dy / D);
}
int calc_phi() {
var n = 1;
repeat (70) { n *= 10; }
var p = var q = 1;
do {
(p, q) = (q, p + q);
} until (q > n);
return muldivr(p, n, q);
}
int calc_sqrt2() {
var n = 1;
repeat (70) { n *= 10; }
var p = var q = 1;
do {
var t = p + q;
(p, q) = (q, t + q);
} until (q > n);
return muldivr(p, n, q);
}
var calc_root(m) {
int base = 1;
repeat(70) { base *= 10; }
var (a, b, c) = (1, 0, - m);
var (p1, q1, p2, q2) = (1, 0, 0, 1);
do {
int k = -1;
var (a1, b1, c1) = (0, 0, 0);
do {
k += 1;
(a1, b1, c1) = (a, b, c);
c += b;
c += b += a;
} until (c > 0);
(a, b, c) = (- c1, - b1, - a1);
(p1, q1) = (k * p1 + q1, p1);
(p2, q2) = (k * p2 + q2, p2);
} until (p1 > base);
return (p1, q1, p2, q2);
}
{-
operator _/%_ infix 20;
(int, int) ((int x) /% (int y)) {
return (x / y, x % y);
}
(int, int) _/%_ (int x, int y) {
return (x / y, x % y);
}
-}
int ataninv(int base, int q) { ;; computes base*atan(1/q)
base ~/= q;
q *= - q;
int sum = 0;
int n = 1;
do {
sum += base ~/ n;
base ~/= q;
n += 2;
} until base == 0;
return sum;
}
int calc_pi() {
int base = 64;
repeat (70) { base *= 10; }
return (ataninv(base << 2, 5) - ataninv(base, 239)) ~>> 4;
}
int main() {
return calc_pi();
}
{-
method_id | in | out
TESTCASE | 0 | | 31415926535897932384626433832795028841971693993751058209749445923078164
-}

View file

@ -0,0 +1,16 @@
(int, int) main(int a, int b, int c, int d, int e, int f) {
int D = a * d - b * c;
int Dx = e * d - b * f;
int Dy = a * f - e * c;
return (Dx / D, Dy / D);
}
{-
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
-}

View file

@ -0,0 +1,24 @@
var twice(f, x) {
return f (f x);
}
_ sqr(x) {
return x * x;
}
var main(x) {
var f = sqr;
return twice(f, x) * f(x);
}
var pow6(x) method_id(4) {
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
-}

View file

@ -0,0 +1,24 @@
() main() { }
int steps(int x) method_id(1) {
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,17 @@
global ((int, int) -> int) op;
int check_assoc(int a, int b, int c) {
return op(op(a, b), c) == op(a, op(b, c));
}
int main(int x, int y, int z) {
op = _+_;
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,14 @@
_ check_assoc(op, a, b, c) {
return op(op(a, b), c) == op(a, op(b, c));
}
int main(int x, int y, int z) {
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,60 @@
const int1 = 1, int2 = 2;
const int int101 = 101;
const int int111 = 111;
const int1r = int1;
const str1 = "const1", str2 = "aabbcc"s;
const slice str2r = str2;
const str1int = 0x636f6e737431;
const str2int = 0xAABBCC;
const int nibbles = 4;
int iget1() { return int1; }
int iget2() { return int2; }
int iget3() { return int1 + int2; }
int iget1r() { return int1r; }
slice sget1() { return str1; }
slice sget2() { return str2; }
slice sget2r() { return str2r; }
const int int240 = ((int1 + int2) * 10) << 3;
int iget240() { return int240; }
builder newc() asm "NEWC";
slice endcs(builder b) asm "ENDC" "CTOS";
int sdeq (slice s1, slice s2) asm "SDEQ";
builder stslicer(builder b, slice s) asm "STSLICER";
_ main() {
int i1 = iget1();
int i2 = iget2();
int i3 = iget3();
throw_unless(int101, i1 == 1);
throw_unless(102, i2 == 2);
throw_unless(103, i3 == 3);
slice s1 = sget1();
slice s2 = sget2();
slice s3 = newc().stslicer(str1).stslicer(str2r).endcs();
throw_unless(int111, sdeq(s1, newc().store_uint(str1int, 12 * nibbles).endcs()));
throw_unless(112, sdeq(s2, newc().store_uint(str2int, 6 * nibbles).endcs()));
throw_unless(113, sdeq(s3, newc().store_uint(0x636f6e737431AABBCC, 18 * nibbles).endcs()));
int i4 = iget240();
throw_unless(104, i4 == 240);
return 0;
}
{-
TESTCASE | 0 | | 0
-}

View file

@ -0,0 +1,19 @@
int foo(int x) inline method_id(1) {
if (x == 1) {
return 111;
} else {
x *= 2;
}
return x + 1;
}
(int, int) main(int x) {
return (foo(x), 222);
}
{-
method_id | in | out
TESTCASE | 1 | 1 | 111
TESTCASE | 1 | 3 | 7
TESTCASE | 0 | 1 | 111 222
TESTCASE | 0 | 3 | 7 222
-}

View file

@ -0,0 +1,61 @@
int foo(int x) inline {
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;
}
(int) main(int x) {
return foo(x) * 10 + 5;
}
{-
method_id | in | out
TESTCASE | 0 | 9 | 9111111111111111111111111111111111111111111111111115
-}

View file

@ -0,0 +1,26 @@
int foo1(int x) {
if (x == 1) {
return 1;
}
return 2;
}
int foo2(int x) inline {
if (x == 1) {
return 11;
}
return 22;
}
int foo3(int x) inline_ref {
if (x == 1) {
return 111;
}
return 222;
}
(int, int, int) main(int x) {
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,43 @@
global int g;
_ foo_repeat() impure inline {
g = 1;
repeat(5) {
g *= 2;
}
}
int foo_until() impure inline {
g = 1;
int i = 0;
do {
g *= 2;
i += 1;
} until (i >= 8);
return i;
}
int foo_while() impure inline {
g = 1;
int i = 0;
while (i < 10) {
g *= 2;
i += 1;
}
return i;
}
_ main() {
foo_repeat();
int x = g;
foo_until();
int y = g;
foo_while();
int z = g;
return (x, y, z);
}
{-
method_id | in | out
TESTCASE | 0 | | 32 256 1024
-}

View file

@ -0,0 +1,12 @@
int foo1() method_id(1) { return 111; }
int foo2() method_id(3) { return 222; }
int foo3() method_id(10) { return 333; }
int main() { return 999; }
{-
method_id | in | out
TESTCASE | 1 | | 111
TESTCASE | 3 | | 222
TESTCASE | 10 | | 333
TESTCASE | 0 | | 999
-}

View file

@ -0,0 +1,54 @@
slice ascii_slice() method_id {
return "string";
}
slice raw_slice() method_id {
return "abcdef"s;
}
slice addr_slice() method_id {
return "Ef8zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM0vF"a;
}
int string_hex() method_id {
return "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345"u;
}
int string_minihash() method_id {
return "transfer(slice, int)"h;
}
int string_maxihash() method_id {
return "transfer(slice, int)"H;
}
int string_crc32() method_id {
return "transfer(slice, int)"c;
}
builder newc() asm "NEWC";
slice endcs(builder b) asm "ENDC" "CTOS";
int sdeq (slice s1, slice s2) asm "SDEQ";
_ main() {
slice s_ascii = ascii_slice();
slice s_raw = raw_slice();
slice s_addr = addr_slice();
int i_hex = string_hex();
int i_mini = string_minihash();
int i_maxi = string_maxihash();
int i_crc = string_crc32();
throw_unless(101, sdeq(s_ascii, newc().store_uint(0x737472696E67, 12 * 4).endcs()));
throw_unless(102, sdeq(s_raw, newc().store_uint(0xABCDEF, 6 * 4).endcs()));
throw_unless(103, sdeq(s_addr, newc().store_uint(4, 3).store_int(-1, 8)
.store_uint(0x3333333333333333333333333333333333333333333333333333333333333333, 256).endcs()));
throw_unless(104, i_hex == 0x4142434445464748494A4B4C4D4E4F505152535455565758595A303132333435);
throw_unless(105, i_mini == 0x7a62e8a8);
throw_unless(106, i_maxi == 0x7a62e8a8ebac41bd6de16c65e7be363bc2d2cbc6a0873778dead4795c13db979);
throw_unless(107, i_crc == 2235694568);
return 0;
}
{-
TESTCASE | 0 | | 0
-}

View file

@ -0,0 +1,17 @@
(int, int) main(int x) {
int y = 5;
if (x < 0) {
x *= 2;
y += 1;
if (x == -10) {
return (111, 0);
}
}
return (x + 1, y);
}
{-
method_id | in | out
TESTCASE | 0 | 10 | 11 5
TESTCASE | 0 | -5 | 111 0
TESTCASE | 0 | -4 | -7 6
-}

View file

@ -0,0 +1,18 @@
int foo(int x) inline {
if (x < 0) {
x *= 2;
if (x == -10) {
return 111;
}
}
return x + 1;
}
int main(int x) {
return foo(x) * 10;
}
{-
method_id | in | out
TESTCASE | 0 | 10 | 110
TESTCASE | 0 | -5 | 1110
TESTCASE | 0 | -4 | -70
-}

View file

@ -0,0 +1,48 @@
_ main() { }
int foo_repeat(int x) method_id(1) {
repeat(10) {
x += 10;
if (x >= 100) {
return x;
}
}
return -1;
}
int foo_while(int x) method_id(2) {
int i = 0;
while (i < 10) {
x += 10;
if (x >= 100) {
return x;
}
i += 1;
}
return -1;
}
int foo_until(int x) method_id(3) {
int i = 0;
do {
x += 10;
if (x >= 100) {
return x;
}
i += 1;
} until (i >= 10);
return -1;
}
{-
method_id | in | out
TESTCASE | 1 | 40 | 100
TESTCASE | 1 | 33 | 103
TESTCASE | 1 | -5 | -1
TESTCASE | 2 | 40 | 100
TESTCASE | 2 | 33 | 103
TESTCASE | 2 | -5 | -1
TESTCASE | 3 | 40 | 100
TESTCASE | 3 | 33 | 103
TESTCASE | 3 | -5 | -1
-}

View file

@ -0,0 +1,35 @@
int foo(int y) {
if (y < 0) {
y *= 2;
if (y == -10) {
return 111;
}
}
return y + 1;
}
(int, int) bar(int x, int y) {
if (x < 0) {
y = foo(y);
x *= 2;
if (x == -10) {
return (111, y);
}
}
return (x + 1, y);
}
(int, int) main(int x, int y) {
(x, y) = bar(x, y);
return (x, y * 10);
}
{-
method_id | in | out
TESTCASE | 0 | 3 3 | 4 30
TESTCASE | 0 | 3 -5 | 4 -50
TESTCASE | 0 | 3 -4 | 4 -40
TESTCASE | 0 | -5 3 | 111 40
TESTCASE | 0 | -5 -5 | 111 1110
TESTCASE | 0 | -5 -4 | 111 -70
TESTCASE | 0 | -4 3 | -7 40
TESTCASE | 0 | -4 -5 | -7 1110
TESTCASE | 0 | -4 -4 | -7 -70
-}

View file

@ -0,0 +1,14 @@
(int, int) main(int id) {
if (id > 0) {
if (id > 10) {
return (2 * id, 3 * id);
}
}
return (5, 6);
}
{-
method_id | in | out
TESTCASE | 0 | 0 | 5 6
TESTCASE | 0 | 4 | 5 6
TESTCASE | 0 | 11 | 22 33
-}

View file

@ -0,0 +1,18 @@
_ f(cs) {
return (cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(8),
cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(8),
cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(8),
cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(8),
cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(8));
}
_ main(cs) {
var (x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10,
x11, x12, x13, x14, x15, x16, x17, x18, x19) = f(cs);
return x0 + x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9
+ x10 + x11 + x12 + x13 + x14 + x15 + x16 + x17 + x18 + x19;
}
{-
method_id | in | out
TESTCASE | 0 | x{000102030405060708090a0b0c0d0e0f10111213} | 190
-}

View file

@ -0,0 +1,17 @@
int main(int x) {
int i = 0;
;; int f = false;
do {
i = i + 1;
if (i > 5) {
return 1;
}
int f = (i * i == 64);
} until (f);
return -1;
}
{-
method_id | in | out
TESTCASE | 0 | 0 | 1
-}

View file

@ -0,0 +1,24 @@
int test(int y) method_id(1) {
int x = 1;
if (y > 0) {
return 1;
}
return x > 0;
}
int f(int y) method_id(2) {
if (y > 0) {
return 1;
}
return 2;
}
_ main() { }
{-
method_id | in | out
TESTCASE | 1 | 10 | 1
TESTCASE | 1 | -5 | -1
TESTCASE | 2 | 10 | 1
TESTCASE | 2 | -5 | 2
-}

View file

@ -0,0 +1,14 @@
_ main(s) {
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
-}

View file

@ -173,6 +173,74 @@ int emulate_mul(int a, int b) {
return r;
}
int emulate_and(int a, int b) {
int both = a & b, any = a | b;
int r = VarDescr::_Int;
if (any & VarDescr::_Nan) {
return r | VarDescr::_Nan;
}
r |= VarDescr::_Finite;
if (any & VarDescr::_Zero) {
return VarDescr::ConstZero;
}
r |= both & (VarDescr::_Even | VarDescr::_Odd);
r |= both & (VarDescr::_Bit | VarDescr::_Bool);
if (both & VarDescr::_Odd) {
r |= VarDescr::_NonZero;
}
return r;
}
int emulate_or(int a, int b) {
if (b & VarDescr::_Zero) {
return a;
} else if (a & VarDescr::_Zero) {
return b;
}
int both = a & b, any = a | b;
int r = VarDescr::_Int;
if (any & VarDescr::_Nan) {
return r | VarDescr::_Nan;
}
r |= VarDescr::_Finite;
r |= any & VarDescr::_NonZero;
r |= any & VarDescr::_Odd;
r |= both & VarDescr::_Even;
return r;
}
int emulate_xor(int a, int b) {
if (b & VarDescr::_Zero) {
return a;
} else if (a & VarDescr::_Zero) {
return b;
}
int both = a & b, any = a | b;
int r = VarDescr::_Int;
if (any & VarDescr::_Nan) {
return r | VarDescr::_Nan;
}
r |= VarDescr::_Finite;
r |= both & VarDescr::_Even;
if (both & VarDescr::_Odd) {
r |= VarDescr::_Even;
}
return r;
}
int emulate_not(int a) {
int f = VarDescr::_Even | VarDescr::_Odd;
if ((a & f) && (~a & f)) {
a ^= f;
}
f = VarDescr::_Pos | VarDescr::_Neg;
if ((a & f) && (~a & f)) {
a ^= f;
}
a &= ~(VarDescr::_Zero | VarDescr::_NonZero | VarDescr::_Bit);
return a;
}
int emulate_lshift(int a, int b) {
if (((a | b) & VarDescr::_Nan) || !(~b & (VarDescr::_Neg | VarDescr::_NonZero))) {
return VarDescr::_Int | VarDescr::_Nan;
@ -427,6 +495,57 @@ AsmOp compile_negate(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
return exec_op("NEGATE", 1);
}
AsmOp compile_and(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
assert(res.size() == 1 && args.size() == 2);
VarDescr &r = res[0], &x = args[0], &y = args[1];
if (x.is_int_const() && y.is_int_const()) {
r.set_const(x.int_const & y.int_const);
x.unused();
y.unused();
return push_const(r.int_const);
}
r.val = emulate_and(x.val, y.val);
return exec_op("AND", 2);
}
AsmOp compile_or(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
assert(res.size() == 1 && args.size() == 2);
VarDescr &r = res[0], &x = args[0], &y = args[1];
if (x.is_int_const() && y.is_int_const()) {
r.set_const(x.int_const | y.int_const);
x.unused();
y.unused();
return push_const(r.int_const);
}
r.val = emulate_or(x.val, y.val);
return exec_op("OR", 2);
}
AsmOp compile_xor(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
assert(res.size() == 1 && args.size() == 2);
VarDescr &r = res[0], &x = args[0], &y = args[1];
if (x.is_int_const() && y.is_int_const()) {
r.set_const(x.int_const ^ y.int_const);
x.unused();
y.unused();
return push_const(r.int_const);
}
r.val = emulate_xor(x.val, y.val);
return exec_op("XOR", 2);
}
AsmOp compile_not(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
assert(res.size() == 1 && args.size() == 1);
VarDescr &r = res[0], &x = args[0];
if (x.is_int_const()) {
r.set_const(~x.int_const);
x.unused();
return push_const(r.int_const);
}
r.val = emulate_not(x.val);
return exec_op("NOT", 1);
}
AsmOp compile_mul_internal(VarDescr& r, VarDescr& x, VarDescr& y) {
if (x.is_int_const() && y.is_int_const()) {
r.set_const(x.int_const * y.int_const);
@ -442,6 +561,7 @@ AsmOp compile_mul_internal(VarDescr& r, VarDescr& x, VarDescr& y) {
if (y.always_zero() && x.always_finite()) {
// dubious optimization: NaN * 0 = ?
r.set_const(y.int_const);
x.unused();
return push_const(r.int_const);
}
if (*y.int_const == 1 && x.always_finite()) {
@ -468,6 +588,7 @@ AsmOp compile_mul_internal(VarDescr& r, VarDescr& x, VarDescr& y) {
if (x.always_zero() && y.always_finite()) {
// dubious optimization: NaN * 0 = ?
r.set_const(x.int_const);
y.unused();
return push_const(r.int_const);
}
if (*x.int_const == 1 && y.always_finite()) {
@ -1000,10 +1121,10 @@ void define_builtins() {
define_builtin_func("_>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, -1));
define_builtin_func("_~>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, 0));
define_builtin_func("_^>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, 1));
define_builtin_func("_&_", arith_bin_op, AsmOp::Custom("AND", 2));
define_builtin_func("_|_", arith_bin_op, AsmOp::Custom("OR", 2));
define_builtin_func("_^_", arith_bin_op, AsmOp::Custom("XOR", 2));
define_builtin_func("~_", arith_un_op, AsmOp::Custom("NOT", 1));
define_builtin_func("_&_", arith_bin_op, compile_and);
define_builtin_func("_|_", arith_bin_op, compile_or);
define_builtin_func("_^_", arith_bin_op, compile_xor);
define_builtin_func("~_", arith_un_op, compile_not);
define_builtin_func("^_+=_", arith_bin_op, compile_add);
define_builtin_func("^_-=_", arith_bin_op, compile_sub);
define_builtin_func("^_*=_", arith_bin_op, compile_mul);
@ -1017,9 +1138,9 @@ void define_builtins() {
define_builtin_func("^_>>=_", arith_bin_op, std::bind(compile_rshift, _1, _2, -1));
define_builtin_func("^_~>>=_", arith_bin_op, std::bind(compile_rshift, _1, _2, 0));
define_builtin_func("^_^>>=_", arith_bin_op, std::bind(compile_rshift, _1, _2, 1));
define_builtin_func("^_&=_", arith_bin_op, AsmOp::Custom("AND", 2));
define_builtin_func("^_|=_", arith_bin_op, AsmOp::Custom("OR", 2));
define_builtin_func("^_^=_", arith_bin_op, AsmOp::Custom("XOR", 2));
define_builtin_func("^_&=_", arith_bin_op, compile_and);
define_builtin_func("^_|=_", arith_bin_op, compile_or);
define_builtin_func("^_^=_", arith_bin_op, compile_xor);
define_builtin_func("muldiv", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, -1));
define_builtin_func("muldivr", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, 0));
define_builtin_func("muldivc", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, 1));
@ -1063,6 +1184,8 @@ void define_builtins() {
AsmOp::Nop());
define_builtin_func("~dump", TypeExpr::new_forall({X}, TypeExpr::new_map(X, TypeExpr::new_tensor({X, Unit}))),
AsmOp::Custom("s0 DUMP", 1, 1), true);
define_builtin_func("~strdump", TypeExpr::new_forall({X}, TypeExpr::new_map(X, TypeExpr::new_tensor({X, Unit}))),
AsmOp::Custom("STRDUMP", 1, 1), true);
define_builtin_func("run_method0", TypeExpr::new_map(Int, Unit),
[](auto a, auto b, auto c) { return compile_run_method(a, b, c, 0, false); }, true);
define_builtin_func("run_method1", TypeExpr::new_forall({X}, TypeExpr::new_map(TypeExpr::new_tensor({Int, X}), Unit)),

127
crypto/func/func-main.cpp Normal file
View file

@ -0,0 +1,127 @@
/*
This file is part of TON Blockchain source code.
TON Blockchain is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
TON Blockchain is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
You must obey the GNU General Public License in all respects for all
of the code used other than OpenSSL. If you modify file(s) with this
exception, you may extend this exception to your version of the file(s),
but you are not obligated to do so. If you do not wish to do so, delete this
exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here.
Copyright 2017-2020 Telegram Systems LLP
*/
#include "func.h"
#include "parser/srcread.h"
#include "parser/lexer.h"
#include "parser/symtable.h"
#include <getopt.h>
#include <fstream>
#include "git.h"
void usage(const char* progname) {
std::cerr
<< "usage: " << progname
<< " [-vIAPSR][-O<level>][-i<indent-spc>][-o<output-filename>][-W<boc-filename>] {<func-source-filename> ...}\n"
"\tGenerates Fift TVM assembler code from a funC source\n"
"-I\tEnables interactive mode (parse stdin)\n"
"-o<fift-output-filename>\tWrites generated code into specified file instead of stdout\n"
"-v\tIncreases verbosity level (extra information output into stderr)\n"
"-i<indent>\tSets indentation for the output code (in two-space units)\n"
"-A\tPrefix code with `\"Asm.fif\" include` preamble\n"
"-O<level>\tSets optimization level (2 by default)\n"
"-P\tEnvelope code into PROGRAM{ ... }END>c\n"
"-S\tInclude stack layout comments in the output code\n"
"-R\tInclude operation rewrite comments in the output code\n"
"-W<output-boc-file>\tInclude Fift code to serialize and save generated code into specified BoC file. Enables "
"-A and -P.\n"
"\t-s\tOutput semantic version of FunC and exit\n"
"\t-V<version>\tShow func build information\n";
std::exit(2);
}
int main(int argc, char* const argv[]) {
int i;
std::string output_filename;
while ((i = getopt(argc, argv, "Ahi:Io:O:PRsSvW:V")) != -1) {
switch (i) {
case 'A':
funC::asm_preamble = true;
break;
case 'I':
funC::interactive = true;
break;
case 'i':
funC::indent = std::max(0, atoi(optarg));
break;
case 'o':
output_filename = optarg;
break;
case 'O':
funC::opt_level = std::max(0, atoi(optarg));
break;
case 'P':
funC::program_envelope = true;
break;
case 'R':
funC::op_rewrite_comments = true;
break;
case 'S':
funC::stack_layout_comments = true;
break;
case 'v':
++funC::verbosity;
break;
case 'W':
funC::boc_output_filename = optarg;
funC::asm_preamble = funC::program_envelope = true;
break;
case 's':
std::cout << funC::func_version << "\n";
std::exit(0);
break;
case 'V':
std::cout << "FunC semantic version: v" << funC::func_version << "\n";
std::cout << "Build information: [ Commit: " << GitMetadata::CommitSHA1() << ", Date: " << GitMetadata::CommitDate() << "]\n";
std::exit(0);
break;
case 'h':
default:
usage(argv[0]);
}
}
std::ostream *outs = &std::cout;
std::unique_ptr<std::fstream> fs;
if (!output_filename.empty()) {
fs = std::make_unique<std::fstream>(output_filename, fs->trunc | fs->out);
if (!fs->is_open()) {
std::cerr << "failed to create output file " << output_filename << '\n';
return 2;
}
outs = fs.get();
}
std::vector<std::string> sources;
while (optind < argc) {
sources.push_back(std::string(argv[optind++]));
}
return funC::func_proceed(sources, *outs, std::cerr);
}

View file

@ -28,16 +28,14 @@
#include "func.h"
#include "parser/srcread.h"
#include "parser/lexer.h"
#include "parser/symtable.h"
#include <getopt.h>
#include <fstream>
#include "git.h"
namespace funC {
int verbosity, indent, opt_level = 2;
bool stack_layout_comments, op_rewrite_comments, program_envelope, asm_preamble;
std::ostream* outs = &std::cout;
bool interactive = false;
std::string generated_from, boc_output_filename;
/*
@ -46,58 +44,59 @@ std::string generated_from, boc_output_filename;
*
*/
void generate_output_func(SymDef* func_sym) {
void generate_output_func(SymDef* func_sym, std::ostream &outs, std::ostream &errs) {
SymValCodeFunc* func_val = dynamic_cast<SymValCodeFunc*>(func_sym->value);
assert(func_val);
std::string name = sym::symbols.get_name(func_sym->sym_idx);
if (verbosity >= 2) {
std::cerr << "\n\n=========================\nfunction " << name << " : " << func_val->get_type() << std::endl;
errs << "\n\n=========================\nfunction " << name << " : " << func_val->get_type() << std::endl;
}
if (!func_val->code) {
std::cerr << "( function `" << name << "` undefined )\n";
errs << "( function `" << name << "` undefined )\n";
throw src::ParseError(func_sym->loc, name);
} else {
CodeBlob& code = *(func_val->code);
if (verbosity >= 3) {
code.print(std::cerr, 9);
code.print(errs, 9);
}
code.simplify_var_types();
if (verbosity >= 5) {
std::cerr << "after simplify_var_types: \n";
code.print(std::cerr, 0);
errs << "after simplify_var_types: \n";
code.print(errs, 0);
}
code.prune_unreachable_code();
if (verbosity >= 5) {
std::cerr << "after prune_unreachable: \n";
code.print(std::cerr, 0);
errs << "after prune_unreachable: \n";
code.print(errs, 0);
}
code.split_vars(true);
if (verbosity >= 5) {
std::cerr << "after split_vars: \n";
code.print(std::cerr, 0);
errs << "after split_vars: \n";
code.print(errs, 0);
}
for (int i = 0; i < 8; i++) {
code.compute_used_code_vars();
if (verbosity >= 4) {
std::cerr << "after compute_used_vars: \n";
code.print(std::cerr, 6);
errs << "after compute_used_vars: \n";
code.print(errs, 6);
}
code.fwd_analyze();
if (verbosity >= 5) {
std::cerr << "after fwd_analyze: \n";
code.print(std::cerr, 6);
errs << "after fwd_analyze: \n";
code.print(errs, 6);
}
code.prune_unreachable_code();
if (verbosity >= 5) {
std::cerr << "after prune_unreachable: \n";
code.print(std::cerr, 6);
errs << "after prune_unreachable: \n";
code.print(errs, 6);
}
}
code.mark_noreturn();
if (verbosity >= 3) {
code.print(std::cerr, 15);
code.print(errs, 15);
}
if (verbosity >= 2) {
std::cerr << "\n---------- resulting code for " << name << " -------------\n";
errs << "\n---------- resulting code for " << name << " -------------\n";
}
bool inline_func = (func_val->flags & 1);
bool inline_ref = (func_val->flags & 2);
@ -107,7 +106,7 @@ void generate_output_func(SymDef* func_sym) {
} else if (inline_ref) {
modifier = "REF";
}
*outs << std::string(indent * 2, ' ') << name << " PROC" << modifier << ":<{\n";
outs << std::string(indent * 2, ' ') << name << " PROC" << modifier << ":<{\n";
int mode = 0;
if (stack_layout_comments) {
mode |= Stack::_StkCmt | Stack::_CptStkCmt;
@ -120,145 +119,71 @@ void generate_output_func(SymDef* func_sym) {
if (fv && (fv->flags & 1) && code.ops->noreturn()) {
mode |= Stack::_InlineFunc;
}
code.generate_code(*outs, mode, indent + 1);
*outs << std::string(indent * 2, ' ') << "}>\n";
code.generate_code(outs, mode, indent + 1);
outs << std::string(indent * 2, ' ') << "}>\n";
if (verbosity >= 2) {
std::cerr << "--------------\n";
errs << "--------------\n";
}
}
}
int generate_output() {
int generate_output(std::ostream &outs, std::ostream &errs) {
if (asm_preamble) {
*outs << "\"Asm.fif\" include\n";
outs << "\"Asm.fif\" include\n";
}
*outs << "// automatically generated from " << generated_from << std::endl;
outs << "// automatically generated from " << generated_from << std::endl;
if (program_envelope) {
*outs << "PROGRAM{\n";
outs << "PROGRAM{\n";
}
for (SymDef* func_sym : glob_func) {
SymValCodeFunc* func_val = dynamic_cast<SymValCodeFunc*>(func_sym->value);
assert(func_val);
std::string name = sym::symbols.get_name(func_sym->sym_idx);
*outs << std::string(indent * 2, ' ');
outs << std::string(indent * 2, ' ');
if (func_val->method_id.is_null()) {
*outs << "DECLPROC " << name << "\n";
outs << "DECLPROC " << name << "\n";
} else {
*outs << func_val->method_id << " DECLMETHOD " << name << "\n";
outs << func_val->method_id << " DECLMETHOD " << name << "\n";
}
}
for (SymDef* gvar_sym : glob_vars) {
assert(dynamic_cast<SymValGlobVar*>(gvar_sym->value));
std::string name = sym::symbols.get_name(gvar_sym->sym_idx);
*outs << std::string(indent * 2, ' ') << "DECLGLOBVAR " << name << "\n";
outs << std::string(indent * 2, ' ') << "DECLGLOBVAR " << name << "\n";
}
int errors = 0;
for (SymDef* func_sym : glob_func) {
try {
generate_output_func(func_sym);
generate_output_func(func_sym, outs, errs);
} catch (src::Error& err) {
std::cerr << "cannot generate code for function `" << sym::symbols.get_name(func_sym->sym_idx) << "`:\n"
errs << "cannot generate code for function `" << sym::symbols.get_name(func_sym->sym_idx) << "`:\n"
<< err << std::endl;
++errors;
}
}
if (program_envelope) {
*outs << "}END>c\n";
outs << "}END>c\n";
}
if (!boc_output_filename.empty()) {
*outs << "2 boc+>B \"" << boc_output_filename << "\" B>file\n";
outs << "2 boc+>B \"" << boc_output_filename << "\" B>file\n";
}
return errors;
}
} // namespace funC
void usage(const char* progname) {
std::cerr
<< "usage: " << progname
<< " [-vIAPSR][-O<level>][-i<indent-spc>][-o<output-filename>][-W<boc-filename>] {<func-source-filename> ...}\n"
"\tGenerates Fift TVM assembler code from a funC source\n"
"-I\tEnables interactive mode (parse stdin)\n"
"-o<fift-output-filename>\tWrites generated code into specified file instead of stdout\n"
"-v\tIncreases verbosity level (extra information output into stderr)\n"
"-i<indent>\tSets indentation for the output code (in two-space units)\n"
"-A\tPrefix code with `\"Asm.fif\" include` preamble\n"
"-O<level>\tSets optimization level (2 by default)\n"
"-P\tEnvelope code into PROGRAM{ ... }END>c\n"
"-S\tInclude stack layout comments in the output code\n"
"-R\tInclude operation rewrite comments in the output code\n"
"-W<output-boc-file>\tInclude Fift code to serialize and save generated code into specified BoC file. Enables "
"-A and -P.\n"
"\t-s\tOutput semantic version of FunC and exit\n"
"\t-V<version>\tShow func build information\n";
std::exit(2);
}
void output_inclusion_stack() {
void output_inclusion_stack(std::ostream &errs) {
while (!funC::inclusion_locations.empty()) {
src::SrcLocation loc = funC::inclusion_locations.top();
funC::inclusion_locations.pop();
if (loc.fdescr) {
std::cerr << "note: included from ";
loc.show(std::cerr);
std::cerr << std::endl;
errs << "note: included from ";
loc.show(errs);
errs << std::endl;
}
}
}
std::string output_filename;
int main(int argc, char* const argv[]) {
int i;
bool interactive = false;
while ((i = getopt(argc, argv, "Ahi:Io:O:PRsSvW:V")) != -1) {
switch (i) {
case 'A':
funC::asm_preamble = true;
break;
case 'I':
interactive = true;
break;
case 'i':
funC::indent = std::max(0, atoi(optarg));
break;
case 'o':
output_filename = optarg;
break;
case 'O':
funC::opt_level = std::max(0, atoi(optarg));
break;
case 'P':
funC::program_envelope = true;
break;
case 'R':
funC::op_rewrite_comments = true;
break;
case 'S':
funC::stack_layout_comments = true;
break;
case 'v':
++funC::verbosity;
break;
case 'W':
funC::boc_output_filename = optarg;
funC::asm_preamble = funC::program_envelope = true;
break;
case 's':
std::cout << funC::func_version << "\n";
std::exit(0);
break;
case 'V':
std::cout << "FunC semantic version: v" << funC::func_version << "\n";
std::cout << "Build information: [ Commit: " << GitMetadata::CommitSHA1() << ", Date: " << GitMetadata::CommitDate() << "]\n";
std::exit(0);
break;
case 'h':
default:
usage(argv[0]);
}
}
int func_proceed(const std::vector<std::string> &sources, std::ostream &outs, std::ostream &errs) {
if (funC::program_envelope && !funC::indent) {
funC::indent = 1;
}
@ -268,12 +193,11 @@ int main(int argc, char* const argv[]) {
int ok = 0, proc = 0;
try {
while (optind < argc) {
// funC::generated_from += std::string{"`"} + argv[optind] + "` ";
ok += funC::parse_source_file(argv[optind++]);
for (auto src : sources) {
ok += funC::parse_source_file(src.c_str());
proc++;
}
if (interactive) {
if (funC::interactive) {
funC::generated_from += "stdin ";
ok += funC::parse_source_stdin();
proc++;
@ -284,29 +208,24 @@ int main(int argc, char* const argv[]) {
if (!proc) {
throw src::Fatal{"no source files, no output"};
}
std::unique_ptr<std::fstream> fs;
if (!output_filename.empty()) {
fs = std::make_unique<std::fstream>(output_filename, fs->trunc | fs->out);
if (!fs->is_open()) {
std::cerr << "failed to create output file " << output_filename << '\n';
return 2;
}
funC::outs = fs.get();
}
funC::generate_output();
return funC::generate_output(outs, errs);
} catch (src::Fatal& fatal) {
std::cerr << "fatal: " << fatal << std::endl;
output_inclusion_stack();
std::exit(1);
errs << "fatal: " << fatal << std::endl;
output_inclusion_stack(errs);
return 2;
} catch (src::Error& error) {
std::cerr << error << std::endl;
output_inclusion_stack();
std::exit(1);
errs << error << std::endl;
output_inclusion_stack(errs);
return 2;
} catch (funC::UnifyError& unif_err) {
std::cerr << "fatal: ";
unif_err.print_message(std::cerr);
std::cerr << std::endl;
output_inclusion_stack();
std::exit(1);
errs << "fatal: ";
unif_err.print_message(errs);
errs << std::endl;
output_inclusion_stack(errs);
return 2;
}
return 0;
}
} // namespace funC

View file

@ -39,7 +39,7 @@ extern std::string generated_from;
constexpr int optimize_depth = 20;
const std::string func_version{"0.2.0"};
const std::string func_version{"0.3.0"};
enum Keyword {
_Eof = -1,
@ -1631,6 +1631,7 @@ inline compile_func_t make_ext_compile(AsmOp op) {
struct SymValAsmFunc : SymValFunc {
simple_compile_func_t simple_compile;
compile_func_t ext_compile;
td::uint64 crc;
~SymValAsmFunc() override = default;
SymValAsmFunc(TypeExpr* ft, const AsmOp& _macro, bool impure = false)
: SymValFunc(-1, ft, impure), simple_compile(make_simple_compile(_macro)) {
@ -1665,4 +1666,19 @@ AsmOp push_const(td::RefInt256 x);
void define_builtins();
extern int verbosity, indent, opt_level;
extern bool stack_layout_comments, op_rewrite_comments, program_envelope, asm_preamble, interactive;
extern std::string generated_from, boc_output_filename;
/*
*
* OUTPUT CODE GENERATOR
*
*/
int func_proceed(const std::vector<std::string> &sources, std::ostream &outs, std::ostream &errs);
} // namespace funC

View file

@ -254,9 +254,7 @@ void parse_const_decl(Lexer& lex) {
if (!sym_def) {
lex.cur().error_at("cannot define global symbol `", "`");
}
if (sym_def->value) {
lex.cur().error_at("global symbol `", "` already exists");
}
Lexem ident = lex.cur();
lex.next();
if (lex.tp() != '=') {
lex.cur().error_at("expected = instead of ", "");
@ -273,10 +271,11 @@ void parse_const_decl(Lexer& lex) {
if ((wanted_type != Expr::_None) && (x->cls != wanted_type)) {
lex.cur().error("expression type does not match wanted type");
}
SymValConst* new_value = nullptr;
if (x->cls == Expr::_Const) { // Integer constant
sym_def->value = new SymValConst{const_cnt++, x->intval};
new_value = new SymValConst{const_cnt++, x->intval};
} else if (x->cls == Expr::_SliceConst) { // Slice constant (string)
sym_def->value = new SymValConst{const_cnt++, x->strval};
new_value = new SymValConst{const_cnt++, x->strval};
} else if (x->cls == Expr::_Apply) {
code.emplace_back(loc, Op::_Import, std::vector<var_idx_t>());
auto tmp_vars = x->pre_compile(code);
@ -304,10 +303,20 @@ void parse_const_decl(Lexer& lex) {
if (op.origin.is_null() || !op.origin->is_valid()) {
lex.cur().error("precompiled expression did not result in a valid integer constant");
}
sym_def->value = new SymValConst{const_cnt++, op.origin};
new_value = new SymValConst{const_cnt++, op.origin};
} else {
lex.cur().error("integer or slice literal or constant expected");
}
if (sym_def->value) {
SymValConst* old_value = dynamic_cast<SymValConst*>(sym_def->value);
Keyword new_type = new_value->get_type();
if (!old_value || old_value->get_type() != new_type ||
(new_type == _Int && *old_value->get_int_value() != *new_value->get_int_value()) ||
(new_type == _Slice && old_value->get_str_value() != new_value->get_str_value())) {
ident.error_at("global symbol `", "` already exists");
}
}
sym_def->value = new_value;
}
FormalArgList parse_formal_args(Lexer& lex) {
@ -1261,19 +1270,48 @@ SymValAsmFunc* parse_asm_func_body(Lexer& lex, TypeExpr* func_type, const Formal
lex.expect(')');
}
while (lex.tp() == _String) {
asm_ops.push_back(AsmOp::Parse(lex.cur().str, cnt, width));
lex.next();
if (asm_ops.back().is_custom()) {
cnt = width;
std::string ops = lex.cur().str; // <op>\n<op>\n...
std::string op;
for (const char& c : ops) {
if (c == '\n') {
if (!op.empty()) {
asm_ops.push_back(AsmOp::Parse(op, cnt, width));
if (asm_ops.back().is_custom()) {
cnt = width;
}
op.clear();
}
} else {
op.push_back(c);
}
}
if (!op.empty()) {
asm_ops.push_back(AsmOp::Parse(op, cnt, width));
if (asm_ops.back().is_custom()) {
cnt = width;
}
}
lex.next();
}
if (asm_ops.empty()) {
throw src::ParseError{lex.cur().loc, "string with assembler instruction expected"};
}
lex.expect(';');
std::string crc_s;
for (const AsmOp& asm_op : asm_ops) {
crc_s += asm_op.op;
}
crc_s.push_back(impure);
for (const int& x : arg_order) {
crc_s += std::string((const char*) (&x), (const char*) (&x + 1));
}
for (const int& x : ret_order) {
crc_s += std::string((const char*) (&x), (const char*) (&x + 1));
}
auto res = new SymValAsmFunc{func_type, asm_ops, impure};
res->arg_order = std::move(arg_order);
res->ret_order = std::move(ret_order);
res->crc = td::crc64(crc_s);
return res;
}
@ -1439,16 +1477,22 @@ void parse_func_def(Lexer& lex) {
// code->print(std::cerr); // !!!DEBUG!!!
func_sym_code->code = code;
} else {
Lexem asm_lexem = lex.cur();
SymValAsmFunc* asm_func = parse_asm_func_body(lex, func_type, arg_list, ret_type, impure);
if (func_sym_val) {
if (dynamic_cast<SymValCodeFunc*>(func_sym_val)) {
lex.cur().error("function `"s + func_name.str + "` was already declared as an ordinary function");
asm_lexem.error("function `"s + func_name.str + "` was already declared as an ordinary function");
}
if (dynamic_cast<SymValAsmFunc*>(func_sym_val)) {
lex.cur().error("redefinition of built-in assembler function `"s + func_name.str + "`");
SymValAsmFunc* asm_func_old = dynamic_cast<SymValAsmFunc*>(func_sym_val);
if (asm_func_old) {
if (asm_func->crc != asm_func_old->crc) {
asm_lexem.error("redefinition of built-in assembler function `"s + func_name.str + "`");
}
} else {
asm_lexem.error("redefinition of previously (somehow) defined function `"s + func_name.str + "`");
}
lex.cur().error("redefinition of previously (somehow) defined function `"s + func_name.str + "`");
}
func_sym->value = parse_asm_func_body(lex, func_type, arg_list, ret_type, impure);
func_sym->value = asm_func;
}
if (method_id.not_null()) {
auto val = dynamic_cast<SymVal*>(func_sym->value);
@ -1657,7 +1701,14 @@ bool parse_source_file(const char* filename, src::Lexem lex) {
throw src::Fatal{msg};
}
}
std::string real_filename = td::realpath(td::CSlice(filename)).move_as_ok();
auto path_res = td::realpath(td::CSlice(filename));
if (path_res.is_error()) {
auto error = path_res.move_as_error();
lex.error(error.message().c_str());
return false;
}
std::string real_filename = path_res.move_as_ok();
if (std::count(source_files.begin(), source_files.end(), real_filename)) {
if (verbosity >= 2) {
if (lex.tp) {

15
crypto/func/test/co2.fc Normal file
View file

@ -0,0 +1,15 @@
const int x = 5;
const slice s = "abacaba";
const int y = 3;
const slice s = "abacaba";
const int x = 5;
const int z = 4, z = 4;
int sdeq (slice s1, slice s2) asm "SDEQ";
() main() {
throw_unless(101, x == 5);
throw_unless(102, y == 3);
throw_unless(103, z == 4);
throw_unless(104, sdeq(s, "abacaba"));
}

24
crypto/func/test/co3.fc Normal file
View file

@ -0,0 +1,24 @@
const val1 = 123456789;
const val2 = 987654321;
const val3 = 135792468;
const val4 = 246813579;
const prec_and = val1 & val2;
const prec_or = val1 | val2;
const prec_xor = val1 ^ val2;
const prec_logic = ((val1 & val2) | val3) ^ val4;
const prec_nand = val1 & (~ val2);
int get_and() { return prec_and; }
int get_or() { return prec_or; }
int get_xor() { return prec_xor; }
int get_logic() { return prec_logic; }
int get_nand() { return prec_nand; }
_ main() {
throw_unless(101, get_and() == 39471121);
throw_unless(102, get_or() == 1071639989);
throw_unless(103, get_xor() == 1032168868);
throw_unless(104, get_logic() == 82599134);
throw_unless(105, get_nand() == 83985668);
}

26
crypto/func/test/s2.fc Normal file
View file

@ -0,0 +1,26 @@
slice test1() asm """
"Test" $>s
PUSHSLICE
""";
slice test2() asm """
"Hello"
" "
"World"
$+ $+ $>s
PUSHSLICE
""";
int sdeq (slice s1, slice s2) asm """SDEQ""";
int sdeq (slice s1, slice s2) asm "SDEQ" "";
int sdeq (slice s1, slice s2) asm "" """
SDEQ
""";
() main() {
slice s = test1();
throw_unless(101, sdeq(s, "Test"));
slice s = test2();
throw_unless(102, sdeq(s, "Hello World"));
}

View file

@ -0,0 +1,131 @@
/*
This file is part of TON Blockchain source code.
TON Blockchain is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
TON Blockchain is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
You must obey the GNU General Public License in all respects for all
of the code used other than OpenSSL. If you modify file(s) with this
exception, you may extend this exception to your version of the file(s),
but you are not obligated to do so. If you do not wish to do so, delete this
exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here.
Copyright 2017-2020 Telegram Systems LLP
*/
#include "func/func.h"
#include "git.h"
#include "td/utils/JsonBuilder.h"
#include "fift/utils.h"
#include "td/utils/base64.h"
#include <sstream>
#include <iomanip>
std::string escape_json(const std::string &s) {
std::ostringstream o;
for (auto c = s.cbegin(); c != s.cend(); c++) {
switch (*c) {
case '"': o << "\\\""; break;
case '\\': o << "\\\\"; break;
case '\b': o << "\\b"; break;
case '\f': o << "\\f"; break;
case '\n': o << "\\n"; break;
case '\r': o << "\\r"; break;
case '\t': o << "\\t"; break;
default:
if ('\x00' <= *c && *c <= '\x1f') {
o << "\\u"
<< std::hex << std::setw(4) << std::setfill('0') << static_cast<int>(*c);
} else {
o << *c;
}
}
}
return o.str();
}
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();
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));
auto &sources_arr = sources_obj.get_array();
std::vector<std::string> sources;
for (auto &item : sources_arr) {
sources.push_back(item.get_string().str());
}
funC::opt_level = std::max(0, opt_level);
funC::program_envelope = 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());
}
TRY_RESULT(code_cell, fift::compile_asm(outs.str(), "/fiftlib/", false));
TRY_RESULT(boc, vm::std_boc_serialize(code_cell));
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", escape_json(outs.str()));
result_obj.leave();
outs.clear();
errs.clear();
return result_json.string_builder().as_cslice().str();
}
extern "C" {
const char* version() {
auto version_json = td::JsonBuilder();
auto obj = version_json.enter_object();
obj("funcVersion", funC::func_version);
obj("funcFiftLibCommitHash", GitMetadata::CommitSHA1());
obj("funcFiftLibCommitDate", GitMetadata::CommitDate());
obj.leave();
return strdup(version_json.string_builder().as_cslice().c_str());
}
const char *func_compile(char *config_json) {
auto res = compile_internal(config_json);
if (res.is_error()) {
auto result = res.move_as_error();
auto error_res = td::JsonBuilder();
auto error_o = error_res.enter_object();
error_o("status", "error");
error_o("message", result.message().str());
error_o.leave();
return strdup(error_res.string_builder().as_cslice().c_str());
}
auto res_string = res.move_as_ok();
return strdup(res_string.c_str());
}
}

View file

@ -125,8 +125,9 @@ int Lexem::set(std::string _str, const SrcLocation& _loc, int _tp, int _val) {
}
Lexer::Lexer(SourceReader& _src, bool init, std::string active_chars, std::string eol_cmts, std::string open_cmts,
std::string close_cmts, std::string quote_chars)
: src(_src), eof(false), lexem("", src.here(), Lexem::Undefined), peek_lexem("", {}, Lexem::Undefined) {
std::string close_cmts, std::string quote_chars, std::string multiline_quote)
: src(_src), eof(false), lexem("", src.here(), Lexem::Undefined), peek_lexem("", {}, Lexem::Undefined),
multiline_quote(std::move(multiline_quote)) {
std::memset(char_class, 0, sizeof(char_class));
unsigned char activity = cc::active;
for (char c : active_chars) {
@ -171,6 +172,19 @@ void Lexer::set_spec(std::array<int, 3>& arr, std::string setup) {
}
}
bool Lexer::is_multiline_quote(const char* begin, const char* end) {
if (multiline_quote.empty()) {
return false;
}
for (const char& c : multiline_quote) {
if (begin == end || *begin != c) {
return false;
}
++begin;
}
return true;
}
void Lexer::expect(int exp_tp, const char* msg) {
if (tp() != exp_tp) {
throw ParseError{lexem.loc, (msg ? std::string{msg} : Lexem::lexem_name_str(exp_tp)) + " expected instead of " +
@ -234,6 +248,37 @@ const Lexem& Lexer::next() {
}
return lexem.clear(src.here(), Lexem::Eof);
}
if (is_multiline_quote(src.get_ptr(), src.get_end_ptr())) {
src.advance(multiline_quote.size());
const char* begin = src.get_ptr();
const char* end = nullptr;
SrcLocation here = src.here();
std::string body;
while (!src.is_eof()) {
if (src.is_eoln()) {
body.push_back('\n');
src.load_line();
continue;
}
if (is_multiline_quote(src.get_ptr(), src.get_end_ptr())) {
end = src.get_ptr();
src.advance(multiline_quote.size());
break;
}
body.push_back(src.cur_char());
src.advance(1);
}
if (!end) {
src.error("string extends past end of file");
}
lexem.set(body, here, Lexem::String);
int c = src.cur_char();
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
lexem.val = c;
src.advance(1);
}
return lexem;
}
int c = src.cur_char();
const char* end = src.get_ptr();
if (is_quote_char(c) || c == '`') {

View file

@ -71,6 +71,7 @@ class Lexer {
Lexem lexem, peek_lexem;
unsigned char char_class[128];
std::array<int, 3> eol_cmt, cmt_op, cmt_cl;
std::string multiline_quote;
enum cc { left_active = 2, right_active = 1, active = 3, allow_repeat = 4, quote_char = 8 };
public:
@ -78,7 +79,8 @@ class Lexer {
return eof;
}
Lexer(SourceReader& _src, bool init = false, std::string active_chars = ";,() ~.", std::string eol_cmts = ";;",
std::string open_cmts = "{-", std::string close_cmts = "-}", std::string quote_chars = "\"");
std::string open_cmts = "{-", std::string close_cmts = "-}", std::string quote_chars = "\"",
std::string multiline_quote = "\"\"\"");
const Lexem& next();
const Lexem& cur() const {
return lexem;
@ -109,6 +111,7 @@ class Lexer {
private:
void set_spec(std::array<int, 3>& arr, std::string setup);
bool is_multiline_quote(const char* begin, const char* end);
};
} // namespace src

View file

@ -2423,7 +2423,7 @@ std::vector<const src::FileDescr*> source_fdescr;
bool parse_source(std::istream* is, src::FileDescr* fdescr) {
src::SourceReader reader{is, fdescr};
src::Lexer lex{reader, true, "(){}:;? #$. ^~ #", "//", "/*", "*/"};
src::Lexer lex{reader, true, "(){}:;? #$. ^~ #", "//", "/*", "*/", ""};
while (lex.tp() != src::_Eof) {
parse_constructor_def(lex);
// std::cerr << lex.cur().str << '\t' << lex.cur().name_str() << std::endl;

View file

@ -125,8 +125,11 @@ bool TupleT::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
}
bool TLB::validate_ref_internal(int* ops, Ref<vm::Cell> cell_ref, bool weak) const {
if (ops && --*ops < 0) {
return false;
if (ops) {
if (*ops <= 0) {
return false;
}
--*ops;
}
bool is_special;
auto cs = load_cell_slice_special(std::move(cell_ref), is_special);

View file

@ -335,7 +335,7 @@ bool CellBuilder::store_ulong_rchk_bool(unsigned long long val, unsigned val_bit
}
CellBuilder& CellBuilder::store_long(long long val, unsigned val_bits) {
return store_long_top(val << (64 - val_bits), val_bits);
return store_long_top(val_bits == 0 ? 0 : (unsigned long long)val << (64 - val_bits), val_bits);
}
CellBuilder& CellBuilder::store_long_top(unsigned long long val, unsigned top_bits) {

View file

@ -1055,44 +1055,47 @@ std::ostream& operator<<(std::ostream& os, Ref<CellSlice> cs_ref) {
// If can_be_special is not null, then it is allowed to load special cell
// Flag whether loaded cell is actually special will be stored into can_be_special
VirtualCell::LoadedCell load_cell_slice_impl(const Ref<Cell>& cell, bool* can_be_special) {
auto* vm_state_interface = VmStateInterface::get();
if (vm_state_interface) {
vm_state_interface->register_cell_load(cell->get_hash());
}
auto r_loaded_cell = cell->load_cell();
if (r_loaded_cell.is_error()) {
throw VmError{Excno::cell_und, "failed to load cell"};
}
auto loaded_cell = r_loaded_cell.move_as_ok();
if (loaded_cell.data_cell->special_type() == DataCell::SpecialType::PrunnedBranch) {
auto virtualization = loaded_cell.virt.get_virtualization();
if (virtualization != 0) {
throw VmVirtError{virtualization};
VirtualCell::LoadedCell load_cell_slice_impl(Ref<Cell> cell, bool* can_be_special) {
while (true) {
auto* vm_state_interface = VmStateInterface::get();
if (vm_state_interface) {
vm_state_interface->register_cell_load(cell->get_hash());
}
}
if (can_be_special) {
*can_be_special = loaded_cell.data_cell->is_special();
} else if (loaded_cell.data_cell->is_special()) {
if (loaded_cell.data_cell->special_type() == DataCell::SpecialType::Library) {
if (vm_state_interface) {
CellSlice cs(std::move(loaded_cell));
DCHECK(cs.size() == Cell::hash_bits + 8);
auto library_cell = vm_state_interface->load_library(cs.data_bits() + 8);
if (library_cell.not_null()) {
//TODO: fix infinity loop
return load_cell_slice_impl(library_cell, nullptr);
}
throw VmError{Excno::cell_und, "failed to load library cell"};
auto r_loaded_cell = cell->load_cell();
if (r_loaded_cell.is_error()) {
throw VmError{Excno::cell_und, "failed to load cell"};
}
auto loaded_cell = r_loaded_cell.move_as_ok();
if (loaded_cell.data_cell->special_type() == DataCell::SpecialType::PrunnedBranch) {
auto virtualization = loaded_cell.virt.get_virtualization();
if (virtualization != 0) {
throw VmVirtError{virtualization};
}
throw VmError{Excno::cell_und, "failed to load library cell (no vm_state_interface available)"};
} else if (loaded_cell.data_cell->special_type() == DataCell::SpecialType::PrunnedBranch) {
CHECK(loaded_cell.virt.get_virtualization() == 0);
throw VmError{Excno::cell_und, "trying to load prunned cell"};
}
throw VmError{Excno::cell_und, "unexpected special cell"};
if (can_be_special) {
*can_be_special = loaded_cell.data_cell->is_special();
} else if (loaded_cell.data_cell->is_special()) {
if (loaded_cell.data_cell->special_type() == DataCell::SpecialType::Library) {
if (vm_state_interface) {
CellSlice cs(std::move(loaded_cell));
DCHECK(cs.size() == Cell::hash_bits + 8);
auto library_cell = vm_state_interface->load_library(cs.data_bits() + 8);
if (library_cell.not_null()) {
cell = library_cell;
can_be_special = nullptr;
continue;
}
throw VmError{Excno::cell_und, "failed to load library cell"};
}
throw VmError{Excno::cell_und, "failed to load library cell (no vm_state_interface available)"};
} else if (loaded_cell.data_cell->special_type() == DataCell::SpecialType::PrunnedBranch) {
CHECK(loaded_cell.virt.get_virtualization() == 0);
throw VmError{Excno::cell_und, "trying to load prunned cell"};
}
throw VmError{Excno::cell_und, "unexpected special cell"};
}
return loaded_cell;
}
return loaded_cell;
}
CellSlice load_cell_slice(const Ref<Cell>& cell) {

View file

@ -105,6 +105,43 @@ int exec_dump_value(VmState* st, unsigned arg) {
return 0;
}
int exec_dump_string(VmState* st) {
VM_LOG(st) << "execute STRDUMP";
if (!vm_debug_enabled) {
return 0;
}
Stack& stack = st->get_stack();
if (stack.depth() > 0){
auto cs = stack[0].as_slice();
if (cs.not_null()) { // wanted t_slice
auto size = cs->size();
if (size % 8 == 0) {
auto cnt = size / 8;
unsigned char tmp[128];
cs.write().fetch_bytes(tmp, cnt);
std::string s{tmp, tmp + cnt};
std::cerr << "#DEBUG#: " << s << std::endl;
}
else {
std::cerr << "#DEBUG#: slice contains not valid bits count" << std::endl;
}
} else {
std::cerr << "#DEBUG#: is not a slice" << std::endl;
}
} else {
std::cerr << "#DEBUG#: s0 is absent" << std::endl;
}
return 0;
}
void register_debug_ops(OpcodeTable& cp0) {
using namespace std::placeholders;
if (!vm_debug_enabled) {
@ -113,7 +150,9 @@ void register_debug_ops(OpcodeTable& cp0) {
} else {
// NB: all non-redefined opcodes in fe00..feff should be redirected to dummy debug definitions
cp0.insert(OpcodeInstr::mksimple(0xfe00, 16, "DUMPSTK", exec_dump_stack))
.insert(OpcodeInstr::mkfixedrange(0xfe01, 0xfe20, 16, 8, instr::dump_1c_and(0xff, "DEBUG "), exec_dummy_debug))
.insert(OpcodeInstr::mkfixedrange(0xfe01, 0xfe14, 16, 8, instr::dump_1c_and(0xff, "DEBUG "), exec_dummy_debug))
.insert(OpcodeInstr::mksimple(0xfe14, 16,"STRDUMP", exec_dump_string))
.insert(OpcodeInstr::mkfixedrange(0xfe15, 0xfe20, 16, 8, instr::dump_1c_and(0xff, "DEBUG "), exec_dummy_debug))
.insert(OpcodeInstr::mkfixed(0xfe2, 12, 4, instr::dump_1sr("DUMP"), exec_dump_value))
.insert(OpcodeInstr::mkfixedrange(0xfe30, 0xfef0, 16, 8, instr::dump_1c_and(0xff, "DEBUG "), exec_dummy_debug))
.insert(OpcodeInstr::mkext(0xfef, 12, 4, dump_dummy_debug_str, exec_dummy_debug_str, compute_len_debug_str));

View file

@ -313,7 +313,7 @@ td::Status LargeBocSerializer::serialize(td::FileFd& fd, int mode) {
return td::Status::Error("bag of cells is too large");
}
boc_writers::FileWriter writer{fd, info.total_size};
boc_writers::FileWriter writer{fd, (size_t) info.total_size};
auto store_ref = [&](unsigned long long value) {
writer.store_uint(value, info.ref_byte_size);
};

View file

@ -90,15 +90,27 @@ void StackEntry::dump(std::ostream& os) const {
os << dec_string(as_int());
break;
case t_cell:
os << "C{" << static_cast<Ref<Cell>>(ref)->get_hash().to_hex() << "}";
if (ref.not_null()) {
os << "C{" << static_cast<Ref<Cell>>(ref)->get_hash().to_hex() << "}";
} else {
os << "C{null}";
}
break;
case t_builder:
os << "BC{" << static_cast<Ref<CellBuilder>>(ref)->to_hex() << "}";
if (ref.not_null()) {
os << "BC{" << static_cast<Ref<CellBuilder>>(ref)->to_hex() << "}";
} else {
os << "BC{null}";
}
break;
case t_slice: {
os << "CS{";
static_cast<Ref<CellSlice>>(ref)->dump(os, 1, false);
os << '}';
if (ref.not_null()) {
os << "CS{";
static_cast<Ref<CellSlice>>(ref)->dump(os, 1, false);
os << '}';
} else {
os << "CS{null}";
}
break;
}
case t_string:

View file

@ -474,7 +474,7 @@ int exec_store_var_integer(VmState* st, int len_bits, bool sgnd, bool quiet) {
stack.check_underflow(2);
auto x = stack.pop_int();
auto cbr = stack.pop_builder();
unsigned len = ((x->bit_size(sgnd) + 7) >> 3);
unsigned len = (((unsigned)x->bit_size(sgnd) + 7) >> 3);
if (len >= (1u << len_bits)) {
throw VmError{Excno::range_chk};
}

View file

@ -503,7 +503,7 @@ int VmState::run() {
bool VmState::try_commit() {
if (cr.d[0].not_null() && cr.d[1].not_null() && cr.d[0]->get_depth() <= max_data_depth &&
cr.d[1]->get_depth() <= max_data_depth) {
cr.d[1]->get_depth() <= max_data_depth && cr.d[0]->get_level() == 0 && cr.d[1]->get_level() == 0) {
cstate.c4 = cr.d[0];
cstate.c5 = cr.d[1];
cstate.committed = true;