/*
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 "words.h"
#include "Dictionary.h"
#include "IntCtx.h"
#include "SourceLookup.h"
#include "common/refcnt.hpp"
#include "common/bigint.hpp"
#include "common/refint.h"
#include "common/bitstring.h"
#include "common/util.h"
#include "Ed25519.h"
#include "vm/cells.h"
#include "vm/cellslice.h"
#include "vm/continuation.h"
#include "vm/cp0.h"
#include "vm/dict.h"
#include "vm/boc.h"
#include "vm/box.hpp"
#include "vm/atom.h"
#include "block/block.h"
#include "td/utils/filesystem.h"
#include "td/utils/misc.h"
#include "td/utils/optional.h"
#include "td/utils/PathView.h"
#include "td/utils/port/thread.h"
#include "td/utils/port/Stat.h"
#include "td/utils/Timer.h"
#include "td/utils/tl_helpers.h"
#include "td/utils/crypto.h"
#include
namespace fift {
void show_total_cells(std::ostream& stream) {
stream << "total cells = " << vm::DataCell::get_total_data_cells() << std::endl;
}
void do_compile(vm::Stack& stack, Ref word_def);
void do_compile_literals(vm::Stack& stack, int count);
void interpret_dot(IntCtx& ctx, bool space_after) {
*ctx.output_stream << dec_string2(ctx.stack.pop_int()) << (space_after ? " " : "");
}
void interpret_dothex(IntCtx& ctx, bool upcase, bool space_after) {
*ctx.output_stream << hex_string(ctx.stack.pop_int(), upcase) << (space_after ? " " : "");
}
void interpret_dotbinary(IntCtx& ctx, bool space_after) {
*ctx.output_stream << binary_string(ctx.stack.pop_int()) << (space_after ? " " : "");
}
void interpret_dot_cellslice_rec(IntCtx& ctx) {
auto cs = ctx.stack.pop_cellslice();
cs->print_rec(*ctx.output_stream);
}
void interpret_dotstack(IntCtx& ctx) {
for (int i = ctx.stack.depth(); i > 0; i--) {
ctx.stack[i - 1].dump(*ctx.output_stream);
*ctx.output_stream << ' ';
}
*ctx.output_stream << std::endl;
}
void interpret_dotstack_list(IntCtx& ctx) {
for (int i = ctx.stack.depth(); i > 0; i--) {
ctx.stack[i - 1].print_list(*ctx.output_stream);
*ctx.output_stream << ' ';
}
*ctx.output_stream << std::endl;
}
void interpret_dump(IntCtx& ctx) {
ctx.stack.pop_chk().dump(*ctx.output_stream);
*ctx.output_stream << ' ';
}
void interpret_dump_internal(vm::Stack& stack) {
stack.push_string(stack.pop_chk().to_string());
}
void interpret_print_list(IntCtx& ctx) {
ctx.stack.pop_chk().print_list(*ctx.output_stream);
*ctx.output_stream << ' ';
}
void interpret_dottc(IntCtx& ctx) {
show_total_cells(*ctx.output_stream);
}
void interpret_dot_internal(vm::Stack& stack) {
stack.push_string(dec_string2(stack.pop_int()));
}
void interpret_dothex_internal(vm::Stack& stack, bool upcase) {
stack.push_string(hex_string(stack.pop_int(), upcase));
}
void interpret_dotbinary_internal(vm::Stack& stack) {
stack.push_string(binary_string(stack.pop_int()));
}
void interpret_plus(vm::Stack& stack) {
stack.push_int(stack.pop_int() + stack.pop_int());
}
void interpret_cond_dup(vm::Stack& stack) {
auto x = stack.pop_int();
if (x->sgn()) {
stack.push_int(x);
}
stack.push_int(std::move(x));
}
void interpret_plus_tiny(vm::Stack& stack, long long y) {
stack.push_int(stack.pop_int() + y);
}
void interpret_minus(vm::Stack& stack) {
auto y = stack.pop_int();
stack.push_int(stack.pop_int() - y);
}
void interpret_times(vm::Stack& stack) {
stack.push_int(stack.pop_int() * stack.pop_int());
}
void interpret_div(vm::Stack& stack, int round_mode) {
auto y = stack.pop_int();
stack.push_int(td::div(stack.pop_int(), y, round_mode));
}
void interpret_mod(vm::Stack& stack, int round_mode) {
auto y = stack.pop_int();
stack.push_int(td::mod(stack.pop_int(), y, round_mode));
}
void interpret_divmod(vm::Stack& stack, int round_mode) {
auto y = stack.pop_int();
auto dm = td::divmod(stack.pop_int(), std::move(y), round_mode);
stack.push_int(std::move(dm.first));
stack.push_int(std::move(dm.second));
}
void interpret_times_div(vm::Stack& stack, int round_mode) {
auto z = stack.pop_int();
auto y = stack.pop_int();
auto x = stack.pop_int();
td::BigIntG<257 * 2> tmp{0};
tmp.add_mul(*x, *y);
auto q = td::RefInt256{true};
tmp.mod_div(*z, q.unique_write(), round_mode);
q.unique_write().normalize();
stack.push_int(std::move(q));
}
void interpret_times_divmod(vm::Stack& stack, int round_mode) {
auto z = stack.pop_int();
auto y = stack.pop_int();
auto x = stack.pop_int();
td::BigIntG<257 * 2> tmp{0};
tmp.add_mul(*x, *y);
auto q = td::RefInt256{true};
tmp.mod_div(*z, q.unique_write(), round_mode);
q.unique_write().normalize();
auto r = td::RefInt256{true, tmp};
stack.push_int(std::move(q));
stack.push_int(std::move(r));
}
void interpret_times_mod(vm::Stack& stack, int round_mode) {
auto z = stack.pop_int();
auto y = stack.pop_int();
auto x = stack.pop_int();
td::BigIntG<257 * 2> tmp{0};
tmp.add_mul(*x, *y);
td::BigIntG<257 * 2> q;
tmp.mod_div(*z, q, round_mode);
auto r = td::RefInt256{true, tmp};
stack.push_int(std::move(r));
}
void interpret_negate(vm::Stack& stack) {
stack.push_int(-stack.pop_int());
}
void interpret_const(vm::Stack& stack, long long val) {
stack.push_smallint(val);
}
void interpret_big_const(vm::Stack& stack, td::RefInt256 val) {
stack.push_int(std::move(val));
}
void interpret_literal(vm::Stack& stack, vm::StackEntry se) {
stack.push(std::move(se));
}
void interpret_cmp(vm::Stack& stack, const char opt[3]) {
auto y = stack.pop_int();
auto x = stack.pop_int();
int r = x->cmp(*y);
assert((unsigned)(r + 1) <= 2);
stack.push_smallint(((const signed char*)opt)[r + 1]);
}
void interpret_sgn(vm::Stack& stack, const char opt[3]) {
auto x = stack.pop_int();
int r = x->sgn();
assert((unsigned)(r + 1) <= 2);
stack.push_smallint(((const signed char*)opt)[r + 1]);
}
void interpret_fits(vm::Stack& stack, bool sgnd) {
int n = stack.pop_smallint_range(1023);
auto x = stack.pop_int();
stack.push_bool(x->fits_bits(n, sgnd));
}
void interpret_pow2(vm::Stack& stack) {
int x = stack.pop_smallint_range(255);
auto r = td::RefInt256{true};
r.unique_write().set_pow2(x);
stack.push_int(r);
}
void interpret_neg_pow2(vm::Stack& stack) {
int x = stack.pop_smallint_range(256);
auto r = td::RefInt256{true};
r.unique_write().set_pow2(x).negate().normalize();
stack.push_int(r);
}
void interpret_pow2_minus1(vm::Stack& stack) {
int x = stack.pop_smallint_range(256);
auto r = td::RefInt256{true};
r.unique_write().set_pow2(x).add_tiny(-1).normalize();
stack.push_int(r);
}
void interpret_mod_pow2(vm::Stack& stack) {
int y = stack.pop_smallint_range(256);
auto x = stack.pop_int();
x.write().mod_pow2(y).normalize();
stack.push_int(x);
}
void interpret_lshift(vm::Stack& stack) {
int y = stack.pop_smallint_range(256);
stack.push_int(stack.pop_int() << y);
}
void interpret_rshift(vm::Stack& stack, int round_mode) {
int y = stack.pop_smallint_range(256);
stack.push_int(rshift(stack.pop_int(), y, round_mode));
}
void interpret_lshift_const(vm::Stack& stack, int y) {
stack.push_int(stack.pop_int() << y);
}
void interpret_rshift_const(vm::Stack& stack, int y) {
stack.push_int(stack.pop_int() >> y);
}
void interpret_times_rshift(vm::Stack& stack, int round_mode) {
int z = stack.pop_smallint_range(256);
auto y = stack.pop_int();
auto x = stack.pop_int();
td::BigIntG<257 * 2> tmp{0};
tmp.add_mul(*x, *y).rshift(z, round_mode).normalize();
auto q = td::RefInt256{true, tmp};
stack.push_int(std::move(q));
}
void interpret_lshift_div(vm::Stack& stack, int round_mode) {
int z = stack.pop_smallint_range(256);
auto y = stack.pop_int();
auto x = stack.pop_int();
td::BigIntG<257 * 2> tmp{*x};
tmp <<= z;
auto q = td::RefInt256{true};
tmp.mod_div(*y, q.unique_write(), round_mode);
q.unique_write().normalize();
stack.push_int(std::move(q));
}
void interpret_not(vm::Stack& stack) {
stack.push_int(~stack.pop_int());
}
void interpret_and(vm::Stack& stack) {
stack.push_int(stack.pop_int() & stack.pop_int());
}
void interpret_or(vm::Stack& stack) {
stack.push_int(stack.pop_int() | stack.pop_int());
}
void interpret_xor(vm::Stack& stack) {
stack.push_int(stack.pop_int() ^ stack.pop_int());
}
void interpret_has_type(vm::Stack& stack, int t) {
stack.push_bool(stack.pop_chk().type() == t);
}
void interpret_drop(vm::Stack& stack) {
stack.check_underflow(1);
stack.pop();
}
void interpret_2drop(vm::Stack& stack) {
stack.check_underflow(2);
stack.pop();
stack.pop();
}
void interpret_dup(vm::Stack& stack) {
stack.check_underflow(1);
stack.push(stack.fetch(0));
}
void interpret_2dup(vm::Stack& stack) {
stack.check_underflow(2);
stack.push(stack.fetch(1));
stack.push(stack.fetch(1));
}
void interpret_over(vm::Stack& stack) {
stack.check_underflow(2);
stack.push(stack.fetch(1));
}
void interpret_2over(vm::Stack& stack) {
stack.check_underflow(4);
stack.push(stack.fetch(3));
stack.push(stack.fetch(3));
}
void interpret_swap(vm::Stack& stack) {
stack.check_underflow(2);
swap(stack[0], stack[1]);
}
void interpret_2swap(vm::Stack& stack) {
stack.check_underflow(4);
swap(stack[0], stack[2]);
swap(stack[1], stack[3]);
}
void interpret_tuck(vm::Stack& stack) {
stack.check_underflow(2);
swap(stack[0], stack[1]);
stack.push(stack.fetch(1));
}
void interpret_nip(vm::Stack& stack) {
stack.check_underflow(2);
stack.pop(stack[1]);
}
void interpret_rot(vm::Stack& stack) {
stack.check_underflow(3);
swap(stack[1], stack[2]);
swap(stack[0], stack[1]);
}
void interpret_rot_rev(vm::Stack& stack) {
stack.check_underflow(3);
swap(stack[0], stack[1]);
swap(stack[1], stack[2]);
}
void interpret_pick(vm::Stack& stack) {
int n = stack.pop_smallint_range(255);
stack.check_underflow(n + 1);
stack.push(stack.fetch(n));
}
void interpret_roll(vm::Stack& stack) {
int n = stack.pop_smallint_range(255);
stack.check_underflow(n + 1);
for (int i = n; i > 0; i--) {
swap(stack[i], stack[i - 1]);
}
}
void interpret_roll_rev(vm::Stack& stack) {
int n = stack.pop_smallint_range(255);
stack.check_underflow(n + 1);
for (int i = 0; i < n; i++) {
swap(stack[i], stack[i + 1]);
}
}
void interpret_reverse(vm::Stack& stack) {
int m = stack.pop_smallint_range(255);
int n = stack.pop_smallint_range(255);
stack.check_underflow(n + m);
int s = 2 * m + n - 1;
for (int i = ((s - 1) >> 1); i >= m; i--) {
swap(stack[i], stack[s - i]);
}
}
void interpret_exch(vm::Stack& stack) {
int n = stack.pop_smallint_range(255);
stack.check_underflow(n + 1);
swap(stack[0], stack[n]);
}
void interpret_exch2(vm::Stack& stack) {
int n = stack.pop_smallint_range(255);
int m = stack.pop_smallint_range(255);
stack.check_underflow(std::max(m, n) + 1);
swap(stack[n], stack[m]);
}
void interpret_depth(vm::Stack& stack) {
stack.push_smallint(stack.depth());
}
void interpret_xchg0(vm::Stack& stack, int x) {
stack.check_underflow_p(x);
std::swap(stack.tos(), stack.at(x));
}
void interpret_xchg(vm::Stack& stack, int x, int y) {
stack.check_underflow_p(x, y);
std::swap(stack.at(x), stack.at(y));
}
void interpret_push(vm::Stack& stack, int x) {
stack.check_underflow_p(x);
stack.push(stack.fetch(x));
}
void interpret_pop(vm::Stack& stack, int x) {
stack.check_underflow_p(x);
std::swap(stack.tos(), stack.at(x));
stack.pop();
}
Ref dup_word_def{true, interpret_dup}, over_word_def{true, interpret_over},
drop_word_def{true, interpret_drop}, nip_word_def{true, interpret_nip}, swap_word_def{true, interpret_swap};
void interpret_make_xchg(vm::Stack& stack) {
using namespace std::placeholders;
int y = stack.pop_smallint_range(255), x = stack.pop_smallint_range(255);
if (x > y) {
std::swap(x, y);
}
if (x) {
stack.push_object(td::Ref{true, std::bind(interpret_xchg, _1, x, y)});
} else if (y <= 1) {
stack.push_object(y ? swap_word_def : Dictionary::nop_word_def);
} else {
stack.push_object(td::Ref{true, std::bind(interpret_xchg0, _1, y)});
}
}
void interpret_make_push(vm::Stack& stack) {
int x = stack.pop_smallint_range(255);
if (x <= 1) {
stack.push_object(x ? over_word_def : dup_word_def);
} else {
stack.push_object(td::Ref{true, std::bind(interpret_push, std::placeholders::_1, x)});
}
}
void interpret_make_pop(vm::Stack& stack) {
int x = stack.pop_smallint_range(255);
if (x <= 1) {
stack.push_object(x ? nip_word_def : drop_word_def);
} else {
stack.push_object(td::Ref{true, std::bind(interpret_pop, std::placeholders::_1, x)});
}
}
void interpret_is_string(vm::Stack& stack) {
stack.push_bool(stack.pop().type() == vm::StackEntry::t_string);
}
int make_utf8_char(char buffer[4], int x) {
if (x < -0x80) {
return 0;
} else if (x < 0x80) {
buffer[0] = (char)x;
return 1;
} else if (x < 0x800) {
buffer[0] = (char)(0xc0 + (x >> 6));
buffer[1] = (char)(0x80 + (x & 0x3f));
return 2;
} else if (x < 0x10000) {
buffer[0] = (char)(0xe0 + (x >> 12));
buffer[1] = (char)(0x80 + ((x >> 6) & 0x3f));
buffer[2] = (char)(0x80 + (x & 0x3f));
return 3;
} else if (x < 0x200000) {
buffer[0] = (char)(0xf0 + (x >> 18));
buffer[1] = (char)(0x80 + ((x >> 12) & 0x3f));
buffer[2] = (char)(0x80 + ((x >> 6) & 0x3f));
buffer[3] = (char)(0x80 + (x & 0x3f));
return 4;
} else {
return 0;
}
}
void interpret_chr(vm::Stack& stack) {
char buffer[8];
unsigned len = make_utf8_char(buffer, stack.pop_smallint_range(0x10ffff, -128));
stack.push_string(std::string{buffer, len});
}
void interpret_hold(vm::Stack& stack) {
stack.check_underflow(2);
char buffer[8];
unsigned len = make_utf8_char(buffer, stack.pop_smallint_range(0x10ffff, -128));
std::string s = stack.pop_string() + std::string{buffer, len};
stack.push_string(std::move(s));
}
void interpret_emit(IntCtx& ctx) {
char buffer[8];
buffer[make_utf8_char(buffer, ctx.stack.pop_smallint_range(0x10ffff, -128))] = 0;
*ctx.output_stream << buffer;
}
void interpret_emit_const(IntCtx& ctx, char c) {
*ctx.output_stream << c;
}
void interpret_type(IntCtx& ctx) {
std::string s = ctx.stack.pop_string();
*ctx.output_stream << s;
}
void interpret_str_concat(vm::Stack& stack) {
std::string t = stack.pop_string();
stack.push_string(stack.pop_string() + t);
}
void interpret_str_equal(vm::Stack& stack) {
stack.check_underflow(2);
std::string t = stack.pop_string(), s = stack.pop_string();
stack.push_bool(s == t);
}
void interpret_str_cmp(vm::Stack& stack) {
stack.check_underflow(2);
std::string t = stack.pop_string(), s = stack.pop_string();
int res = s.compare(std::move(t));
stack.push_smallint((res > 0) - (res < 0));
}
void interpret_str_len(vm::Stack& stack) {
stack.push_smallint((long long)stack.pop_string().size());
}
void interpret_str_split(vm::Stack& stack) {
stack.check_underflow(2);
unsigned sz = stack.pop_smallint_range(0x7fffffff);
std::string str = stack.pop_string();
if (sz > str.size()) {
throw IntError{"not enough bytes for cutting"};
}
stack.push_string(std::string{str, 0, sz});
stack.push_string(std::string{str, sz});
}
void interpret_str_reverse(vm::Stack& stack) {
std::string s = stack.pop_string();
auto it = s.begin();
while (it < s.end()) {
if ((*it & 0xc0) != 0xc0) {
++it;
} else {
auto it0 = it++;
while (it < s.end() && (*it & 0xc0) == 0x80) {
++it;
}
std::reverse(it0, it);
}
}
std::reverse(s.begin(), s.end());
stack.push_string(std::move(s));
}
void interpret_str_remove_trailing_int(vm::Stack& stack, int arg) {
char x = (char)(arg ? arg : stack.pop_long_range(127));
std::string s = stack.pop_string();
s.resize(s.find_last_not_of(x) + 1); // if not found, this expression will be 0
stack.push_string(std::move(s));
}
void interpret_bytes_len(vm::Stack& stack) {
stack.push_smallint((long long)stack.pop_bytes().size());
}
const char hex_digits[] = "0123456789abcdef";
const char HEX_digits[] = "0123456789ABCDEF";
static inline const char* hex_digits_table(bool upcase) {
return upcase ? HEX_digits : hex_digits;
}
void interpret_bytes_hex_print_raw(IntCtx& ctx, bool upcase) {
auto hex_digits = hex_digits_table(upcase);
std::string str = ctx.stack.pop_bytes();
for (unsigned c : str) {
*ctx.output_stream << hex_digits[(c >> 4) & 15] << hex_digits[c & 15];
}
}
void interpret_bytes_to_hex(vm::Stack& stack, bool upcase) {
auto hex_digits = hex_digits_table(upcase);
std::string str = stack.pop_bytes();
std::string t(str.size() * 2, 0);
for (std::size_t i = 0; i < str.size(); i++) {
unsigned c = str[i];
t[2 * i] = hex_digits[(c >> 4) & 15];
t[2 * i + 1] = hex_digits[c & 15];
}
stack.push_string(std::move(t));
}
void interpret_hex_to_bytes(vm::Stack& stack, bool partial) {
std::string str = stack.pop_string(), t;
if (!partial) {
if (str.size() & 1) {
throw IntError{"not a hex string"};
}
t.reserve(str.size() >> 1);
}
std::size_t i;
unsigned f = 0;
for (i = 0; i < str.size(); i++) {
int c = str[i];
if (c >= '0' && c <= '9') {
c -= '0';
} else {
c |= 0x20;
if (c >= 'a' && c <= 'f') {
c -= 'a' - 10;
} else {
if (!partial) {
throw IntError{"not a hex string"};
}
break;
}
}
f = (f << 4) + c;
if (i & 1) {
t += (char)(f & 0xff);
}
}
stack.push_bytes(t);
if (partial) {
stack.push_smallint(i & -2);
}
}
void interpret_bytes_split(vm::Stack& stack) {
stack.check_underflow(2);
unsigned sz = stack.pop_smallint_range(0x7fffffff);
std::string str = stack.pop_bytes();
if (sz > str.size()) {
throw IntError{"not enough bytes for cutting"};
}
stack.push_bytes(std::string{str, 0, sz});
stack.push_bytes(std::string{str, sz});
}
void interpret_bytes_concat(vm::Stack& stack) {
std::string t = stack.pop_bytes();
stack.push_bytes(stack.pop_bytes() + t);
}
void interpret_bytes_equal(vm::Stack& stack) {
stack.check_underflow(2);
std::string t = stack.pop_bytes(), s = stack.pop_bytes();
stack.push_bool(s == t);
}
void interpret_bytes_cmp(vm::Stack& stack) {
stack.check_underflow(2);
std::string t = stack.pop_bytes(), s = stack.pop_bytes();
int res = s.compare(std::move(t));
stack.push_smallint((res > 0) - (res < 0));
}
void interpret_bytes_fetch_int(vm::Stack& stack, int mode) {
stack.check_underflow(2);
unsigned bits = (unsigned)stack.pop_smallint_range(256 + (mode & 1));
std::string str = stack.pop_bytes();
if ((bits & 7)) {
throw IntError{"can load only an integer number of bytes"};
}
unsigned sz = bits >> 3;
if (str.size() < sz) {
throw IntError{"not enough bytes in the source"};
}
td::RefInt256 x{true};
bool ok;
const unsigned char* ptr = (const unsigned char*)(str.data());
if (!(mode & 0x10)) {
ok = x.write().import_bytes(ptr, sz, mode & 1);
} else {
ok = x.write().import_bytes_lsb(ptr, sz, mode & 1);
}
if (!ok) {
throw IntError{"cannot load integer"};
}
if (mode & 2) {
stack.push_bytes(std::string{str, sz});
}
stack.push_int(std::move(x));
}
void interpret_int_to_bytes(vm::Stack& stack, bool sgnd, bool lsb) {
stack.check_underflow(2);
unsigned bits = (unsigned)stack.pop_smallint_range(sgnd ? 264 : 256, 1);
td::RefInt256 x = stack.pop_int();
if ((bits & 7)) {
throw IntError{"can store only an integer number of bytes"};
}
unsigned sz = bits >> 3;
unsigned char buffer[33];
if (!(lsb ? x->export_bytes_lsb(buffer, sz, sgnd) : x->export_bytes(buffer, sz, sgnd))) {
throw IntError{"cannot store integer"};
}
stack.push_bytes(std::string{(char*)buffer, sz});
}
void interpret_string_to_bytes(vm::Stack& stack) {
stack.push_bytes(stack.pop_string());
}
void interpret_bytes_hash(vm::Stack& stack) {
std::string str = stack.pop_bytes();
unsigned char buffer[32];
digest::hash_str(buffer, str.c_str(), str.size());
td::RefInt256 x{true};
x.write().import_bytes(buffer, 32, false);
stack.push_int(std::move(x));
}
void interpret_empty(vm::Stack& stack) {
stack.push(td::Ref{true});
}
void interpret_store(vm::Stack& stack, bool sgnd) {
stack.check_underflow(3);
int bits = stack.pop_smallint_range(1023);
auto x = stack.pop_int();
auto cell = stack.pop_builder();
if (!cell.write().store_int256_bool(*x, bits, sgnd)) {
throw IntError{"integer does not fit into cell"};
}
stack.push(cell);
}
void interpret_store_str(vm::Stack& stack) {
stack.check_underflow(2);
auto str = stack.pop_string();
auto cell = stack.pop_builder();
cell.write().store_bytes(str); // may throw CellWriteError
stack.push(cell);
}
void interpret_store_bytes(vm::Stack& stack) {
stack.check_underflow(2);
auto str = stack.pop_bytes();
auto cell = stack.pop_builder();
cell.write().store_bytes(str); // may throw CellWriteError
stack.push(cell);
}
void interpret_string_to_cellslice(vm::Stack& stack) {
auto str = stack.pop_string();
vm::CellBuilder cb;
cb.store_bytes(str); // may throw CellWriteError
stack.push_cellslice(td::Ref{true, cb.finalize()});
}
void interpret_store_cellslice(vm::Stack& stack) {
stack.check_underflow(2);
auto cs = stack.pop_cellslice();
auto cb = stack.pop_builder();
vm::cell_builder_add_slice(cb.write(), *cs);
stack.push(std::move(cb));
}
void interpret_store_cellslice_ref(vm::Stack& stack) {
stack.check_underflow(2);
auto cs = stack.pop_cellslice();
vm::CellBuilder cs_cell_builder;
vm::cell_builder_add_slice(cs_cell_builder, *cs);
auto cb = stack.pop_builder();
if (!cb.write().store_ref_bool(cs_cell_builder.finalize())) {
throw IntError{"cell reference list overflow"};
}
stack.push(std::move(cb));
}
void interpret_concat_cellslice(vm::Stack& stack) {
stack.check_underflow(2);
auto cs2 = stack.pop_cellslice();
auto cs1 = stack.pop_cellslice();
vm::CellBuilder cb;
vm::cell_builder_add_slice(cb, *cs1);
vm::cell_builder_add_slice(cb, *cs2);
stack.push_cellslice(td::Ref{true, cb.finalize()});
}
void interpret_concat_cellslice_ref(vm::Stack& stack) {
stack.check_underflow(2);
auto cs2 = stack.pop_cellslice();
auto cs1 = stack.pop_cellslice();
vm::CellBuilder builder1, builder2;
vm::cell_builder_add_slice(builder1, *cs1);
vm::cell_builder_add_slice(builder2, *cs2);
if (!builder1.store_ref_bool(builder2.finalize())) {
throw IntError{"cell reference list overflow"};
}
stack.push_cellslice(td::Ref{true, builder1.finalize()});
}
void interpret_concat_builders(vm::Stack& stack) {
stack.check_underflow(2);
auto cb2 = stack.pop_builder();
auto cb1 = stack.pop_builder();
cb1.write().append_builder(std::move(cb2));
stack.push_builder(std::move(cb1));
}
void interpret_slice_bitrefs(vm::Stack& stack, int mode) {
auto cs = stack.pop_cellslice();
if (mode & 1) {
stack.push_smallint(cs->size());
}
if (mode & 2) {
stack.push_smallint(cs->size_refs());
}
}
void interpret_builder_bitrefs(vm::Stack& stack, int mode) {
auto cb = stack.pop_builder();
if (mode & 1) {
stack.push_smallint(cb->size());
}
if (mode & 2) {
stack.push_smallint(cb->size_refs());
}
}
void interpret_builder_remaining_bitrefs(vm::Stack& stack, int mode) {
auto cb = stack.pop_builder();
if (mode & 1) {
stack.push_smallint(cb->remaining_bits());
}
if (mode & 2) {
stack.push_smallint(cb->remaining_refs());
}
}
void interpret_cell_hash(vm::Stack& stack) {
auto cell = stack.pop_cell();
td::RefInt256 hash{true};
hash.write().import_bytes(cell->get_hash().as_slice().ubegin(), 32, false);
stack.push_int(std::move(hash));
}
void interpret_store_ref(vm::Stack& stack) {
auto ref = stack.pop_cell();
auto cb = stack.pop_builder();
if (!cb.write().store_ref_bool(ref)) {
throw IntError{"cell reference list overflow"};
}
stack.push(std::move(cb));
}
void interpret_store_end(vm::Stack& stack, bool special) {
auto cell = stack.pop_builder()->finalize_copy(special);
if (cell.is_null()) {
throw IntError{"invalid special cell constructed"};
}
stack.push_cell(std::move(cell));
}
void interpret_from_cell(vm::Stack& stack) {
auto cell = stack.pop_cell();
Ref cs{true};
if (!cs.unique_write().load(vm::NoVmOrd(), std::move(cell))) {
throw IntError{"deserializing a special cell as ordinary"};
}
stack.push(cs);
}
// cs n -- cs' x
// cs n -- cs' x -1 OR cs' 0
// mode & 1 : signed
// mode & 2 : advance position
// mode & 4 : return error on stack
void interpret_fetch(vm::Stack& stack, int mode) {
auto n = stack.pop_smallint_range(256 + (mode & 1));
auto cs = stack.pop_cellslice();
if (!cs->have(n)) {
stack.push(std::move(cs));
stack.push_bool(false);
if (!(mode & 4)) {
throw IntError{"end of data while reading integer from cell"};
}
} else {
if (mode & 2) {
stack.push_int(cs.write().fetch_int256(n, mode & 1));
stack.push(std::move(cs));
} else {
stack.push_int(cs->prefetch_int256(n, mode & 1));
}
if (mode & 4) {
stack.push_bool(true);
}
}
}
// mode & 1 : return result as bytes (instead of string)
// mode & 2 : advance position
// mode & 4 : return error on stack
void interpret_fetch_bytes(vm::Stack& stack, int mode) {
unsigned n = stack.pop_smallint_range(127);
auto cs = stack.pop_cellslice();
if (!cs->have(n * 8)) {
stack.push(std::move(cs));
stack.push_bool(false);
if (!(mode & 4)) {
throw IntError{"end of data while reading byte string from cell"};
}
} else {
// unfortunately, std::string's data() is writeable only in C++17
unsigned char tmp[128];
if (mode & 2) {
cs.write().fetch_bytes(tmp, n);
} else {
cs.write().prefetch_bytes(tmp, n);
}
std::string s{tmp, tmp + n};
if (mode & 1) {
stack.push_bytes(std::move(s));
} else {
stack.push_string(std::move(s));
}
stack.push(std::move(cs));
if (mode & 4) {
stack.push_bool(true);
}
}
}
void interpret_cell_empty(vm::Stack& stack) {
auto cs = stack.pop_cellslice();
stack.push_bool(cs->empty_ext());
}
void interpret_cell_check_empty(vm::Stack& stack) {
auto cs = stack.pop_cellslice();
if (!cs->empty_ext()) {
throw IntError{"cell slice not empty"};
}
}
void interpret_cell_remaining(vm::Stack& stack) {
auto cs = stack.pop_cellslice();
stack.push_smallint(cs->size());
stack.push_smallint(cs->size_refs());
}
// mode & 1 : return result as slice (instead of cell)
// mode & 2 : advance position
// mode & 4 : return error on stack
void interpret_fetch_ref(vm::Stack& stack, int mode) {
auto cs = stack.pop_cellslice();
if (!cs->have_refs(1)) {
stack.push(std::move(cs));
stack.push_bool(false);
if (!(mode & 4)) {
throw IntError{"end of data while reading reference from cell"};
}
} else {
auto cell = (mode & 2) ? cs.write().fetch_ref() : cs.write().prefetch_ref();
if (mode & 2) {
stack.push(std::move(cs));
}
if (mode & 1) {
Ref new_cs{true, vm::NoVm(), std::move(cell)};
stack.push(std::move(new_cs));
} else {
stack.push_cell(std::move(cell));
}
if (mode & 4) {
stack.push_bool(true);
}
}
}
// Box create/fetch/store operations
void interpret_hole(vm::Stack& stack) {
stack.push_box(Ref{true});
}
void interpret_box(vm::Stack& stack) {
stack.push_box(Ref{true, stack.pop_chk()});
}
void interpret_box_fetch(vm::Stack& stack) {
stack.push(stack.pop_box()->get());
}
void interpret_box_store(vm::Stack& stack) {
stack.check_underflow(2);
auto box = stack.pop_box();
box->set(stack.pop());
}
void interpret_push_null(vm::Stack& stack) {
stack.push({});
}
void interpret_is_null(vm::Stack& stack) {
stack.push_bool(stack.pop_chk().empty());
}
// Tuple/array operations
void interpret_empty_tuple(vm::Stack& stack) {
stack.push_tuple(Ref{true});
}
void interpret_is_tuple(vm::Stack& stack) {
stack.push_bool(stack.pop_chk().type() == vm::StackEntry::t_tuple);
}
void interpret_tuple_push(vm::Stack& stack) {
stack.check_underflow(2);
auto val = stack.pop();
auto tuple = stack.pop_tuple();
tuple.write().emplace_back(std::move(val));
stack.push_tuple(std::move(tuple));
}
void interpret_tuple_pop(vm::Stack& stack) {
auto tuple = stack.pop_tuple();
if (tuple->empty()) {
throw IntError{"empty tuple"};
}
auto val = tuple->back();
tuple.write().pop_back();
stack.push_tuple(std::move(tuple));
stack.push(std::move(val));
}
void interpret_tuple_len(vm::Stack& stack) {
stack.push_smallint(stack.pop_tuple()->size());
}
void interpret_tuple_index(vm::Stack& stack) {
auto idx = stack.pop_long_range(std::numeric_limits::max());
auto tuple = stack.pop_tuple();
if ((td::uint64)idx >= tuple->size()) {
throw vm::VmError{vm::Excno::range_chk, "array index out of range"};
}
stack.push((*tuple)[td::narrow_cast(idx)]);
}
void interpret_tuple_set(vm::Stack& stack) {
auto idx = stack.pop_long_range(std::numeric_limits::max());
auto val = stack.pop_chk();
auto tuple = stack.pop_tuple();
if ((td::uint64)idx >= tuple->size()) {
throw vm::VmError{vm::Excno::range_chk, "array index out of range"};
}
tuple.write()[td::narrow_cast(idx)] = std::move(val);
stack.push_tuple(std::move(tuple));
}
void interpret_make_tuple(vm::Stack& stack) {
int n = stack.pop_smallint_range(255);
stack.check_underflow(n);
Ref ref{true};
auto& tuple = ref.unique_write();
tuple.reserve(n);
for (int i = n - 1; i >= 0; i--) {
tuple.push_back(std::move(stack[i]));
}
stack.pop_many(n);
stack.push_tuple(std::move(ref));
}
void interpret_tuple_explode(vm::Stack& stack, bool pop_count) {
std::size_t n = pop_count ? (unsigned)stack.pop_smallint_range(255) : 0;
auto ref = stack.pop_tuple();
const auto& tuple = *ref;
if (!pop_count) {
n = tuple.size();
if (n > 255) {
throw IntError{"tuple too large to be exploded"};
}
} else if (tuple.size() != n) {
throw IntError{"tuple size mismatch"};
}
if (ref.is_unique()) {
auto& tuplew = ref.unique_write();
for (auto& entry : tuplew) {
stack.push(std::move(entry));
}
} else {
for (const auto& entry : tuple) {
stack.push(entry);
}
}
if (!pop_count) {
stack.push_smallint((td::int32)n);
}
}
void interpret_allot(vm::Stack& stack) {
auto n = stack.pop_long_range(0xffffffff);
Ref ref{true};
auto& tuple = ref.unique_write();
tuple.reserve(td::narrow_cast(n));
while (n-- > 0) {
tuple.emplace_back(Ref{true});
}
stack.push(std::move(ref));
}
// Atoms
void interpret_atom(vm::Stack& stack) {
bool create = stack.pop_bool();
auto atom = vm::Atom::find(stack.pop_string(), create);
if (atom.is_null()) {
stack.push_bool(false);
} else {
stack.push_atom(std::move(atom));
stack.push_bool(true);
}
}
void interpret_atom_name(vm::Stack& stack) {
stack.push_string(stack.pop_atom()->name_ext());
}
void interpret_atom_anon(vm::Stack& stack) {
stack.push_atom(vm::Atom::anon());
}
void interpret_is_atom(vm::Stack& stack) {
stack.push_bool(stack.pop().is_atom());
}
bool are_eqv(vm::StackEntry x, vm::StackEntry y) {
if (x.type() != y.type()) {
return false;
}
switch (x.type()) {
case vm::StackEntry::t_null:
return true;
case vm::StackEntry::t_atom:
return std::move(x).as_atom() == std::move(y).as_atom();
case vm::StackEntry::t_int:
return !td::cmp(std::move(x).as_int(), std::move(y).as_int());
case vm::StackEntry::t_string:
return std::move(x).as_string() == std::move(y).as_string();
default:
return false;
}
}
void interpret_is_eqv(vm::Stack& stack) {
auto y = stack.pop(), x = stack.pop();
stack.push_bool(are_eqv(std::move(x), std::move(y)));
}
void interpret_is_eq(vm::Stack& stack) {
auto y = stack.pop(), x = stack.pop();
stack.push_bool(x == y);
}
// BoC (de)serialization
void interpret_boc_serialize(vm::Stack& stack) {
vm::BagOfCells boc;
boc.add_root(stack.pop_cell());
auto res = boc.import_cells();
if (res.is_error()) {
throw IntError{(PSLICE() << "cannot serialize bag-of-cells " << res.error()).c_str()};
}
stack.push_bytes(boc.serialize_to_string());
}
void interpret_boc_serialize_ext(vm::Stack& stack) {
int mode = stack.pop_smallint_range(vm::BagOfCells::Mode::max);
vm::BagOfCells boc;
boc.add_root(stack.pop_cell());
auto res = boc.import_cells();
if (res.is_error()) {
throw IntError{(PSLICE() << "cannot serialize bag-of-cells " << res.error()).c_str()};
}
stack.push_bytes(boc.serialize_to_string(mode));
}
void interpret_boc_deserialize(vm::Stack& stack) {
std::string bytes = stack.pop_bytes();
vm::BagOfCells boc;
auto res = boc.deserialize(td::Slice{bytes});
if (res.is_error()) {
throw IntError{(PSLICE() << "cannot deserialize bag-of-cells " << res.error()).c_str()};
}
if (res.ok() <= 0 || boc.get_root_cell().is_null()) {
throw IntError{"cannot deserialize bag-of-cells "};
}
stack.push_cell(boc.get_root_cell());
}
void interpret_read_file(IntCtx& ctx) {
std::string filename = ctx.stack.pop_string();
auto r_data = ctx.source_lookup->read_file(filename);
if (r_data.is_error()) {
throw IntError{PSTRING() << "error reading file `" << filename << "`: " << r_data.error()};
}
ctx.stack.push_bytes(r_data.move_as_ok().data);
}
void interpret_read_file_part(IntCtx& ctx) {
auto size = ctx.stack.pop_long_range(std::numeric_limits::max());
auto offset = ctx.stack.pop_long_range(std::numeric_limits::max());
std::string filename = ctx.stack.pop_string();
auto r_data = ctx.source_lookup->read_file_part(filename, size, offset);
if (r_data.is_error()) {
throw IntError{PSTRING() << "error reading file `" << filename << "`: " << r_data.error()};
}
ctx.stack.push_bytes(r_data.move_as_ok().data);
}
void interpret_write_file(IntCtx& ctx) {
std::string filename = ctx.stack.pop_string();
std::string str = ctx.stack.pop_bytes();
auto status = ctx.source_lookup->write_file(filename, str);
if (status.is_error()) {
throw IntError{PSTRING() << "error writing file `" << filename << "`: " << status.error()};
}
}
void interpret_file_exists(IntCtx& ctx) {
std::string filename = ctx.stack.pop_string();
auto res = ctx.source_lookup->is_file_exists(filename);
ctx.stack.push_bool(res);
}
// custom and crypto
void interpret_now(vm::Stack& stack) {
stack.push_smallint(std::time(nullptr));
}
void interpret_new_keypair(vm::Stack& stack) {
auto priv_key = td::Ed25519::generate_private_key();
if (!priv_key.is_ok()) {
throw fift::IntError{priv_key.error().to_string()};
}
auto pub_key = priv_key.ok().get_public_key();
if (!pub_key.is_ok()) {
throw fift::IntError{pub_key.error().to_string()};
}
stack.push_bytes(priv_key.ok().as_octet_string());
stack.push_bytes(pub_key.ok().as_octet_string());
}
void interpret_priv_key_to_pub(vm::Stack& stack) {
std::string str = stack.pop_bytes();
if (str.size() != 32) {
throw IntError{"Ed25519 private key must be exactly 32 bytes long"};
}
td::Ed25519::PrivateKey priv_key{td::SecureString{str}};
auto pub_key = priv_key.get_public_key();
if (!pub_key.is_ok()) {
throw fift::IntError{pub_key.error().to_string()};
}
stack.push_bytes(pub_key.ok().as_octet_string());
}
void interpret_ed25519_sign(vm::Stack& stack) {
stack.check_underflow(2);
std::string key = stack.pop_bytes(), data = stack.pop_bytes();
if (key.size() != 32) {
throw IntError{"Ed25519 private key must be exactly 32 bytes long"};
}
td::Ed25519::PrivateKey priv_key{td::SecureString{key}};
auto signature = priv_key.sign(td::Slice{data});
if (!signature.is_ok()) {
throw fift::IntError{signature.error().to_string()};
}
stack.push_bytes(signature.move_as_ok());
}
void interpret_ed25519_sign_uint(vm::Stack& stack) {
stack.check_underflow(2);
std::string key = stack.pop_bytes();
td::RefInt256 data_int = stack.pop_int();
if (key.size() != 32) {
throw IntError{"Ed25519 private key must be exactly 32 bytes long"};
}
unsigned char data[32];
if (!data_int->export_bytes(data, 32, false)) {
throw IntError{"Ed25519 data to be signed must fit into 256 bits"};
}
td::Ed25519::PrivateKey priv_key{td::SecureString{key}};
auto signature = priv_key.sign(td::Slice{data, 32});
if (!signature.is_ok()) {
throw fift::IntError{signature.error().to_string()};
}
stack.push_bytes(signature.move_as_ok());
}
void interpret_ed25519_chksign(vm::Stack& stack) {
stack.check_underflow(3);
std::string key = stack.pop_bytes(), signature = stack.pop_bytes(), data = stack.pop_bytes();
if (key.size() != 32) {
throw IntError{"Ed25519 public key must be exactly 32 bytes long"};
}
if (signature.size() != 64) {
throw IntError{"Ed25519 signature must be exactly 64 bytes long"};
}
td::Ed25519::PublicKey pub_key{td::SecureString{key}};
auto res = pub_key.verify_signature(td::Slice{data}, td::Slice{signature});
stack.push_bool(res.is_ok());
}
void interpret_crc16(vm::Stack& stack) {
std::string str = stack.pop_bytes();
stack.push_smallint(td::crc16(td::Slice{str}));
}
void interpret_crc32(vm::Stack& stack) {
std::string str = stack.pop_bytes();
stack.push_smallint(td::crc32(td::Slice{str}));
}
void interpret_crc32c(vm::Stack& stack) {
std::string str = stack.pop_bytes();
stack.push_smallint(td::crc32c(td::Slice{str}));
}
// vm dictionaries
void interpret_dict_new(vm::Stack& stack) {
stack.push({});
}
void interpret_dict_to_slice(vm::Stack& stack) {
vm::CellBuilder cb;
cb.store_maybe_ref(stack.pop_maybe_cell());
stack.push_cellslice(vm::load_cell_slice_ref(cb.finalize()));
}
void interpret_load_dict(vm::Stack& stack, bool fetch) {
auto cs = stack.pop_cellslice();
Ref dict;
bool non_empty;
if (!(cs.write().fetch_bool_to(non_empty) && (!non_empty || cs.write().fetch_ref_to(dict)))) {
throw IntError{"cell underflow"};
}
stack.push_maybe_cell(std::move(dict));
if (fetch) {
stack.push_cellslice(std::move(cs));
}
}
void interpret_store_dict(vm::Stack& stack) {
auto cell = stack.pop_maybe_cell();
auto cb = stack.pop_builder();
if (!cb.write().store_maybe_ref(std::move(cell))) {
throw IntError{"cell overflow"};
}
stack.push_builder(std::move(cb));
}
// val key dict keylen -- dict' ?
void interpret_dict_add_u(vm::Stack& stack, vm::Dictionary::SetMode mode, bool add_builder, bool sgnd) {
int n = stack.pop_smallint_range(vm::Dictionary::max_key_bits);
vm::Dictionary dict{stack.pop_maybe_cell(), n};
unsigned char buffer[vm::Dictionary::max_key_bytes];
vm::BitSlice key = dict.integer_key(stack.pop_int(), n, sgnd, buffer);
if (!key.is_valid()) {
throw IntError{"not enough bits for a dictionary key"};
}
bool res;
if (add_builder) {
res = dict.set_builder(std::move(key), stack.pop_builder(), mode);
} else {
res = dict.set(std::move(key), stack.pop_cellslice(), mode);
}
stack.push_maybe_cell(std::move(dict).extract_root_cell());
stack.push_bool(res);
}
void interpret_dict_get_u(vm::Stack& stack, bool sgnd) {
int n = stack.pop_smallint_range(vm::Dictionary::max_key_bits);
vm::Dictionary dict{stack.pop_maybe_cell(), n};
unsigned char buffer[vm::Dictionary::max_key_bytes];
vm::BitSlice key = dict.integer_key(stack.pop_int(), n, sgnd, buffer);
if (!key.is_valid()) {
throw IntError{"not enough bits for a dictionary key"};
}
auto res = dict.lookup(std::move(key));
if (res.not_null()) {
stack.push_cellslice(std::move(res));
stack.push_bool(true);
} else {
stack.push_bool(false);
}
}
void interpret_dict_map(IntCtx& ctx) {
auto func = pop_exec_token(ctx);
int n = ctx.stack.pop_smallint_range(vm::Dictionary::max_key_bits);
vm::Dictionary dict{ctx.stack.pop_maybe_cell(), n};
vm::Dictionary::simple_map_func_t simple_map = [&ctx, func](vm::CellBuilder& cb, Ref cs_ref) -> bool {
ctx.stack.push_builder(Ref(cb));
ctx.stack.push_cellslice(std::move(cs_ref));
func->run(ctx);
assert(cb.is_unique());
if (!ctx.stack.pop_bool()) {
return false;
}
Ref cb_ref = ctx.stack.pop_builder();
cb = *cb_ref;
return true;
};
dict.map(std::move(simple_map));
ctx.stack.push_maybe_cell(std::move(dict).extract_root_cell());
}
void interpret_dict_map_ext(IntCtx& ctx) {
auto func = pop_exec_token(ctx);
int n = ctx.stack.pop_smallint_range(vm::Dictionary::max_key_bits);
vm::Dictionary dict{ctx.stack.pop_maybe_cell(), n};
vm::Dictionary::map_func_t map_func = [&ctx, func](vm::CellBuilder& cb, Ref cs_ref,
td::ConstBitPtr key, int key_len) -> bool {
ctx.stack.push_builder(Ref(cb));
td::RefInt256 x{true};
x.unique_write().import_bits(key, key_len, false);
ctx.stack.push_int(std::move(x));
ctx.stack.push_cellslice(std::move(cs_ref));
func->run(ctx);
assert(cb.is_unique());
if (!ctx.stack.pop_bool()) {
return false;
}
Ref cb_ref = ctx.stack.pop_builder();
cb = *cb_ref;
return true;
};
dict.map(std::move(map_func));
ctx.stack.push_maybe_cell(std::move(dict).extract_root_cell());
}
void interpret_dict_foreach(IntCtx& ctx) {
auto func = pop_exec_token(ctx);
int n = ctx.stack.pop_smallint_range(vm::Dictionary::max_key_bits);
vm::Dictionary dict{ctx.stack.pop_maybe_cell(), n};
vm::Dictionary::foreach_func_t foreach_func = [&ctx, func](Ref cs_ref, td::ConstBitPtr key,
int key_len) -> bool {
td::RefInt256 x{true};
x.unique_write().import_bits(key, key_len, false);
ctx.stack.push_int(std::move(x));
ctx.stack.push_cellslice(std::move(cs_ref));
func->run(ctx);
return ctx.stack.pop_bool();
};
ctx.stack.push_bool(dict.check_for_each(std::move(foreach_func)));
}
void interpret_dict_merge(IntCtx& ctx) {
auto func = pop_exec_token(ctx);
int n = ctx.stack.pop_smallint_range(vm::Dictionary::max_key_bits);
vm::Dictionary dict2{ctx.stack.pop_maybe_cell(), n};
vm::Dictionary dict1{ctx.stack.pop_maybe_cell(), n};
vm::Dictionary::simple_combine_func_t simple_combine = [&ctx, func](vm::CellBuilder& cb, Ref cs1_ref,
Ref cs2_ref) -> bool {
ctx.stack.push_builder(Ref(cb));
ctx.stack.push_cellslice(std::move(cs1_ref));
ctx.stack.push_cellslice(std::move(cs2_ref));
func->run(ctx);
assert(cb.is_unique());
if (!ctx.stack.pop_bool()) {
return false;
}
Ref cb_ref = ctx.stack.pop_builder();
cb = *cb_ref;
return true;
};
if (!dict1.combine_with(dict2, std::move(simple_combine))) {
throw IntError{"cannot combine dictionaries"};
}
ctx.stack.push_maybe_cell(std::move(dict1).extract_root_cell());
}
void interpret_dict_diff(IntCtx& ctx) {
auto func = pop_exec_token(ctx);
int n = ctx.stack.pop_smallint_range(vm::Dictionary::max_key_bits);
vm::Dictionary dict2{ctx.stack.pop_maybe_cell(), n};
vm::Dictionary dict1{ctx.stack.pop_maybe_cell(), n};
vm::Dictionary::scan_diff_func_t scan_value_pair =
[&ctx, func](td::ConstBitPtr key, int key_len, Ref cs1_ref, Ref cs2_ref) -> bool {
td::RefInt256 x{true};
x.unique_write().import_bits(key, key_len, false);
ctx.stack.push_int(std::move(x));
ctx.stack.push_maybe_cellslice(std::move(cs1_ref));
ctx.stack.push_maybe_cellslice(std::move(cs2_ref));
func->run(ctx);
return ctx.stack.pop_bool();
};
ctx.stack.push_bool(dict1.scan_diff(dict2, std::move(scan_value_pair)));
}
void interpret_pfx_dict_add(vm::Stack& stack, vm::Dictionary::SetMode mode, bool add_builder) {
int n = stack.pop_smallint_range(vm::Dictionary::max_key_bits);
vm::PrefixDictionary dict{stack.pop_maybe_cell(), n};
auto cs = stack.pop_cellslice();
bool res;
if (add_builder) {
res = dict.set_builder(cs->data_bits(), cs->size(), stack.pop_builder(), mode);
} else {
res = dict.set(cs->data_bits(), cs->size(), stack.pop_cellslice(), mode);
}
stack.push_maybe_cell(std::move(dict).extract_root_cell());
stack.push_bool(res);
}
void interpret_pfx_dict_get(vm::Stack& stack) {
int n = stack.pop_smallint_range(vm::Dictionary::max_key_bits);
vm::PrefixDictionary dict{stack.pop_maybe_cell(), n};
auto cs = stack.pop_cellslice();
auto res = dict.lookup(cs->data_bits(), cs->size());
if (res.not_null()) {
stack.push_cellslice(std::move(res));
stack.push_bool(true);
} else {
stack.push_bool(false);
}
}
void interpret_bitstring_hex_literal(IntCtx& ctx) {
auto s = ctx.scan_word_to('}');
unsigned char buff[128];
int bits = (int)td::bitstring::parse_bitstring_hex_literal(buff, sizeof(buff), s.begin(), s.end());
if (bits < 0) {
throw IntError{"Invalid hex bitstring constant"};
}
auto cs = Ref{true, vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize()};
ctx.stack.push(std::move(cs));
push_argcount(ctx.stack, 1);
}
void interpret_bitstring_binary_literal(IntCtx& ctx) {
auto s = ctx.scan_word_to('}');
unsigned char buff[128];
int bits = (int)td::bitstring::parse_bitstring_binary_literal(buff, sizeof(buff), s.begin(), s.end());
if (bits < 0) {
throw IntError{"Invalid binary bitstring constant"};
}
auto cs = Ref{true, vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize()};
ctx.stack.push(std::move(cs));
push_argcount(ctx.stack, 1);
}
void interpret_word(IntCtx& ctx) {
char sep = (char)ctx.stack.pop_smallint_range(127);
auto word = (sep != ' ' ? ctx.scan_word_to(sep, true) : ctx.scan_word());
ctx.stack.push_string(word);
}
void interpret_word_ext(IntCtx& ctx) {
int mode = ctx.stack.pop_smallint_range(11);
auto delims = ctx.stack.pop_string();
if (mode & 8) {
ctx.skipspc(mode & 4);
}
ctx.stack.push_string(ctx.scan_word_ext(CharClassifier{delims, mode & 3}));
}
void interpret_skipspc(IntCtx& ctx) {
ctx.skipspc();
}
void interpret_wordlist_begin_aux(vm::Stack& stack) {
stack.push({vm::from_object, Ref{true}});
}
void interpret_wordlist_begin(IntCtx& ctx) {
check_not_int_exec(ctx);
interpret_wordlist_begin_aux(ctx.stack);
push_argcount(ctx, 0);
++(ctx.state);
}
void interpret_wordlist_end_aux(vm::Stack& stack) {
Ref wordlist_ref = pop_word_list(stack);
wordlist_ref.write().close();
stack.push({vm::from_object, Ref{wordlist_ref}});
}
void interpret_wordlist_end(IntCtx& ctx) {
check_compile(ctx);
interpret_wordlist_end_aux(ctx.stack);
push_argcount(ctx, 1);
--(ctx.state);
}
void interpret_internal_interpret_begin(IntCtx& ctx) {
check_compile(ctx);
push_argcount(ctx, 0);
ctx.state = -ctx.state;
}
void interpret_internal_interpret_end(IntCtx& ctx) {
check_int_exec(ctx);
ctx.state = -ctx.state;
ctx.stack.push({vm::from_object, Dictionary::nop_word_def});
}
// (create)
// maybe need an extra argument to identify the vocabulary (namespace) to be edited
void interpret_create_aux(IntCtx& ctx, int mode) {
if (mode < 0) {
mode = ctx.stack.pop_smallint_range(3);
}
std::string word = ctx.stack.pop_string();
if (!word.size()) {
throw IntError{"non-empty word name expected"};
}
auto wd_ref = pop_exec_token(ctx.stack);
if (!(mode & 2)) {
word += ' ';
}
bool active = (mode & 1);
auto entry = ctx.dictionary->lookup(word);
if (entry) {
*entry = WordRef{wd_ref, active}; // redefine word
} else {
ctx.dictionary->def_word(std::move(word), {wd_ref, active});
}
}
// { bl word 0 (create) } : create
void interpret_create(IntCtx& ctx) {
auto word = ctx.scan_word();
if (!word.size()) {
throw IntError{"non-empty word name expected"};
}
ctx.stack.push_string(word);
interpret_create_aux(ctx, 0);
}
Ref create_aux_wd{Ref{true, std::bind(interpret_create_aux, std::placeholders::_1, -1)}};
// { bl word 2 ' (create) } :: :
void interpret_colon(IntCtx& ctx, int mode) {
ctx.stack.push_string(ctx.scan_word());
ctx.stack.push_smallint(mode);
ctx.stack.push_smallint(2);
ctx.stack.push({vm::from_object, create_aux_wd});
//push_argcount(ctx, 2, create_wd);
}
// (forget)
void interpret_forget_aux(IntCtx& ctx) {
std::string s = ctx.stack.pop_string();
auto s_copy = s;
auto entry = ctx.dictionary->lookup(s);
if (!entry) {
s += " ";
entry = ctx.dictionary->lookup(s);
}
if (!entry) {
throw IntError{"`" + s_copy + "` not found"};
} else {
ctx.dictionary->undef_word(s);
}
}
// { bl word (forget) } : forget
void interpret_forget(IntCtx& ctx) {
ctx.stack.push_string(ctx.scan_word());
interpret_forget_aux(ctx);
}
void interpret_quote_str(IntCtx& ctx) {
ctx.stack.push_string(ctx.scan_word_to('"'));
push_argcount(ctx.stack, 1);
}
int str_utf8_code(const char* str, int& len) {
if (len <= 0) {
return -1;
}
if (len >= 1 && (unsigned char)str[0] < 0x80) {
len = 1;
return str[0];
}
if (len >= 2 && (str[0] & 0xe0) == 0xc0 && (str[1] & 0xc0) == 0x80) {
len = 2;
return ((str[0] & 0x1f) << 6) | (str[1] & 0x3f);
}
if (len >= 3 && (str[0] & 0xf0) == 0xe0 && (str[1] & 0xc0) == 0x80 && (str[2] & 0xc0) == 0x80) {
len = 3;
return ((str[0] & 0x0f) << 12) | ((str[1] & 0x3f) << 6) | (str[2] & 0x3f);
}
if (len >= 4 && (str[0] & 0xf8) == 0xf0 && (str[1] & 0xc0) == 0x80 && (str[2] & 0xc0) == 0x80 &&
(str[3] & 0xc0) == 0x80) {
len = 4;
return ((str[0] & 7) << 18) | ((str[1] & 0x3f) << 12) | ((str[2] & 0x3f) << 6) | (str[3] & 0x3f);
}
return -1;
}
void interpret_char(IntCtx& ctx) {
auto s = ctx.scan_word();
int len = (s.size() < 10 ? (int)s.size() : 10);
int code = str_utf8_code(s.data(), len);
if (code < 0 || s.size() != (unsigned)len) {
throw IntError{"exactly one character expected"};
}
ctx.stack.push_smallint(code);
push_argcount(ctx, 1);
}
int parse_number(std::string s, td::RefInt256& num, td::RefInt256& denom, bool allow_frac = true,
bool throw_error = false) {
if (allow_frac) {
auto pos = s.find('/');
if (pos != std::string::npos) {
return parse_number(std::string{s, 0, pos}, num, denom, false, throw_error) > 0 &&
parse_number(std::string{s, pos + 1}, denom, num, false, throw_error) > 0
? 2
: 0;
}
}
const char* str = s.c_str();
int len = (int)s.size();
int frac = -1, base, *frac_ptr = allow_frac ? &frac : nullptr;
num = td::RefInt256{true};
auto& x = num.unique_write();
if (len >= 4 && str[0] == '-' && str[1] == '0' && (str[2] == 'x' || str[2] == 'b')) {
if (str[2] == 'x') {
base = 16;
if (x.parse_hex(str + 3, len - 3, frac_ptr) != len - 3) {
return 0;
}
} else {
base = 2;
if (x.parse_binary(str + 3, len - 3, frac_ptr) != len - 3) {
return 0;
}
}
x.negate().normalize();
} else if (len >= 3 && str[0] == '0' && (str[1] == 'x' || str[1] == 'b')) {
if (str[1] == 'x') {
base = 16;
if (x.parse_hex(str + 2, len - 2, frac_ptr) != len - 2) {
return 0;
}
} else {
base = 2;
if (x.parse_binary(str + 2, len - 2, frac_ptr) != len - 2) {
return 0;
}
}
} else {
base = 10;
if (!len || x.parse_dec(str, len, frac_ptr) != len) {
return 0;
}
}
if (!x.signed_fits_bits(257)) {
if (throw_error) {
throw IntError{"integer constant too large"};
}
return 0;
}
if (frac < 0) {
return 1;
} else {
denom = td::RefInt256{true, 1};
while (frac-- > 0) {
if (!denom.unique_write().mul_tiny(base).normalize_bool()) {
if (throw_error) {
throw IntError{"denominator in constant too large"};
}
return 0;
}
}
if (!denom.unique_write().unsigned_fits_bits(256)) {
if (throw_error) {
throw IntError{"denominator in constant too large"};
}
return 0;
}
return 2;
}
}
void interpret_parse_number(vm::Stack& stack) {
td::RefInt256 num, denom;
int res = parse_number(stack.pop_string(), num, denom, true, false);
if (res >= 1) {
stack.push_int(std::move(num));
}
if (res == 2) {
stack.push_int(std::move(denom));
}
stack.push_smallint(res);
}
void interpret_parse_hex_number(vm::Stack& stack) {
td::RefInt256 x{true};
auto str = stack.pop_string();
bool ok = (str.size() <= 65535) && x.unique_write().parse_hex(str.data(), (int)str.size()) == (int)str.size();
if (ok) {
stack.push_int(std::move(x));
}
stack.push_smallint(ok);
}
void interpret_quit(IntCtx& ctx) {
throw Quit{0};
}
void interpret_bye(IntCtx& ctx) {
throw Quit{-1};
}
void interpret_halt(vm::Stack& stack) {
int code = stack.pop_smallint_range(255);
throw Quit{~code};
}
void interpret_abort(IntCtx& ctx) {
throw IntError{ctx.stack.pop_string()};
}
Ref interpret_execute(IntCtx& ctx) {
return pop_exec_token(ctx);
}
Ref interpret_execute_times(IntCtx& ctx) {
int count = ctx.stack.pop_smallint_range(1000000000);
auto wd_ref = pop_exec_token(ctx);
if (!count) {
return {};
}
while (--count > 0) {
wd_ref->run(ctx);
}
return wd_ref;
}
Ref interpret_if(IntCtx& ctx) {
auto true_ref = pop_exec_token(ctx);
if (ctx.stack.pop_bool()) {
return true_ref;
} else {
return {};
}
}
Ref interpret_ifnot(IntCtx& ctx) {
auto false_ref = pop_exec_token(ctx);
if (ctx.stack.pop_bool()) {
return {};
} else {
return false_ref;
}
}
Ref interpret_cond(IntCtx& ctx) {
auto false_ref = pop_exec_token(ctx);
auto true_ref = pop_exec_token(ctx);
if (ctx.stack.pop_bool()) {
return true_ref;
} else {
return false_ref;
}
}
void interpret_while(IntCtx& ctx) {
auto body_ref = pop_exec_token(ctx);
auto cond_ref = pop_exec_token(ctx);
while (true) {
cond_ref->run(ctx);
if (!ctx.stack.pop_bool()) {
break;
}
body_ref->run(ctx);
}
}
void interpret_until(IntCtx& ctx) {
auto body_ref = pop_exec_token(ctx);
do {
body_ref->run(ctx);
} while (!ctx.stack.pop_bool());
}
void interpret_tick(IntCtx& ctx) {
std::string word = ctx.scan_word().str();
auto entry = ctx.dictionary->lookup(word);
if (!entry) {
entry = ctx.dictionary->lookup(word + ' ');
if (!entry) {
throw IntError{"word `" + word + "` undefined"};
}
}
ctx.stack.push({vm::from_object, entry->get_def()});
push_argcount(ctx, 1);
}
void interpret_find(IntCtx& ctx) {
std::string word = ctx.stack.pop_string();
auto entry = ctx.dictionary->lookup(word);
if (!entry) {
entry = ctx.dictionary->lookup(word + ' ');
}
if (!entry) {
ctx.stack.push_bool(false);
} else {
ctx.stack.push({vm::from_object, entry->get_def()});
ctx.stack.push_bool(true);
}
}
void interpret_tick_nop(vm::Stack& stack) {
stack.push({vm::from_object, Dictionary::nop_word_def});
}
void interpret_include(IntCtx& ctx) {
auto fname = ctx.stack.pop_string();
auto r_file = ctx.source_lookup->lookup_source(fname, ctx.currentd_dir);
if (r_file.is_error()) {
throw IntError{"cannot locate file `" + fname + "`"};
}
auto file = r_file.move_as_ok();
std::stringstream ss(std::move(file.data));
IntCtx::Savepoint save{ctx, td::PathView(file.path).file_name().str(), td::PathView(file.path).parent_dir().str(),
&ss};
funny_interpret_loop(ctx);
}
void interpret_skip_source(vm::Stack& stack) {
throw SkipToEof();
}
void interpret_words(IntCtx& ctx) {
for (const auto& x : *ctx.dictionary) {
*ctx.output_stream << x.first << " ";
}
*ctx.output_stream << std::endl;
}
void interpret_pack_std_smc_addr(vm::Stack& stack) {
block::StdAddress a;
stack.check_underflow(3);
int mode = stack.pop_smallint_range(7);
td::RefInt256 x = stack.pop_int_finite();
if (td::sgn(x) < 0) {
throw IntError{"non-negative integer expected"};
}
CHECK(x->export_bytes(a.addr.data(), 32, false));
a.workchain = stack.pop_smallint_range(0x7f, -0x80);
a.testnet = mode & 2;
a.bounceable = !(mode & 1);
stack.push_string(a.rserialize(mode & 4));
}
void interpret_unpack_std_smc_addr(vm::Stack& stack) {
block::StdAddress a;
if (!a.parse_addr(stack.pop_string())) {
stack.push_bool(false);
} else {
stack.push_smallint(a.workchain);
td::RefInt256 x{true};
CHECK(x.write().import_bytes(a.addr.data(), 32, false));
stack.push_int(std::move(x));
stack.push_smallint(a.testnet * 2 + 1 - a.bounceable);
stack.push_bool(true);
}
}
void interpret_bytes_to_base64(vm::Stack& stack, bool base64_url) {
stack.push_string(td::str_base64_encode(stack.pop_bytes(), base64_url));
}
void interpret_base64_to_bytes(vm::Stack& stack, bool allow_base64_url, bool quiet) {
auto s = stack.pop_string();
if (!td::is_valid_base64(s, allow_base64_url)) {
stack.push_bool(false);
if (!quiet) {
throw IntError{"invalid base64"};
}
} else {
stack.push_bytes(td::str_base64_decode(s, allow_base64_url));
if (quiet) {
stack.push_bool(true);
}
}
}
vm::VmLog create_vm_log(td::LogInterface* logger) {
if (!logger) {
return {};
}
auto options = td::LogOptions::plain();
options.level = 4;
options.fix_newlines = true;
return {logger, options};
}
class StringLogger : public td::LogInterface {
public:
void append(td::CSlice slice) override {
res.append(slice.data(), slice.size());
}
std::string res;
};
class OstreamLogger : public td::LogInterface {
public:
explicit OstreamLogger(std::ostream* stream) : stream_(stream) {
}
void append(td::CSlice slice) override {
stream_->write(slice.data(), slice.size());
}
private:
std::ostream* stream_{nullptr};
};
td::Ref vm_libraries{true};
std::vector[> get_vm_libraries() {
if (vm_libraries->get().type() == vm::StackEntry::t_cell) {
return {vm_libraries->get().as_cell()};
} else {
return {};
}
}
void interpret_run_vm_code(IntCtx& ctx, bool with_gas) {
long long gas_limit = with_gas ? ctx.stack.pop_long_range(vm::GasLimits::infty) : vm::GasLimits::infty;
auto cs = ctx.stack.pop_cellslice();
OstreamLogger ostream_logger(ctx.error_stream);
auto log = create_vm_log(ctx.error_stream ? &ostream_logger : nullptr);
vm::GasLimits gas{gas_limit};
int res = vm::run_vm_code(cs, ctx.stack, 0, nullptr, log, nullptr, &gas, get_vm_libraries());
ctx.stack.push_smallint(res);
if (with_gas) {
ctx.stack.push_smallint(gas.gas_consumed());
}
}
void interpret_run_vm_dict(IntCtx& ctx, bool with_gas) {
long long gas_limit = with_gas ? ctx.stack.pop_long_range(vm::GasLimits::infty) : vm::GasLimits::infty;
auto cs = ctx.stack.pop_cellslice();
OstreamLogger ostream_logger(ctx.error_stream);
auto log = create_vm_log(ctx.error_stream ? &ostream_logger : nullptr);
vm::GasLimits gas{gas_limit};
int res = vm::run_vm_code(cs, ctx.stack, 3, nullptr, log, nullptr, &gas, get_vm_libraries());
ctx.stack.push_smallint(res);
if (with_gas) {
ctx.stack.push_smallint(gas.gas_consumed());
}
}
void interpret_run_vm(IntCtx& ctx, bool with_gas) {
long long gas_limit = with_gas ? ctx.stack.pop_long_range(vm::GasLimits::infty) : vm::GasLimits::infty;
auto data = ctx.stack.pop_cell();
auto cs = ctx.stack.pop_cellslice();
OstreamLogger ostream_logger(ctx.error_stream);
auto log = create_vm_log(ctx.error_stream ? &ostream_logger : nullptr);
vm::GasLimits gas{gas_limit};
int res = vm::run_vm_code(cs, ctx.stack, 3, &data, log, nullptr, &gas);
ctx.stack.push_smallint(res);
ctx.stack.push_cell(std::move(data));
if (with_gas) {
ctx.stack.push_smallint(gas.gas_consumed());
}
}
void do_interpret_db_run_vm_parallel(std::ostream* stream, vm::Stack& stack, vm::TonDb* ton_db_ptr, int threads_n,
int tasks_n) {
if (!ton_db_ptr || !*ton_db_ptr) {
throw vm::VmError{vm::Excno::fatal, "Ton database is not available"};
}
auto& ton_db = *ton_db_ptr;
auto txn = ton_db->begin_transaction();
auto txn_abort = td::ScopeExit() + [&] { ton_db->abort_transaction(std::move(txn)); };
struct Task {
vm::Ref code;
vm::SmartContractDb smart;
td::optional diff;
td::unique_ptr guard;
Ref stack;
int res{0};
Ref data;
std::string log;
};
std::vector tasks(tasks_n);
std::vector threads(threads_n);
for (auto& task : tasks) {
task.code = stack.pop_cellslice();
auto smart_hash = td::serialize(stack.pop_smallint_range(1000000000));
task.smart = txn->begin_smartcontract(smart_hash);
task.guard = td::create_lambda_guard([&] { txn->abort_smartcontract(std::move(task.smart)); });
auto argsn = stack.pop_smallint_range(100);
task.stack = stack.split_top(argsn);
}
std::atomic next_task_i{0};
auto run_tasks = [&] {
while (true) {
auto task_i = next_task_i++;
if (task_i >= tasks_n) {
break;
}
auto& task = tasks[task_i];
auto data = task.smart->get_root();
StringLogger logger;
vm::VmLog log = create_vm_log(stream ? &logger : nullptr);
task.res = vm::run_vm_code(task.code, task.stack, 3, &data, std::move(log));
task.smart->set_root(data);
task.diff = vm::SmartContractDiff(std::move(task.smart));
task.data = std::move(data);
task.log = std::move(logger.res);
}
};
td::Timer timer;
for (auto& thread : threads) {
thread = td::thread(run_tasks);
}
run_tasks();
for (auto& thread : threads) {
thread.join();
}
if (stream) {
int id = 0;
for (auto& task : tasks) {
id++;
*stream << "Task #" << id << " vm_log begin" << std::endl;
*stream << task.log;
*stream << "Task #" << id << " vm_log end" << std::endl;
}
}
LOG(ERROR) << timer;
timer = {};
for (auto& task : tasks) {
auto retn = task.stack.write().pop_smallint_range(100, -1);
if (retn == -1) {
retn = task.stack->depth();
}
stack.push_from_stack(std::move(*task.stack), retn);
stack.push_smallint(task.res);
stack.push_cell(std::move(task.data));
task.guard->dismiss();
if (task.diff) {
txn->commit_smartcontract(std::move(task.diff.value()));
} else {
txn->commit_smartcontract(std::move(task.smart));
}
}
LOG(ERROR) << timer;
timer = {};
txn_abort.dismiss();
ton_db->commit_transaction(std::move(txn));
timer = {};
LOG(INFO) << "TonDB stats: \n" << ton_db->stats();
}
void interpret_db_run_vm(IntCtx& ctx) {
do_interpret_db_run_vm_parallel(ctx.error_stream, ctx.stack, ctx.ton_db, 0, 1);
}
void interpret_db_run_vm_parallel(IntCtx& ctx) {
auto threads_n = ctx.stack.pop_smallint_range(32, 0);
auto tasks_n = ctx.stack.pop_smallint_range(1000000000);
do_interpret_db_run_vm_parallel(ctx.error_stream, ctx.stack, ctx.ton_db, threads_n, tasks_n);
}
// n -- executes $n
void interpret_get_cmdline_arg(IntCtx& ctx) {
int n = ctx.stack.pop_smallint_range(999999);
char buffer[14];
sprintf(buffer, "$%d ", n);
auto entry = ctx.dictionary->lookup(std::string{buffer});
if (!entry) {
throw IntError{"-?"};
} else {
(*entry)(ctx);
}
}
// x1 .. xn n 'w -->
void interpret_execute_internal(IntCtx& ctx) {
Ref word_def = pop_exec_token(ctx);
int count = ctx.stack.pop_smallint_range(255);
ctx.stack.check_underflow(count);
word_def->run(ctx);
}
// wl x1 .. xn n 'w --> wl'
void interpret_compile_internal(vm::Stack& stack) {
Ref word_def = pop_exec_token(stack);
int count = stack.pop_smallint_range(255);
do_compile_literals(stack, count);
if (word_def != Dictionary::nop_word_def) {
do_compile(stack, word_def);
}
}
void do_compile(vm::Stack& stack, Ref word_def) {
Ref wl_ref = pop_word_list(stack);
if (word_def != Dictionary::nop_word_def) {
if ((td::uint64)word_def->list_size() <= 1) {
// inline short definitions
wl_ref.write().append(*(word_def->get_list()));
} else {
wl_ref.write().push_back(word_def);
}
}
stack.push({vm::from_object, wl_ref});
}
void compile_one_literal(WordList& wlist, vm::StackEntry val) {
using namespace std::placeholders;
if (val.type() == vm::StackEntry::t_int) {
auto x = std::move(val).as_int();
if (!x->signed_fits_bits(257)) {
throw IntError{"invalid numeric literal"};
} else if (x->signed_fits_bits(64)) {
wlist.push_back(Ref{true, std::bind(interpret_const, _1, x->to_long())});
} else {
wlist.push_back(Ref{true, std::bind(interpret_big_const, _1, std::move(x))});
}
} else {
wlist.push_back(Ref{true, std::bind(interpret_literal, _1, std::move(val))});
}
}
void do_compile_literals(vm::Stack& stack, int count) {
if (count < 0) {
throw IntError{"cannot compile a negative number of literals"};
}
stack.check_underflow(count + 1);
Ref wl_ref = std::move(stack[count]).as_object();
if (wl_ref.is_null()) {
throw IntError{"list of words expected"};
}
for (int i = count - 1; i >= 0; i--) {
compile_one_literal(wl_ref.write(), std::move(stack[i]));
}
stack.pop_many(count + 1);
stack.push({vm::from_object, wl_ref});
}
void init_words_common(Dictionary& d) {
using namespace std::placeholders;
d.def_word("nop ", Dictionary::nop_word_def);
// stack print/dump words
d.def_ctx_word(". ", std::bind(interpret_dot, _1, true));
d.def_ctx_word("._ ", std::bind(interpret_dot, _1, false));
d.def_ctx_word("x. ", std::bind(interpret_dothex, _1, false, true));
d.def_ctx_word("x._ ", std::bind(interpret_dothex, _1, false, false));
d.def_ctx_word("X. ", std::bind(interpret_dothex, _1, true, true));
d.def_ctx_word("X._ ", std::bind(interpret_dothex, _1, true, false));
d.def_ctx_word("b. ", std::bind(interpret_dotbinary, _1, true));
d.def_ctx_word("b._ ", std::bind(interpret_dotbinary, _1, false));
d.def_ctx_word("csr. ", interpret_dot_cellslice_rec);
d.def_ctx_word(".s ", interpret_dotstack);
d.def_ctx_word(".sl ", interpret_dotstack_list);
d.def_ctx_word(".dump ", interpret_dump);
d.def_ctx_word(".l ", interpret_print_list);
d.def_ctx_word(".tc ", interpret_dottc);
d.def_stack_word("(dump) ", interpret_dump_internal);
d.def_stack_word("(.) ", interpret_dot_internal);
d.def_stack_word("(x.) ", std::bind(interpret_dothex_internal, _1, false));
d.def_stack_word("(X.) ", std::bind(interpret_dothex_internal, _1, true));
d.def_stack_word("(b.) ", interpret_dotbinary_internal);
// stack manipulation
d.def_stack_word("drop ", interpret_drop);
d.def_stack_word("2drop ", interpret_2drop);
d.def_stack_word("dup ", interpret_dup);
d.def_stack_word("over ", interpret_over);
d.def_stack_word("2dup ", interpret_2dup);
d.def_stack_word("2over ", interpret_2over);
d.def_stack_word("swap ", interpret_swap);
d.def_stack_word("2swap ", interpret_2swap);
d.def_stack_word("tuck ", interpret_tuck);
d.def_stack_word("nip ", interpret_nip);
d.def_stack_word("rot ", interpret_rot);
d.def_stack_word("-rot ", interpret_rot_rev);
d.def_stack_word("pick ", interpret_pick);
d.def_stack_word("roll ", interpret_roll);
d.def_stack_word("-roll ", interpret_roll_rev);
d.def_stack_word("reverse ", interpret_reverse);
d.def_stack_word("exch ", interpret_exch);
d.def_stack_word("exch2 ", interpret_exch2);
d.def_stack_word("depth ", interpret_depth);
d.def_stack_word("?dup ", interpret_cond_dup);
// low-level stack manipulation
d.def_stack_word(" ", interpret_make_xchg);
d.def_stack_word(" ", interpret_make_push);
d.def_stack_word(" ", interpret_make_pop);
// arithmetic
d.def_stack_word("+ ", interpret_plus);
d.def_stack_word("- ", interpret_minus);
d.def_stack_word("negate ", interpret_negate);
d.def_stack_word("1+ ", std::bind(interpret_plus_tiny, _1, 1));
d.def_stack_word("1- ", std::bind(interpret_plus_tiny, _1, -1));
d.def_stack_word("2+ ", std::bind(interpret_plus_tiny, _1, 2));
d.def_stack_word("2- ", std::bind(interpret_plus_tiny, _1, -2));
d.def_stack_word("* ", interpret_times);
d.def_stack_word("/ ", std::bind(interpret_div, _1, -1));
d.def_stack_word("/c ", std::bind(interpret_div, _1, 1));
d.def_stack_word("/r ", std::bind(interpret_div, _1, 0));
d.def_stack_word("mod ", std::bind(interpret_mod, _1, -1));
d.def_stack_word("rmod ", std::bind(interpret_mod, _1, 0));
d.def_stack_word("cmod ", std::bind(interpret_mod, _1, 1));
d.def_stack_word("/mod ", std::bind(interpret_divmod, _1, -1));
d.def_stack_word("/cmod ", std::bind(interpret_divmod, _1, 1));
d.def_stack_word("/rmod ", std::bind(interpret_divmod, _1, 0));
d.def_stack_word("*/ ", std::bind(interpret_times_div, _1, -1));
d.def_stack_word("*/c ", std::bind(interpret_times_div, _1, 1));
d.def_stack_word("*/r ", std::bind(interpret_times_div, _1, 0));
d.def_stack_word("*/mod ", std::bind(interpret_times_divmod, _1, -1));
d.def_stack_word("*/cmod ", std::bind(interpret_times_divmod, _1, 1));
d.def_stack_word("*/rmod ", std::bind(interpret_times_divmod, _1, 0));
d.def_stack_word("*mod ", std::bind(interpret_times_mod, _1, -1));
d.def_stack_word("1<< ", interpret_pow2);
d.def_stack_word("-1<< ", interpret_neg_pow2);
d.def_stack_word("1<<1- ", interpret_pow2_minus1);
d.def_stack_word("%1<< ", interpret_mod_pow2);
d.def_stack_word("<< ", interpret_lshift);
d.def_stack_word(">> ", std::bind(interpret_rshift, _1, -1));
d.def_stack_word(">>c ", std::bind(interpret_rshift, _1, 1));
d.def_stack_word(">>r ", std::bind(interpret_rshift, _1, 0));
d.def_stack_word("2* ", std::bind(interpret_lshift_const, _1, 1));
d.def_stack_word("2/ ", std::bind(interpret_rshift_const, _1, 1));
d.def_stack_word("*>> ", std::bind(interpret_times_rshift, _1, -1));
d.def_stack_word("*>>c ", std::bind(interpret_times_rshift, _1, 1));
d.def_stack_word("*>>r ", std::bind(interpret_times_rshift, _1, 0));
d.def_stack_word("< ", std::bind(interpret_lshift_div, _1, -1));
d.def_stack_word("< ", std::bind(interpret_cmp, _1, "\xff\x00\xff"));
d.def_stack_word("<= ", std::bind(interpret_cmp, _1, "\xff\xff\x00"));
d.def_stack_word(">= ", std::bind(interpret_cmp, _1, "\x00\xff\xff"));
d.def_stack_word("< ", std::bind(interpret_cmp, _1, "\xff\x00\x00"));
d.def_stack_word("> ", std::bind(interpret_cmp, _1, "\x00\x00\xff"));
d.def_stack_word("sgn ", std::bind(interpret_sgn, _1, "\xff\x00\x01"));
d.def_stack_word("0= ", std::bind(interpret_sgn, _1, "\x00\xff\x00"));
d.def_stack_word("0<> ", std::bind(interpret_sgn, _1, "\xff\x00\xff"));
d.def_stack_word("0<= ", std::bind(interpret_sgn, _1, "\xff\xff\x00"));
d.def_stack_word("0>= ", std::bind(interpret_sgn, _1, "\x00\xff\xff"));
d.def_stack_word("0< ", std::bind(interpret_sgn, _1, "\xff\x00\x00"));
d.def_stack_word("0> ", std::bind(interpret_sgn, _1, "\x00\x00\xff"));
d.def_stack_word("fits ", std::bind(interpret_fits, _1, true));
d.def_stack_word("ufits ", std::bind(interpret_fits, _1, false));
// char/string manipulation
d.def_active_word("\"", interpret_quote_str);
d.def_active_word("char ", interpret_char);
d.def_ctx_word("emit ", interpret_emit);
d.def_ctx_word("space ", std::bind(interpret_emit_const, _1, ' '));
d.def_ctx_word("cr ", std::bind(interpret_emit_const, _1, '\n'));
d.def_ctx_word("type ", interpret_type);
d.def_stack_word("string? ", interpret_is_string);
d.def_stack_word("chr ", interpret_chr);
d.def_stack_word("hold ", interpret_hold);
d.def_stack_word("(number) ", interpret_parse_number);
d.def_stack_word("(hex-number) ", interpret_parse_hex_number);
d.def_stack_word("$| ", interpret_str_split);
d.def_stack_word("$+ ", interpret_str_concat);
d.def_stack_word("$= ", interpret_str_equal);
d.def_stack_word("$cmp ", interpret_str_cmp);
d.def_stack_word("$reverse ", interpret_str_reverse);
d.def_stack_word("(-trailing) ", std::bind(interpret_str_remove_trailing_int, _1, 0));
d.def_stack_word("-trailing ", std::bind(interpret_str_remove_trailing_int, _1, ' '));
d.def_stack_word("-trailing0 ", std::bind(interpret_str_remove_trailing_int, _1, '0'));
d.def_stack_word("$len ", interpret_str_len);
d.def_stack_word("Blen ", interpret_bytes_len);
d.def_ctx_word("Bx. ", std::bind(interpret_bytes_hex_print_raw, _1, true));
d.def_stack_word("B>X ", std::bind(interpret_bytes_to_hex, _1, true));
d.def_stack_word("B>x ", std::bind(interpret_bytes_to_hex, _1, false));
d.def_stack_word("x>B ", std::bind(interpret_hex_to_bytes, _1, false));
d.def_stack_word("x>B? ", std::bind(interpret_hex_to_bytes, _1, true));
d.def_stack_word("B| ", interpret_bytes_split);
d.def_stack_word("B+ ", interpret_bytes_concat);
d.def_stack_word("B= ", interpret_bytes_equal);
d.def_stack_word("Bcmp ", interpret_bytes_cmp);
d.def_stack_word("u>B ", std::bind(interpret_int_to_bytes, _1, false, false));
d.def_stack_word("i>B ", std::bind(interpret_int_to_bytes, _1, true, false));
d.def_stack_word("Lu>B ", std::bind(interpret_int_to_bytes, _1, false, true));
d.def_stack_word("Li>B ", std::bind(interpret_int_to_bytes, _1, true, true));
d.def_stack_word("B>u@ ", std::bind(interpret_bytes_fetch_int, _1, 0));
d.def_stack_word("B>i@ ", std::bind(interpret_bytes_fetch_int, _1, 1));
d.def_stack_word("B>u@+ ", std::bind(interpret_bytes_fetch_int, _1, 2));
d.def_stack_word("B>i@+ ", std::bind(interpret_bytes_fetch_int, _1, 3));
d.def_stack_word("B>Lu@ ", std::bind(interpret_bytes_fetch_int, _1, 0x10));
d.def_stack_word("B>Li@ ", std::bind(interpret_bytes_fetch_int, _1, 0x11));
d.def_stack_word("B>Lu@+ ", std::bind(interpret_bytes_fetch_int, _1, 0x12));
d.def_stack_word("B>Li@+ ", std::bind(interpret_bytes_fetch_int, _1, 0x13));
d.def_stack_word("$>B ", interpret_string_to_bytes);
d.def_stack_word("Bhash ", interpret_bytes_hash);
// cell manipulation (create, write and modify cells)
d.def_stack_word(" ", std::bind(interpret_store_end, _1, false));
d.def_stack_word("b>spec ", std::bind(interpret_store_end, _1, true));
d.def_stack_word("$>s ", interpret_string_to_cellslice);
d.def_stack_word("|+ ", interpret_concat_cellslice);
d.def_stack_word("|_ ", interpret_concat_cellslice_ref);
d.def_stack_word("b+ ", interpret_concat_builders);
d.def_stack_word("bbits ", std::bind(interpret_builder_bitrefs, _1, 1));
d.def_stack_word("brefs ", std::bind(interpret_builder_bitrefs, _1, 2));
d.def_stack_word("bbitrefs ", std::bind(interpret_builder_bitrefs, _1, 3));
d.def_stack_word("brembits ", std::bind(interpret_builder_remaining_bitrefs, _1, 1));
d.def_stack_word("bremrefs ", std::bind(interpret_builder_remaining_bitrefs, _1, 2));
d.def_stack_word("brembitrefs ", std::bind(interpret_builder_remaining_bitrefs, _1, 3));
d.def_stack_word("hash ", interpret_cell_hash);
// cellslice manipulation (read from cells)
d.def_stack_word("] ", interpret_cell_check_empty);
d.def_stack_word("empty? ", interpret_cell_empty);
d.def_stack_word("remaining ", interpret_cell_remaining);
d.def_stack_word("sbits ", std::bind(interpret_slice_bitrefs, _1, 1));
d.def_stack_word("srefs ", std::bind(interpret_slice_bitrefs, _1, 2));
d.def_stack_word("sbitrefs ", std::bind(interpret_slice_bitrefs, _1, 3));
// boc manipulation
d.def_stack_word("B>boc ", interpret_boc_deserialize);
d.def_stack_word("boc>B ", interpret_boc_serialize);
d.def_stack_word("boc+>B ", interpret_boc_serialize_ext);
d.def_ctx_word("file>B ", interpret_read_file);
d.def_ctx_word("filepart>B ", interpret_read_file_part);
d.def_ctx_word("B>file ", interpret_write_file);
d.def_ctx_word("file-exists? ", interpret_file_exists);
// custom & crypto
d.def_stack_word("now ", interpret_now);
d.def_stack_word("newkeypair ", interpret_new_keypair);
d.def_stack_word("priv>pub ", interpret_priv_key_to_pub);
d.def_stack_word("ed25519_sign ", interpret_ed25519_sign);
d.def_stack_word("ed25519_chksign ", interpret_ed25519_chksign);
d.def_stack_word("ed25519_sign_uint ", interpret_ed25519_sign_uint);
d.def_stack_word("crc16 ", interpret_crc16);
d.def_stack_word("crc32 ", interpret_crc32);
d.def_stack_word("crc32c ", interpret_crc32c);
// vm dictionaries
d.def_stack_word("dictnew ", interpret_dict_new);
d.def_stack_word("dict>s ", interpret_dict_to_slice);
d.def_stack_word("dict, ", interpret_store_dict);
d.def_stack_word("dict@ ", std::bind(interpret_load_dict, _1, false));
d.def_stack_word("dict@+ ", std::bind(interpret_load_dict, _1, true));
d.def_stack_word("udict!+ ", std::bind(interpret_dict_add_u, _1, vm::Dictionary::SetMode::Add, false, false));
d.def_stack_word("udict! ", std::bind(interpret_dict_add_u, _1, vm::Dictionary::SetMode::Set, false, false));
d.def_stack_word("b>udict!+ ", std::bind(interpret_dict_add_u, _1, vm::Dictionary::SetMode::Add, true, false));
d.def_stack_word("b>udict! ", std::bind(interpret_dict_add_u, _1, vm::Dictionary::SetMode::Set, true, false));
d.def_stack_word("udict@ ", std::bind(interpret_dict_get_u, _1, false));
d.def_stack_word("idict!+ ", std::bind(interpret_dict_add_u, _1, vm::Dictionary::SetMode::Add, false, true));
d.def_stack_word("idict! ", std::bind(interpret_dict_add_u, _1, vm::Dictionary::SetMode::Set, false, true));
d.def_stack_word("b>idict!+ ", std::bind(interpret_dict_add_u, _1, vm::Dictionary::SetMode::Add, true, true));
d.def_stack_word("b>idict! ", std::bind(interpret_dict_add_u, _1, vm::Dictionary::SetMode::Set, true, true));
d.def_stack_word("idict@ ", std::bind(interpret_dict_get_u, _1, true));
d.def_stack_word("pfxdict!+ ", std::bind(interpret_pfx_dict_add, _1, vm::Dictionary::SetMode::Add, false));
d.def_stack_word("pfxdict! ", std::bind(interpret_pfx_dict_add, _1, vm::Dictionary::SetMode::Set, false));
d.def_stack_word("pfxdict@ ", interpret_pfx_dict_get);
d.def_ctx_word("dictmap ", interpret_dict_map);
d.def_ctx_word("dictmapext ", interpret_dict_map_ext);
d.def_ctx_word("dictforeach ", interpret_dict_foreach);
d.def_ctx_word("dictmerge ", interpret_dict_merge);
d.def_ctx_word("dictdiff ", interpret_dict_diff);
// slice/bitstring constants
d.def_active_word("x{", interpret_bitstring_hex_literal);
d.def_active_word("b{", interpret_bitstring_binary_literal);
// boxes/holes/variables
d.def_stack_word("hole ", interpret_hole);
d.def_stack_word("box ", interpret_box);
d.def_stack_word("@ ", interpret_box_fetch);
d.def_stack_word("! ", interpret_box_store);
d.def_stack_word("null ", interpret_push_null);
d.def_stack_word("null? ", interpret_is_null);
// tuples/arrays
d.def_stack_word("| ", interpret_empty_tuple);
d.def_stack_word(", ", interpret_tuple_push);
d.def_stack_word("tpop ", interpret_tuple_pop);
d.def_stack_word("[] ", interpret_tuple_index);
d.def_stack_word("[]= ", interpret_tuple_set);
d.def_stack_word("count ", interpret_tuple_len);
d.def_stack_word("tuple? ", interpret_is_tuple);
d.def_stack_word("tuple ", interpret_make_tuple);
d.def_stack_word("untuple ", std::bind(interpret_tuple_explode, _1, true));
d.def_stack_word("explode ", std::bind(interpret_tuple_explode, _1, false));
d.def_stack_word("allot ", interpret_allot);
// atoms
d.def_stack_word("anon ", interpret_atom_anon);
d.def_stack_word("(atom) ", interpret_atom);
d.def_stack_word("atom>$ ", interpret_atom_name);
d.def_stack_word("eq? ", interpret_is_eq);
d.def_stack_word("eqv? ", interpret_is_eqv);
d.def_stack_word("atom? ", interpret_is_atom);
// execution control
d.def_ctx_tail_word("execute ", interpret_execute);
d.def_ctx_tail_word("times ", interpret_execute_times);
d.def_ctx_tail_word("if ", interpret_if);
d.def_ctx_tail_word("ifnot ", interpret_ifnot);
d.def_ctx_tail_word("cond ", interpret_cond);
d.def_ctx_word("while ", interpret_while);
d.def_ctx_word("until ", interpret_until);
// compiler control
d.def_active_word("[ ", interpret_internal_interpret_begin);
d.def_active_word("] ", interpret_internal_interpret_end);
d.def_active_word("{ ", interpret_wordlist_begin);
d.def_active_word("} ", interpret_wordlist_end);
d.def_stack_word("({) ", interpret_wordlist_begin_aux);
d.def_stack_word("(}) ", interpret_wordlist_end_aux);
d.def_stack_word("(compile) ", interpret_compile_internal);
d.def_ctx_word("(execute) ", interpret_execute_internal);
d.def_active_word("' ", interpret_tick);
d.def_stack_word("'nop ", interpret_tick_nop);
// dictionary manipulation
d.def_ctx_word("find ", interpret_find);
d.def_ctx_word("create ", interpret_create);
d.def_ctx_word("(create) ", std::bind(interpret_create_aux, _1, -1));
d.def_active_word(": ", std::bind(interpret_colon, _1, 0));
d.def_active_word(":: ", std::bind(interpret_colon, _1, 1));
d.def_active_word(":_ ", std::bind(interpret_colon, _1, 2));
d.def_active_word("::_ ", std::bind(interpret_colon, _1, 3));
d.def_ctx_word("(forget) ", interpret_forget_aux);
d.def_ctx_word("forget ", interpret_forget);
d.def_ctx_word("words ", interpret_words);
// input parse
d.def_ctx_word("word ", interpret_word);
d.def_ctx_word("(word) ", interpret_word_ext);
d.def_ctx_word("skipspc ", interpret_skipspc);
d.def_ctx_word("include ", interpret_include);
d.def_stack_word("skip-to-eof ", interpret_skip_source);
d.def_ctx_word("abort ", interpret_abort);
d.def_ctx_word("quit ", interpret_quit);
d.def_ctx_word("bye ", interpret_bye);
d.def_stack_word("halt ", interpret_halt);
}
void init_words_ton(Dictionary& d) {
using namespace std::placeholders;
d.def_stack_word("smca>$ ", interpret_pack_std_smc_addr);
d.def_stack_word("$>smca ", interpret_unpack_std_smc_addr);
d.def_stack_word("B>base64 ", std::bind(interpret_bytes_to_base64, _1, false));
d.def_stack_word("B>base64url ", std::bind(interpret_bytes_to_base64, _1, true));
d.def_stack_word("base64>B ", std::bind(interpret_base64_to_bytes, _1, false, false));
d.def_stack_word("base64url>B ", std::bind(interpret_base64_to_bytes, _1, true, false));
}
void init_words_vm(Dictionary& d) {
using namespace std::placeholders;
vm::init_op_cp0();
// vm run
d.def_stack_word("vmlibs ", std::bind(interpret_literal, _1, vm::StackEntry{vm_libraries}));
d.def_ctx_word("runvmcode ", std::bind(interpret_run_vm_code, _1, false));
d.def_ctx_word("gasrunvmcode ", std::bind(interpret_run_vm_code, _1, true));
d.def_ctx_word("runvmdict ", std::bind(interpret_run_vm_dict, _1, false));
d.def_ctx_word("gasrunvmdict ", std::bind(interpret_run_vm_dict, _1, true));
d.def_ctx_word("runvm ", std::bind(interpret_run_vm, _1, false));
d.def_ctx_word("gasrunvm ", std::bind(interpret_run_vm, _1, true));
d.def_ctx_word("dbrunvm ", interpret_db_run_vm);
d.def_ctx_word("dbrunvm-parallel ", interpret_db_run_vm_parallel);
}
void import_cmdline_args(Dictionary& d, std::string arg0, int n, const char* const argv[]) {
using namespace std::placeholders;
LOG(DEBUG) << "import_cmdlist_args(" << arg0 << "," << n << ")";
d.def_stack_word("$0 ", std::bind(interpret_literal, _1, vm::StackEntry{arg0}));
for (int i = 0; i < n; i++) {
char buffer[14];
sprintf(buffer, "$%d ", i + 1);
d.def_stack_word(buffer, std::bind(interpret_literal, _1, vm::StackEntry{argv[i]}));
}
d.def_stack_word("$# ", std::bind(interpret_const, _1, n));
d.def_ctx_word("$() ", interpret_get_cmdline_arg);
}
std::pair numeric_value_ext(std::string s, bool allow_frac = true) {
td::RefInt256 num, denom;
int res = parse_number(s, num, denom, allow_frac);
if (res <= 0) {
throw IntError{"-?"};
}
return std::make_pair(std::move(num), res == 2 ? std::move(denom) : td::RefInt256{});
}
td::RefInt256 numeric_value(std::string s) {
td::RefInt256 num, denom;
int res = parse_number(s, num, denom, false);
if (res != 1) {
throw IntError{"-?"};
}
return num;
}
int funny_interpret_loop(IntCtx& ctx) {
while (ctx.load_next_line()) {
if (ctx.is_sb()) {
continue;
}
std::ostringstream errs;
bool ok = true;
while (ok) {
ctx.skipspc();
const char* ptr = ctx.get_input();
if (!*ptr) {
break;
}
std::string Word;
Word.reserve(128);
auto entry = ctx.dictionary->lookup("");
std::string entry_word;
const char* ptr_end = ptr;
while (*ptr && *ptr != ' ' && *ptr != '\t') {
Word += *ptr++;
auto cur = ctx.dictionary->lookup(Word);
if (cur) {
entry = cur;
entry_word = Word;
ptr_end = ptr;
}
}
auto cur = ctx.dictionary->lookup(Word + " ");
if (cur || !entry) {
entry = std::move(cur);
ctx.set_input(ptr);
ctx.skipspc();
} else {
Word = entry_word;
ctx.set_input(ptr_end);
}
try {
if (entry) {
if (entry->is_active()) {
(*entry)(ctx);
} else {
ctx.stack.push_smallint(0);
ctx.stack.push({vm::from_object, entry->get_def()});
}
} else {
auto res = numeric_value_ext(Word);
ctx.stack.push(std::move(res.first));
if (res.second.not_null()) {
ctx.stack.push(std::move(res.second));
push_argcount(ctx, 2);
} else {
push_argcount(ctx, 1);
}
}
if (ctx.state > 0) {
interpret_compile_internal(ctx.stack);
} else {
interpret_execute_internal(ctx);
}
} catch (IntError& ab) {
errs << ctx << Word << ": " << ab.msg;
ok = false;
} catch (vm::VmError& ab) {
errs << ctx << Word << ": " << ab.get_msg();
ok = false;
} catch (vm::CellBuilder::CellWriteError) {
errs << ctx << Word << ": Cell builder write error";
ok = false;
} catch (vm::VmFatal) {
errs << ctx << Word << ": fatal vm error";
ok = false;
} catch (Quit& q) {
if (ctx.include_depth) {
throw;
}
if (!q.res) {
ok = false;
} else {
return q.res;
}
} catch (SkipToEof) {
return 0;
}
};
if (!ok) {
auto err_msg = errs.str();
if (!err_msg.empty()) {
LOG(ERROR) << err_msg;
}
ctx.clear();
if (ctx.include_depth) {
throw IntError{"error interpreting included file `" + ctx.filename + "` : " + err_msg};
}
} else if (!ctx.state && !ctx.include_depth) {
*ctx.output_stream << " ok" << std::endl;
}
}
return 0;
}
} // namespace fift