1
0
mirror of https://github.com/danog/ton.git synced 2024-12-03 09:58:01 +01:00
ton/crypto/vm/cellops.cpp
2019-09-07 14:33:36 +04:00

1421 lines
53 KiB
C++

/*
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 <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include <functional>
#include "vm/cellops.h"
#include "vm/log.h"
#include "vm/opctable.h"
#include "vm/stack.hpp"
#include "vm/continuation.h"
#include "vm/excno.hpp"
#include "vm/vmstate.h"
#include "common/bigint.hpp"
#include "common/refint.h"
namespace vm {
int exec_push_ref(VmState* st, CellSlice& cs, int mode, int pfx_bits) {
if (!cs.have_refs(1)) {
throw VmError{Excno::inv_opcode, "no references left for a PUSHREF instruction"};
}
cs.advance(pfx_bits);
auto cell = cs.fetch_ref();
Stack& stack = st->get_stack();
VM_LOG(st) << "execute PUSHREF" << (mode == 2 ? "CONT" : (mode == 1 ? "SLICE" : "")) << " ("
<< cell->get_hash().to_hex() << ")";
switch (mode) {
default:
case 0:
stack.push_cell(std::move(cell));
break;
case 1:
stack.push_cellslice(load_cell_slice_ref(std::move(cell)));
break;
case 2:
stack.push_cont(Ref<OrdCont>{true, load_cell_slice_ref(std::move(cell)), st->get_cp()});
break;
}
return 0;
}
std::string dump_push_ref(CellSlice& cs, unsigned args, int pfx_bits, std::string name) {
if (!cs.have_refs(1)) {
return "";
}
cs.advance(pfx_bits);
cs.advance_refs(1);
return name;
}
int compute_len_push_ref(const CellSlice& cs, unsigned args, int pfx_bits) {
return cs.have_refs(1) ? (0x10000 + pfx_bits) : 0;
}
int exec_push_slice_common(VmState* st, CellSlice& cs, unsigned data_bits, unsigned refs, int pfx_bits) {
if (!cs.have(pfx_bits + data_bits)) {
throw VmError{Excno::inv_opcode, "not enough data bits for a PUSHSLICE instruction"};
}
if (!cs.have_refs(refs)) {
throw VmError{Excno::inv_opcode, "not enough references for a PUSHSLICE instruction"};
}
Stack& stack = st->get_stack();
cs.advance(pfx_bits);
auto slice = cs.fetch_subslice(data_bits, refs);
slice.unique_write().remove_trailing();
VM_LOG(st) << "execute PUSHSLICE " << slice;
stack.push(std::move(slice));
return 0;
}
std::string dump_push_slice_common(CellSlice& cs, unsigned data_bits, unsigned refs, int pfx_bits,
const char* name = "PUSHSLICE ") {
if (!cs.have(pfx_bits + data_bits) || !cs.have_refs(refs)) {
return "";
}
cs.advance(pfx_bits);
auto slice = cs.fetch_subslice(data_bits, refs);
slice.unique_write().remove_trailing();
std::ostringstream os{name};
slice->dump_hex(os, 1, false);
return os.str();
}
int compute_len_push_slice_common(const CellSlice& cs, unsigned data_bits, unsigned refs, int pfx_bits) {
unsigned bits = pfx_bits + data_bits;
return cs.have(bits) && cs.have_refs(refs) ? (refs << 16) + bits : 0;
}
int exec_push_slice(VmState* st, CellSlice& cs, unsigned args, int pfx_bits) {
return exec_push_slice_common(st, cs, (args & 15) * 8 + 4, 0, pfx_bits);
}
std::string dump_push_slice(CellSlice& cs, unsigned args, int pfx_bits) {
return dump_push_slice_common(cs, (args & 15) * 8 + 4, 0, pfx_bits);
}
int compute_len_push_slice(const CellSlice& cs, unsigned args, int pfx_bits) {
return compute_len_push_slice_common(cs, (args & 15) * 8 + 4, 0, pfx_bits);
}
int exec_push_slice_r(VmState* st, CellSlice& cs, unsigned args, int pfx_bits) {
unsigned refs = ((args >> 5) & 3) + 1;
unsigned data_bits = (args & 31) * 8 + 1;
return exec_push_slice_common(st, cs, data_bits, refs, pfx_bits);
}
std::string dump_push_slice_r(CellSlice& cs, unsigned args, int pfx_bits) {
unsigned refs = ((args >> 5) & 3) + 1;
unsigned data_bits = (args & 31) * 8 + 1;
return dump_push_slice_common(cs, data_bits, refs, pfx_bits);
}
int compute_len_push_slice_r(const CellSlice& cs, unsigned args, int pfx_bits) {
unsigned refs = ((args >> 5) & 3) + 1;
unsigned data_bits = (args & 31) * 8 + 1;
return compute_len_push_slice_common(cs, data_bits, refs, pfx_bits);
}
int exec_push_slice_r2(VmState* st, CellSlice& cs, unsigned args, int pfx_bits) {
unsigned refs = (args >> 7) & 7;
unsigned data_bits = (args & 127) * 8 + 6;
return exec_push_slice_common(st, cs, data_bits, refs, pfx_bits);
}
std::string dump_push_slice_r2(CellSlice& cs, unsigned args, int pfx_bits) {
unsigned refs = (args >> 7) & 7;
unsigned data_bits = (args & 127) * 8 + 6;
return dump_push_slice_common(cs, data_bits, refs, pfx_bits);
}
int compute_len_push_slice_r2(const CellSlice& cs, unsigned args, int pfx_bits) {
unsigned refs = (args >> 7) & 7;
unsigned data_bits = (args & 127) * 8 + 6;
return compute_len_push_slice_common(cs, data_bits, refs, pfx_bits);
}
int exec_push_cont(VmState* st, CellSlice& cs, unsigned args, int pfx_bits) {
unsigned refs = (args >> 7) & 3;
unsigned data_bits = (args & 127) * 8;
if (!cs.have(pfx_bits + data_bits)) {
throw VmError{Excno::inv_opcode, "not enough data bits for a PUSHCONT instruction"};
}
if (!cs.have_refs(refs)) {
throw VmError{Excno::inv_opcode, "not enough references for a PUSHCONT instruction"};
}
Stack& stack = st->get_stack();
cs.advance(pfx_bits);
auto slice = cs.fetch_subslice(data_bits, refs);
VM_LOG(st) << "execute PUSHCONT " << slice;
stack.push_cont(Ref<OrdCont>{true, std::move(slice), st->get_cp()});
return 0;
}
std::string dump_push_cont(CellSlice& cs, unsigned args, int pfx_bits) {
unsigned refs = (args >> 7) & 3;
unsigned data_bits = (args & 127) * 8;
if (!cs.have(pfx_bits + data_bits) || !cs.have_refs(refs)) {
return "";
}
cs.advance(pfx_bits);
auto slice = cs.fetch_subslice(data_bits, refs);
std::ostringstream os{"PUSHCONT "};
slice->dump_hex(os, 1, false);
return os.str();
}
int compute_len_push_cont(const CellSlice& cs, unsigned args, int pfx_bits) {
unsigned refs = (args >> 7) & 3;
unsigned data_bits = (args & 127) * 8;
return compute_len_push_slice_common(cs, data_bits, refs, pfx_bits);
}
int exec_push_cont_simple(VmState* st, CellSlice& cs, unsigned args, int pfx_bits) {
unsigned data_bits = (args & 15) * 8;
if (!cs.have(pfx_bits + data_bits)) {
throw VmError{Excno::inv_opcode, "not enough data bits for a PUSHCONT instruction"};
}
Stack& stack = st->get_stack();
cs.advance(pfx_bits);
auto slice = cs.fetch_subslice(data_bits);
VM_LOG(st) << "execute PUSHCONT " << slice;
stack.push_cont(Ref<OrdCont>{true, std::move(slice), st->get_cp()});
return 0;
}
std::string dump_push_cont_simple(CellSlice& cs, unsigned args, int pfx_bits) {
unsigned data_bits = (args & 15) * 8;
if (!cs.have(pfx_bits + data_bits)) {
return "";
}
cs.advance(pfx_bits);
auto slice = cs.fetch_subslice(data_bits);
std::ostringstream os{"PUSHCONT "};
slice->dump_hex(os, 1, false);
return os.str();
}
int compute_len_push_cont_simple(const CellSlice& cs, unsigned args, int pfx_bits) {
unsigned data_bits = (args & 15) * 8;
return compute_len_push_slice_common(cs, data_bits, 0, pfx_bits);
}
void register_cell_const_ops(OpcodeTable& cp0) {
using namespace std::placeholders;
cp0.insert(OpcodeInstr::mkext(0x88, 8, 0, std::bind(dump_push_ref, _1, _2, _3, "PUSHREF"),
std::bind(exec_push_ref, _1, _2, 0, _4), compute_len_push_ref))
.insert(OpcodeInstr::mkext(0x89, 8, 0, std::bind(dump_push_ref, _1, _2, _3, "PUSHREFSLICE"),
std::bind(exec_push_ref, _1, _2, 1, _4), compute_len_push_ref))
.insert(OpcodeInstr::mkext(0x8a, 8, 0, std::bind(dump_push_ref, _1, _2, _3, "PUSHREFCONT"),
std::bind(exec_push_ref, _1, _2, 2, _4), compute_len_push_ref))
.insert(OpcodeInstr::mkext(0x8b, 8, 4, dump_push_slice, exec_push_slice, compute_len_push_slice))
.insert(OpcodeInstr::mkext(0x8c, 8, 7, dump_push_slice_r, exec_push_slice_r, compute_len_push_slice_r))
.insert(OpcodeInstr::mkextrange((0x8d * 8) << 7, (0x8d * 8 + 5) << 7, 18, 10, dump_push_slice_r2,
exec_push_slice_r2, compute_len_push_slice_r2))
.insert(OpcodeInstr::mkext(0x8e / 2, 7, 9, dump_push_cont, exec_push_cont, compute_len_push_cont))
.insert(OpcodeInstr::mkext(9, 4, 4, dump_push_cont_simple, exec_push_cont_simple, compute_len_push_cont_simple));
}
int exec_un_cs_cmp(VmState* st, const char* name, const std::function<bool(Ref<CellSlice>)>& func) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute " << name;
stack.check_underflow(1);
stack.push_smallint(func(stack.pop_cellslice()) ? -1 : 0);
return 0;
}
int exec_iun_cs_cmp(VmState* st, const char* name, const std::function<int(Ref<CellSlice>)>& func) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute " << name;
stack.check_underflow(1);
stack.push_smallint(func(stack.pop_cellslice()));
return 0;
}
int exec_bin_cs_cmp(VmState* st, const char* name, const std::function<bool(Ref<CellSlice>, Ref<CellSlice>)>& func) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute " << name;
stack.check_underflow(2);
auto cs2 = stack.pop_cellslice();
auto cs1 = stack.pop_cellslice();
stack.push_smallint(func(cs1, cs2) ? -1 : 0);
return 0;
}
int exec_ibin_cs_cmp(VmState* st, const char* name, const std::function<int(Ref<CellSlice>, Ref<CellSlice>)>& func) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute " << name;
stack.check_underflow(2);
auto cs2 = stack.pop_cellslice();
auto cs1 = stack.pop_cellslice();
stack.push_smallint(func(cs1, cs2));
return 0;
}
namespace {
using namespace std::placeholders;
void reg_un_cs_cmp(OpcodeTable& cp, unsigned code, unsigned len, const char* name,
std::function<bool(Ref<CellSlice>)> func) {
cp.insert(OpcodeInstr::mksimple(code, len, name, std::bind(exec_un_cs_cmp, _1, name, std::move(func))));
}
void reg_iun_cs_cmp(OpcodeTable& cp, unsigned code, unsigned len, const char* name,
std::function<int(Ref<CellSlice>)> func) {
cp.insert(OpcodeInstr::mksimple(code, len, name, std::bind(exec_iun_cs_cmp, _1, name, std::move(func))));
}
void reg_bin_cs_cmp(OpcodeTable& cp, unsigned code, unsigned len, const char* name,
std::function<bool(Ref<CellSlice>, Ref<CellSlice>)> func) {
cp.insert(OpcodeInstr::mksimple(code, len, name, std::bind(exec_bin_cs_cmp, _1, name, std::move(func))));
}
void reg_ibin_cs_cmp(OpcodeTable& cp, unsigned code, unsigned len, const char* name,
std::function<int(Ref<CellSlice>, Ref<CellSlice>)> func) {
cp.insert(OpcodeInstr::mksimple(code, len, name, std::bind(exec_ibin_cs_cmp, _1, name, std::move(func))));
}
} // namespace
void register_cell_cmp_ops(OpcodeTable& cp0) {
reg_un_cs_cmp(cp0, 0xc700, 16, "SEMPTY", [](auto cs) { return cs->empty() && !cs->size_refs(); });
reg_un_cs_cmp(cp0, 0xc701, 16, "SDEMPTY", [](auto cs) { return cs->empty(); });
reg_un_cs_cmp(cp0, 0xc702, 16, "SREMPTY", [](auto cs) { return !cs->size_refs(); });
reg_un_cs_cmp(cp0, 0xc703, 16, "SDFIRST", [](auto cs) { return cs->prefetch_long(1) == -1; });
reg_ibin_cs_cmp(cp0, 0xc704, 16, "SDLEXCMP", [](auto cs1, auto cs2) { return cs1->lex_cmp(*cs2); });
reg_bin_cs_cmp(cp0, 0xc705, 16, "SDEQ", [](auto cs1, auto cs2) { return !cs1->lex_cmp(*cs2); });
reg_bin_cs_cmp(cp0, 0xc708, 16, "SDPFX", [](auto cs1, auto cs2) { return cs1->is_prefix_of(*cs2); });
reg_bin_cs_cmp(cp0, 0xc709, 16, "SDPFXREV", [](auto cs1, auto cs2) { return cs2->is_prefix_of(*cs1); });
reg_bin_cs_cmp(cp0, 0xc70a, 16, "SDPPFX", [](auto cs1, auto cs2) { return cs1->is_proper_prefix_of(*cs2); });
reg_bin_cs_cmp(cp0, 0xc70b, 16, "SDPPFXREV", [](auto cs1, auto cs2) { return cs2->is_proper_prefix_of(*cs1); });
reg_bin_cs_cmp(cp0, 0xc70c, 16, "SDSFX", [](auto cs1, auto cs2) { return cs1->is_suffix_of(*cs2); });
reg_bin_cs_cmp(cp0, 0xc70d, 16, "SDSFXREV", [](auto cs1, auto cs2) { return cs2->is_suffix_of(*cs1); });
reg_bin_cs_cmp(cp0, 0xc70e, 16, "SDPSFX", [](auto cs1, auto cs2) { return cs1->is_proper_suffix_of(*cs2); });
reg_bin_cs_cmp(cp0, 0xc70f, 16, "SDPSFXREV", [](auto cs1, auto cs2) { return cs2->is_proper_suffix_of(*cs1); });
reg_iun_cs_cmp(cp0, 0xc710, 16, "SDCNTLEAD0", [](auto cs) { return cs->count_leading(0); });
reg_iun_cs_cmp(cp0, 0xc711, 16, "SDCNTLEAD1", [](auto cs) { return cs->count_leading(1); });
reg_iun_cs_cmp(cp0, 0xc712, 16, "SDCNTTRAIL0", [](auto cs) { return cs->count_trailing(0); });
reg_iun_cs_cmp(cp0, 0xc713, 16, "SDCNTTRAIL1", [](auto cs) { return cs->count_trailing(1); });
}
int exec_new_builder(VmState* st) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute NEWC";
stack.push_builder(Ref<CellBuilder>{true});
return 0;
}
int exec_builder_to_cell(VmState* st) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute ENDC";
stack.check_underflow(1);
stack.push_cell(stack.pop_builder()->finalize_copy());
return 0;
}
int exec_builder_to_special_cell(VmState* st) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute ENDXC";
stack.check_underflow(2);
bool special = stack.pop_bool();
stack.push_cell(stack.pop_builder()->finalize_copy(special));
return 0;
}
inline void check_space(const CellBuilder& builder, unsigned bits, unsigned refs = 0) {
if (!builder.can_extend_by(bits, refs)) {
throw VmError{Excno::cell_ov};
}
}
int store_int_common_fail(int code, Stack& stack, Ref<CellBuilder> builder, td::RefInt256 x, unsigned args) {
if (!(args & 2)) {
stack.push_int_quiet(std::move(x), true);
stack.push_builder(std::move(builder));
} else {
stack.push_builder(std::move(builder));
stack.push_int_quiet(std::move(x), true);
}
stack.push_smallint(code);
return 0;
}
int exec_store_int_common(Stack& stack, unsigned bits, unsigned args) {
bool sgnd = !(args & 1);
Ref<CellBuilder> builder;
td::RefInt256 x;
if (!(args & 2)) {
builder = stack.pop_builder();
x = stack.pop_int();
} else {
x = stack.pop_int();
builder = stack.pop_builder();
}
if (!builder->can_extend_by(bits)) {
if (args & 4) {
return store_int_common_fail(-1, stack, std::move(builder), std::move(x), args);
}
throw VmError{Excno::cell_ov};
}
if (!x->fits_bits(bits, sgnd)) {
if (args & 4) {
return store_int_common_fail(1, stack, std::move(builder), std::move(x), args);
}
throw VmError{Excno::range_chk};
}
builder.write().store_int256(*x, bits, sgnd);
stack.push_builder(std::move(builder));
if (args & 4) {
stack.push_smallint(0);
}
return 0;
}
int exec_store_int(VmState* st, unsigned args, bool sgnd) {
unsigned bits = (args & 0xff) + 1;
Stack& stack = st->get_stack();
VM_LOG(st) << "execute ST" << (sgnd ? 'I' : 'U') << ' ' << bits;
stack.check_underflow(2);
return exec_store_int_common(stack, bits, !sgnd);
}
int exec_store_ref(VmState* st, bool quiet) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute STREF" << (quiet ? "Q\n" : "\n");
stack.check_underflow(2);
auto builder = stack.pop_builder();
auto cell = stack.pop_cell();
if (!builder->can_extend_by(0, 1)) {
if (!quiet) {
throw VmError{Excno::cell_ov};
}
stack.push_cell(std::move(cell));
stack.push_builder(std::move(builder));
stack.push_smallint(-1);
return 0;
}
builder.write().store_ref(std::move(cell));
stack.push_builder(std::move(builder));
if (quiet) {
stack.push_smallint(0);
}
return 0;
}
int exec_store_ref_rev(VmState* st, bool quiet) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute STREFR" << (quiet ? "Q\n" : "\n");
stack.check_underflow(2);
auto cell = stack.pop_cell();
auto builder = stack.pop_builder();
if (!builder->can_extend_by(0, 1)) {
if (!quiet) {
throw VmError{Excno::cell_ov};
}
stack.push_builder(std::move(builder));
stack.push_cell(std::move(cell));
stack.push_smallint(-1);
return 0;
}
builder.write().store_ref(std::move(cell));
stack.push_builder(std::move(builder));
if (quiet) {
stack.push_smallint(0);
}
return 0;
}
int exec_store_builder_as_ref(VmState* st, bool quiet) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute STBREF\n";
stack.check_underflow(2);
auto builder = stack.pop_builder();
auto builder2 = stack.pop_builder();
if (!builder->can_extend_by(0, 1)) {
if (!quiet) {
throw VmError{Excno::cell_ov};
}
stack.push_builder(std::move(builder2));
stack.push_builder(std::move(builder));
stack.push_smallint(-1);
return 0;
}
builder.write().store_ref(builder2->finalize_copy());
stack.push_builder(std::move(builder));
if (quiet) {
stack.push_smallint(0);
}
return 0;
}
int exec_store_builder_as_ref_rev(VmState* st, bool quiet) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute STBREFR\n";
stack.check_underflow(2);
auto builder2 = stack.pop_builder();
auto builder = stack.pop_builder();
if (!builder->can_extend_by(0, 1)) {
if (!quiet) {
throw VmError{Excno::cell_ov};
}
stack.push_builder(std::move(builder));
stack.push_builder(std::move(builder2));
stack.push_smallint(-1);
return 0;
}
builder.write().store_ref(builder2->finalize_copy());
stack.push_builder(std::move(builder));
if (quiet) {
stack.push_smallint(0);
}
return 0;
}
int exec_store_slice(VmState* st, bool quiet) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute STSLICE\n";
stack.check_underflow(2);
auto builder = stack.pop_builder();
auto cs = stack.pop_cellslice();
if (!builder->can_extend_by(cs->size(), cs->size_refs())) {
if (!quiet) {
throw VmError{Excno::cell_ov};
}
stack.push_cellslice(std::move(cs));
stack.push_builder(std::move(builder));
stack.push_smallint(-1);
return 0;
}
cell_builder_add_slice(builder.write(), *cs);
stack.push_builder(std::move(builder));
if (quiet) {
stack.push_smallint(0);
}
return 0;
}
int exec_store_slice_rev(VmState* st, bool quiet) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute STSLICER\n";
stack.check_underflow(2);
auto cs = stack.pop_cellslice();
auto builder = stack.pop_builder();
if (!builder->can_extend_by(cs->size(), cs->size_refs())) {
if (!quiet) {
throw VmError{Excno::cell_ov};
}
stack.push_builder(std::move(builder));
stack.push_cellslice(std::move(cs));
stack.push_smallint(-1);
return 0;
}
cell_builder_add_slice(builder.write(), *cs);
stack.push_builder(std::move(builder));
if (quiet) {
stack.push_smallint(0);
}
return 0;
}
int exec_store_builder(VmState* st, bool quiet) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute STB\n";
stack.check_underflow(2);
auto builder = stack.pop_builder();
auto cb2 = stack.pop_builder();
if (!builder->can_extend_by(cb2->size(), cb2->size_refs())) {
if (!quiet) {
throw VmError{Excno::cell_ov};
}
stack.push_builder(std::move(cb2));
stack.push_builder(std::move(builder));
stack.push_smallint(-1);
return 0;
}
builder.write().append_builder(std::move(cb2));
stack.push_builder(std::move(builder));
if (quiet) {
stack.push_smallint(0);
}
return 0;
}
int exec_store_builder_rev(VmState* st, bool quiet) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute STBR\n";
stack.check_underflow(2);
auto cb2 = stack.pop_builder();
auto builder = stack.pop_builder();
if (!builder->can_extend_by(cb2->size(), cb2->size_refs())) {
if (!quiet) {
throw VmError{Excno::cell_ov};
}
stack.push_builder(std::move(builder));
stack.push_builder(std::move(cb2));
stack.push_smallint(-1);
return 0;
}
builder.write().append_builder(std::move(cb2));
stack.push_builder(std::move(builder));
if (quiet) {
stack.push_smallint(0);
}
return 0;
}
int exec_store_int_var(VmState* st, unsigned args) {
bool sgnd = !(args & 1);
Stack& stack = st->get_stack();
VM_LOG(st) << "execute ST" << (sgnd ? 'I' : 'U') << 'X' << ((args & 2) ? "R" : "") << ((args & 4) ? "Q\n" : "\n");
stack.check_underflow(3);
unsigned bits = stack.pop_smallint_range(256 + sgnd);
return exec_store_int_common(stack, bits, args);
}
std::string dump_store_int_var(CellSlice& cs, unsigned args) {
bool sgnd = !(args & 1);
std::string s = "ST";
s += sgnd ? 'I' : 'U';
s += 'X';
if (args & 2) {
s += 'R';
}
if (args & 4) {
s += 'Q';
}
return s;
}
int exec_store_int_fixed(VmState* st, unsigned args) {
unsigned bits = (args & 0xff) + 1;
args >>= 8;
bool sgnd = !(args & 1);
Stack& stack = st->get_stack();
VM_LOG(st) << "execute ST" << (sgnd ? 'I' : 'U') << ((args & 2) ? "R" : "") << ((args & 4) ? "Q " : " ") << bits;
stack.check_underflow(2);
return exec_store_int_common(stack, bits, args);
}
std::string dump_store_int_fixed(CellSlice& cs, unsigned args) {
unsigned bits = (args & 0xff) + 1;
bool sgnd = !(args & 0x100);
std::ostringstream s{"ST"};
s << (sgnd ? 'I' : 'U');
if (args & 0x200) {
s << 'R';
}
if (args & 0x400) {
s << 'Q';
}
s << ' ' << bits;
return s.str();
}
int exec_store_const_ref(VmState* st, CellSlice& cs, unsigned args, int pfx_bits) {
unsigned refs = (args & 1) + 1;
if (!cs.have_refs(refs)) {
throw VmError{Excno::inv_opcode, "no references left for a STREFCONST instruction"};
}
cs.advance(pfx_bits);
Stack& stack = st->get_stack();
VM_LOG(st) << "execute STREF" << refs << "CONST\n";
stack.check_underflow(1);
auto builder = stack.pop_builder();
check_space(*builder, 0, refs);
do {
builder.write().store_ref(cs.fetch_ref());
} while (--refs > 0);
stack.push_builder(std::move(builder));
return 0;
}
std::string dump_store_const_ref(CellSlice& cs, unsigned args, int pfx_bits) {
unsigned refs = (args & 1) + 1;
if (!cs.have_refs(refs)) {
return "";
}
cs.advance(pfx_bits);
cs.advance_refs(refs);
return refs > 1 ? (std::string{"STREF"} + (char)('0' + refs) + "CONST") : "STREFCONST";
}
int compute_len_store_const_ref(const CellSlice& cs, unsigned args, int pfx_bits) {
unsigned refs = (args & 1) + 1;
return cs.have_refs(refs) ? ((refs << 16) + pfx_bits) : 0;
}
int exec_store_le_int(VmState* st, unsigned args) {
unsigned bits = (args & 2) ? 64 : 32;
bool sgnd = !(args & 1);
Stack& stack = st->get_stack();
VM_LOG(st) << "execute ST" << (sgnd ? 'I' : 'U') << "LE" << bits / 8;
stack.check_underflow(2);
auto builder = stack.pop_builder();
auto x = stack.pop_int();
check_space(*builder, bits);
if (!(sgnd ? x->signed_fits_bits(bits) : x->unsigned_fits_bits(bits))) {
throw VmError{Excno::range_chk};
}
unsigned char buff[8];
st->ensure_throw(x->export_bytes_lsb(buff, bits >> 3, sgnd));
builder.write().store_bytes(buff, bits >> 3);
stack.push_builder(std::move(builder));
return 0;
}
std::string dump_store_le_int(CellSlice& cs, unsigned args) {
bool sgnd = !(args & 1);
return std::string{"ST"} + (sgnd ? 'I' : 'U') + "LE" + ((args & 2) ? '8' : '4');
}
int exec_int_builder_func(VmState* st, std::string name, const std::function<int(Ref<CellBuilder>)>& func) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute " << name;
stack.check_underflow(1);
stack.push_smallint(func(stack.pop_builder()));
return 0;
}
int exec_2int_builder_func(VmState* st, std::string name,
const std::function<std::pair<int, int>(Ref<CellBuilder>)>& func) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute " << name;
stack.check_underflow(1);
std::pair<int, int> res = func(stack.pop_builder());
stack.push_smallint(res.first);
stack.push_smallint(res.second);
return 0;
}
int exec_builder_chk_bits(VmState* st, unsigned args, bool quiet) {
unsigned bits = (args & 0xff) + 1;
Stack& stack = st->get_stack();
VM_LOG(st) << "execute BCHKBITS" << (quiet ? "Q " : " ") << bits;
stack.check_underflow(1);
auto builder = stack.pop_builder();
if (quiet) {
stack.push_smallint(builder->can_extend_by(bits) ? -1 : 0);
} else {
check_space(*builder, bits);
}
return 0;
}
int exec_builder_chk_bits_refs(VmState* st, unsigned mode) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute BCHK" << ((mode & 1) ? "BIT" : "") << ((mode & 2) ? "REFS" : "S") << ((mode & 4) ? "Q" : "");
stack.check_underflow(1 + (mode & 1) + ((mode & 2) >> 1));
unsigned refs = (mode & 2) ? stack.pop_smallint_range(7) : 0;
unsigned bits = (mode & 1) ? stack.pop_smallint_range(1023) : 0;
auto builder = stack.pop_builder();
if (mode & 4) {
stack.push_smallint(builder->can_extend_by(bits, refs) ? -1 : 0);
} else {
check_space(*builder, bits, refs);
}
return 0;
}
int exec_store_same(VmState* st, const char* name, int val) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute " << name;
stack.check_underflow(2 + (val < 0));
if (val < 0) {
val = stack.pop_smallint_range(1);
}
unsigned bits = stack.pop_smallint_range(1023);
auto builder = stack.pop_builder();
check_space(*builder, bits);
builder.write().reserve_slice(bits) = (bool)val;
stack.push_builder(std::move(builder));
return 0;
}
int exec_store_const_slice(VmState* st, CellSlice& cs, unsigned args, int pfx_bits) {
unsigned refs = (args >> 3) & 3;
unsigned data_bits = (args & 7) * 8 + 2;
if (!cs.have(pfx_bits + data_bits)) {
throw VmError{Excno::inv_opcode, "not enough data bits for a STSLICECONST instruction"};
}
if (!cs.have_refs(refs)) {
throw VmError{Excno::inv_opcode, "not enough references for a STSLICECONST instruction"};
}
Stack& stack = st->get_stack();
cs.advance(pfx_bits);
auto slice = cs.fetch_subslice(data_bits, refs);
slice.unique_write().remove_trailing();
VM_LOG(st) << "execute STSLICECONST " << slice;
auto builder = stack.pop_builder();
check_space(*builder, slice->size(), slice->size_refs());
cell_builder_add_slice(builder.write(), *slice);
stack.push_builder(std::move(builder));
return 0;
}
std::string dump_store_const_slice(CellSlice& cs, unsigned args, int pfx_bits) {
unsigned refs = (args >> 3) & 3;
unsigned data_bits = (args & 7) * 8 + 2;
return dump_push_slice_common(cs, data_bits, refs, pfx_bits, "STSLICECONST ");
}
int compute_len_store_const_slice(const CellSlice& cs, unsigned args, int pfx_bits) {
unsigned refs = (args >> 3) & 3;
unsigned data_bits = (args & 7) * 8 + 2;
return compute_len_push_slice_common(cs, data_bits, refs, pfx_bits);
}
void register_cell_serialize_ops(OpcodeTable& cp0) {
using namespace std::placeholders;
cp0.insert(OpcodeInstr::mksimple(0xc8, 8, "NEWC", exec_new_builder))
.insert(OpcodeInstr::mksimple(0xc9, 8, "ENDC", exec_builder_to_cell))
.insert(
OpcodeInstr::mkfixed(0xca, 8, 8, instr::dump_1c_l_add(1, "STI "), std::bind(exec_store_int, _1, _2, true)))
.insert(
OpcodeInstr::mkfixed(0xcb, 8, 8, instr::dump_1c_l_add(1, "STU "), std::bind(exec_store_int, _1, _2, false)))
.insert(OpcodeInstr::mksimple(0xcc, 8, "STREF", std::bind(exec_store_ref, _1, false)))
.insert(OpcodeInstr::mksimple(0xcd, 8, "ENDCST", std::bind(exec_store_builder_as_ref_rev, _1, false)))
.insert(OpcodeInstr::mksimple(0xce, 8, "STSLICE", std::bind(exec_store_slice, _1, false)))
.insert(OpcodeInstr::mkfixed(0xcf00 >> 3, 13, 3, dump_store_int_var, exec_store_int_var))
.insert(OpcodeInstr::mkfixed(0xcf08 >> 3, 13, 11, dump_store_int_fixed, exec_store_int_fixed))
.insert(OpcodeInstr::mksimple(0xcf10, 16, "STREF", std::bind(exec_store_ref, _1, false)))
.insert(OpcodeInstr::mksimple(0xcf11, 16, "STBREF", std::bind(exec_store_builder_as_ref, _1, false)))
.insert(OpcodeInstr::mksimple(0xcf12, 16, "STSLICE", std::bind(exec_store_slice, _1, false)))
.insert(OpcodeInstr::mksimple(0xcf13, 16, "STB", std::bind(exec_store_builder, _1, false)))
.insert(OpcodeInstr::mksimple(0xcf14, 16, "STREFR", std::bind(exec_store_ref_rev, _1, false)))
.insert(OpcodeInstr::mksimple(0xcf15, 16, "STBREFR", std::bind(exec_store_builder_as_ref_rev, _1, false)))
.insert(OpcodeInstr::mksimple(0xcf16, 16, "STSLICER", std::bind(exec_store_slice_rev, _1, false)))
.insert(OpcodeInstr::mksimple(0xcf17, 16, "STBR", std::bind(exec_store_builder_rev, _1, false)))
.insert(OpcodeInstr::mksimple(0xcf18, 16, "STREFQ", std::bind(exec_store_ref, _1, true)))
.insert(OpcodeInstr::mksimple(0xcf19, 16, "STBREFQ", std::bind(exec_store_builder_as_ref, _1, true)))
.insert(OpcodeInstr::mksimple(0xcf1a, 16, "STSLICEQ", std::bind(exec_store_slice, _1, true)))
.insert(OpcodeInstr::mksimple(0xcf1b, 16, "STBQ", std::bind(exec_store_builder, _1, true)))
.insert(OpcodeInstr::mksimple(0xcf1c, 16, "STREFRQ", std::bind(exec_store_ref_rev, _1, true)))
.insert(OpcodeInstr::mksimple(0xcf1d, 16, "STBREFRQ", std::bind(exec_store_builder_as_ref_rev, _1, true)))
.insert(OpcodeInstr::mksimple(0xcf1e, 16, "STSLICERQ", std::bind(exec_store_slice_rev, _1, true)))
.insert(OpcodeInstr::mksimple(0xcf1f, 16, "STBRQ", std::bind(exec_store_builder_rev, _1, true)))
.insert(OpcodeInstr::mkextrange(0xcf20, 0xcf22, 16, 1, dump_store_const_ref, exec_store_const_ref,
compute_len_store_const_ref))
.insert(OpcodeInstr::mksimple(0xcf23, 16, "ENDXC", exec_builder_to_special_cell))
.insert(OpcodeInstr::mkfixed(0xcf28 >> 2, 14, 2, dump_store_le_int, exec_store_le_int))
.insert(OpcodeInstr::mksimple(
0xcf31, 16, "BBITS",
std::bind(exec_int_builder_func, _1, "BBITS", [](Ref<CellBuilder> b) { return b->size(); })))
.insert(OpcodeInstr::mksimple(
0xcf32, 16, "BREFS",
std::bind(exec_int_builder_func, _1, "BREFS", [](Ref<CellBuilder> b) { return b->size_refs(); })))
.insert(OpcodeInstr::mksimple(
0xcf33, 16, "BBITREFS",
std::bind(exec_2int_builder_func, _1, "BBITSREFS",
[](Ref<CellBuilder> b) { return std::make_pair(b->size(), b->size_refs()); })))
.insert(OpcodeInstr::mksimple(
0xcf35, 16, "BREMBITS",
std::bind(exec_int_builder_func, _1, "BREMBITS", [](Ref<CellBuilder> b) { return b->remaining_bits(); })))
.insert(OpcodeInstr::mksimple(
0xcf36, 16, "BREMREFS",
std::bind(exec_int_builder_func, _1, "BREMREFS", [](Ref<CellBuilder> b) { return b->remaining_refs(); })))
.insert(OpcodeInstr::mksimple(
0xcf37, 16, "BREMBITREFS",
std::bind(exec_2int_builder_func, _1, "BREMBITSREFS",
[](Ref<CellBuilder> b) { return std::make_pair(b->remaining_bits(), b->remaining_refs()); })))
.insert(OpcodeInstr::mkfixed(0xcf38, 16, 8, instr::dump_1c_l_add(1, "BCHKBITS "),
std::bind(exec_builder_chk_bits, _1, _2, false)))
.insert(OpcodeInstr::mksimple(0xcf39, 16, "BCHKBITS", std::bind(exec_builder_chk_bits_refs, _1, 1)))
.insert(OpcodeInstr::mksimple(0xcf3a, 16, "BCHKREFS", std::bind(exec_builder_chk_bits_refs, _1, 2)))
.insert(OpcodeInstr::mksimple(0xcf3b, 16, "BCHKBITREFS", std::bind(exec_builder_chk_bits_refs, _1, 3)))
.insert(OpcodeInstr::mkfixed(0xcf3c, 16, 8, instr::dump_1c_l_add(1, "BCHKBITSQ "),
std::bind(exec_builder_chk_bits, _1, _2, true)))
.insert(OpcodeInstr::mksimple(0xcf3d, 16, "BCHKBITSQ", std::bind(exec_builder_chk_bits_refs, _1, 5)))
.insert(OpcodeInstr::mksimple(0xcf3e, 16, "BCHKREFSQ", std::bind(exec_builder_chk_bits_refs, _1, 6)))
.insert(OpcodeInstr::mksimple(0xcf3f, 16, "BCHKBITREFSQ", std::bind(exec_builder_chk_bits_refs, _1, 7)))
.insert(OpcodeInstr::mksimple(0xcf40, 16, "STZEROES", std::bind(exec_store_same, _1, "STZEROES", 0)))
.insert(OpcodeInstr::mksimple(0xcf41, 16, "STONES", std::bind(exec_store_same, _1, "STONES", 1)))
.insert(OpcodeInstr::mksimple(0xcf42, 16, "STSAME", std::bind(exec_store_same, _1, "STSAME", -1)))
.insert(OpcodeInstr::mkext(0xcf80 >> 7, 9, 5, dump_store_const_slice, exec_store_const_slice,
compute_len_store_const_slice));
}
int exec_cell_to_slice(VmState* st) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute CTOS";
auto cell = stack.pop_cell();
stack.push_cellslice(load_cell_slice_ref(std::move(cell)));
return 0;
}
int exec_cell_to_slice_maybe_special(VmState* st) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute XCTOS";
bool is_special;
auto cell = stack.pop_cell();
stack.push_cellslice(load_cell_slice_ref_special(std::move(cell), is_special));
stack.push_bool(is_special);
return 0;
}
int exec_load_special_cell(VmState* st, bool quiet) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute XLOAD" << (quiet ? "Q" : "");
auto cell = stack.pop_cell();
stack.push_cell(cell);
if (quiet) {
stack.push_bool(true);
}
return 0;
}
int exec_slice_chk_empty(VmState* st) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute ENDS";
auto cs = stack.pop_cellslice();
if (cs->size() || cs->size_refs()) {
throw VmError{Excno::cell_und, "extra data remaining in deserialized cell"};
}
return 0;
}
int exec_load_int_common(Stack& stack, unsigned bits, unsigned mode) {
auto cs = stack.pop_cellslice();
if (!cs->have(bits)) {
if (!(mode & 4)) {
throw VmError{Excno::cell_und};
}
if (!(mode & 2)) {
stack.push_cellslice(std::move(cs));
}
stack.push_smallint(0);
return 0;
}
bool sgnd = !(mode & 1);
if (mode & 2) {
stack.push_int(cs->prefetch_int256(bits, sgnd));
} else {
stack.push_int(cs.write().fetch_int256(bits, sgnd));
stack.push_cellslice(std::move(cs));
}
if (mode & 4) {
stack.push_smallint(-1);
}
return 0;
}
int exec_load_int_fixed(VmState* st, unsigned args, unsigned mode) {
unsigned bits = (args & 0xff) + 1;
bool sgnd = !(mode & 1);
VM_LOG(st) << "execute " << (mode & 2 ? "P" : "") << "LD" << (sgnd ? 'I' : 'U') << (mode & 4 ? "Q " : " ") << bits;
return exec_load_int_common(st->get_stack(), bits, mode);
}
int exec_preload_ref_fixed(VmState* st, unsigned args) {
unsigned idx = args & 3;
Stack& stack = st->get_stack();
VM_LOG(st) << "execute PLDREFIDX " << idx;
auto cs = stack.pop_cellslice();
if (!cs->have_refs(idx + 1)) {
throw VmError{Excno::cell_und};
}
stack.push_cell(cs->prefetch_ref(idx));
return 0;
}
int exec_preload_ref(VmState* st) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute PLDREFVAR";
stack.check_underflow(2);
unsigned idx = stack.pop_smallint_range(3);
auto cs = stack.pop_cellslice();
if (!cs->have_refs(idx + 1)) {
throw VmError{Excno::cell_und};
}
stack.push_cell(cs->prefetch_ref(idx));
return 0;
}
int exec_load_ref(VmState* st, unsigned mode) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute " << (mode & 2 ? "P" : "") << "LDREF" << (mode & 4 ? "Q" : "");
auto cs = stack.pop_cellslice();
if (!cs->have_refs()) {
if (!(mode & 4)) {
throw VmError{Excno::cell_und};
}
stack.push_smallint(0);
return 0;
}
if (mode & 2) {
stack.push_cell(cs->prefetch_ref());
} else {
stack.push_cell(cs.write().fetch_ref());
stack.push_cellslice(std::move(cs));
}
if (mode & 4) {
stack.push_smallint(-1);
}
return 0;
}
int exec_load_ref_rev_to_slice(VmState* st, unsigned mode) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute " << (mode & 2 ? "P" : "") << "LDREFRTOS" << (mode & 4 ? "Q" : "");
auto cs = stack.pop_cellslice();
if (!cs->have_refs()) {
if (!(mode & 4)) {
throw VmError{Excno::cell_und};
}
stack.push_smallint(0);
return 0;
}
if (mode & 2) {
stack.push_cellslice(load_cell_slice_ref(cs->prefetch_ref()));
} else {
auto cell = cs.write().fetch_ref();
stack.push_cellslice(std::move(cs));
stack.push_cellslice(load_cell_slice_ref(std::move(cell)));
}
if (mode & 4) {
stack.push_smallint(-1);
}
return 0;
}
int exec_load_slice_common(Stack& stack, unsigned bits, unsigned mode) {
auto cs = stack.pop_cellslice();
if (!cs->have(bits)) {
if (!(mode & 2)) {
throw VmError{Excno::cell_und};
}
if (!(mode & 1)) {
stack.push_cellslice(std::move(cs));
}
stack.push_smallint(0);
return 0;
}
if (mode & 1) {
stack.push_cellslice(cs->prefetch_subslice(bits));
} else {
stack.push_cellslice(cs.write().fetch_subslice(bits));
stack.push_cellslice(std::move(cs));
}
if (mode & 2) {
stack.push_smallint(-1);
}
return 0;
}
int exec_load_slice_fixed(VmState* st, unsigned args) {
unsigned bits = (args & 0xff) + 1;
VM_LOG(st) << "execute LDSLICE " << bits;
return exec_load_slice_common(st->get_stack(), bits, 0);
}
int exec_load_int_var(VmState* st, unsigned args) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute " << (args & 2 ? "PLD" : "LD") << (args & 1 ? "UX" : "IX") << (args & 4 ? "Q\n" : "\n");
stack.check_underflow(2);
unsigned bits = stack.pop_smallint_range(257 - (args & 1));
return exec_load_int_common(stack, bits, args & 7);
}
std::string dump_load_int_var(CellSlice&, unsigned args) {
return (args & 2 ? std::string{"PLD"} : std::string{"LD"}) + (args & 1 ? "UX" : "IX") + (args & 4 ? "Q" : "");
}
int exec_load_int_fixed2(VmState* st, unsigned args) {
unsigned bits = (args & 0xff) + 1;
args >>= 8;
VM_LOG(st) << "execute " << (args & 2 ? "PLD" : "LD") << (args & 1 ? "U" : "I") << (args & 4 ? "Q " : " ") << bits;
return exec_load_int_common(st->get_stack(), bits, args & 7);
}
std::string dump_load_int_fixed2(CellSlice&, unsigned args) {
std::ostringstream os{args & 0x200 ? "PLD" : "LD"};
os << (args & 0x100 ? 'U' : 'I');
if (args & 0x400) {
os << 'Q';
}
os << ' ' << ((args & 0xff) + 1);
return os.str();
}
int exec_preload_uint_fixed_0e(VmState* st, unsigned args) {
unsigned bits = ((args & 7) + 1) << 5;
Stack& stack = st->get_stack();
VM_LOG(st) << "execute PLDUZ " << bits;
auto cs = stack.pop_cellslice();
auto x = cs->prefetch_int256_zeroext(bits, false);
stack.push_cellslice(std::move(cs));
stack.push_int(std::move(x));
return 0;
}
std::string dump_preload_uint_fixed_0e(CellSlice&, unsigned args) {
std::ostringstream os{"PLDUZ "};
unsigned bits = ((args & 7) + 1) << 5;
os << bits;
return os.str();
}
int exec_load_slice(VmState* st, unsigned args) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute " << (args & 1 ? "PLDSLICEX" : "LDSLICEX") << (args & 2 ? "Q\n" : "\n");
stack.check_underflow(2);
unsigned bits = stack.pop_smallint_range(1023);
return exec_load_slice_common(stack, bits, args);
}
std::string dump_load_slice(CellSlice&, unsigned args) {
return std::string{args & 1 ? "P" : ""} + "LDSLICEX" + (args & 2 ? "Q" : "");
}
int exec_load_slice_fixed2(VmState* st, unsigned args) {
unsigned bits = (args & 0xff) + 1;
args >>= 8;
VM_LOG(st) << "execute " << (args & 1 ? "PLDSLICE" : "LDSLICE") << (args & 2 ? "Q " : " ") << bits;
return exec_load_slice_common(st->get_stack(), bits, args);
}
std::string dump_load_slice_fixed2(CellSlice&, unsigned args) {
unsigned bits = (args & 0xff) + 1;
std::ostringstream os{args & 0x100 ? "PLDSLICE" : "LDSLICE"};
if (args & 0x200) {
os << 'Q';
}
os << ' ' << bits;
return os.str();
}
int exec_slice_op_args(VmState* st, const char* name, unsigned max_arg1,
const std::function<bool(CellSlice&, unsigned)>& func) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute " << name;
stack.check_underflow(2);
unsigned x = stack.pop_smallint_range(max_arg1);
auto cs = stack.pop_cellslice();
if (!func(cs.write(), x)) {
throw VmError{Excno::cell_und};
}
stack.push_cellslice(std::move(cs));
return 0;
}
int exec_slice_op_args2(VmState* st, const char* name, unsigned max_arg1, unsigned max_arg2,
const std::function<bool(CellSlice&, unsigned, unsigned)>& func) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute " << name;
stack.check_underflow(3);
unsigned y = stack.pop_smallint_range(max_arg2);
unsigned x = stack.pop_smallint_range(max_arg1);
auto cs = stack.pop_cellslice();
if (!func(cs.write(), x, y)) {
throw VmError{Excno::cell_und};
}
stack.push_cellslice(std::move(cs));
return 0;
}
int exec_slice_begins_with_common(VmState* st, Ref<CellSlice> cs2, bool quiet) {
Stack& stack = st->get_stack();
auto cs = stack.pop_cellslice();
if (!cs->has_prefix(*cs2)) {
if (!quiet) {
throw VmError{Excno::cell_und, "slice does not begin with expected data bits"};
}
stack.push_cellslice(std::move(cs));
stack.push_smallint(0);
return 0;
}
cs.write().advance(cs2->size());
stack.push_cellslice(std::move(cs));
if (quiet) {
stack.push_smallint(-1);
}
return 0;
}
int exec_slice_begins_with(VmState* st, bool quiet) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute SDBEGINSX" << (quiet ? "Q\n" : "\n");
stack.check_underflow(2);
return exec_slice_begins_with_common(st, stack.pop_cellslice(), quiet);
}
int exec_slice_begins_with_const(VmState* st, CellSlice& cs, unsigned args, int pfx_bits) {
bool quiet = args & 128;
unsigned data_bits = (args & 127) * 8 + 3;
if (!cs.have(pfx_bits + data_bits)) {
throw VmError{Excno::inv_opcode, "not enough data bits for a SDBEGINS instruction"};
}
cs.advance(pfx_bits);
auto slice = cs.fetch_subslice(data_bits);
slice.unique_write().remove_trailing();
VM_LOG(st) << "execute SDBEGINS" << (quiet ? "Q " : " ") << slice;
return exec_slice_begins_with_common(st, slice, quiet);
}
std::string dump_slice_begins_with_const(CellSlice& cs, unsigned args, int pfx_bits) {
bool quiet = args & 128;
unsigned data_bits = (args & 127) * 8 + 3;
return dump_push_slice_common(cs, data_bits, 0, pfx_bits, quiet ? "SDBEGINSQ " : "SDBEGINS ");
}
int compute_len_slice_begins_with_const(const CellSlice& cs, unsigned args, int pfx_bits) {
unsigned data_bits = (args & 127) * 8 + 3;
return compute_len_push_slice_common(cs, data_bits, 0, pfx_bits);
}
int exec_subslice(VmState* st) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute SUBSLICE\n";
stack.check_underflow(5);
unsigned r2 = stack.pop_smallint_range(4), l2 = stack.pop_smallint_range(1023);
unsigned r1 = stack.pop_smallint_range(4), l1 = stack.pop_smallint_range(1023);
auto cs = stack.pop_cellslice();
if (!cs.write().skip_first(l1, r1) || !cs.unique_write().only_first(l2, r2)) {
throw VmError{Excno::cell_und};
}
stack.push_cellslice(std::move(cs));
return 0;
}
int exec_split(VmState* st, bool quiet) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute SPLIT" << (quiet ? "Q\n" : "\n");
stack.check_underflow(3);
unsigned refs = stack.pop_smallint_range(4), bits = stack.pop_smallint_range(1023);
auto cs = stack.pop_cellslice();
if (!cs->have(bits) || !cs->have_refs(refs)) {
if (!quiet) {
throw VmError{Excno::cell_und};
}
stack.push_cellslice(std::move(cs));
stack.push_smallint(0);
return 0;
}
auto cs2 = cs;
cs2.write().only_first(bits, refs);
cs.write().skip_first(bits, refs);
stack.push_cellslice(std::move(cs2));
stack.push_cellslice(std::move(cs));
if (quiet) {
stack.push_smallint(-1);
}
return 0;
}
int exec_slice_chk_op_args(VmState* st, const char* name, unsigned max_arg1, bool quiet,
const std::function<bool(const CellSlice&, unsigned)>& func) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute " << name;
stack.check_underflow(2);
unsigned x = stack.pop_smallint_range(max_arg1);
auto cs = stack.pop_cellslice();
bool res = func(*cs, x);
if (quiet) {
stack.push_smallint(res ? -1 : 0);
} else if (!res) {
throw VmError{Excno::cell_und};
}
return 0;
}
int exec_slice_chk_op_args2(VmState* st, const char* name, unsigned max_arg1, unsigned max_arg2, bool quiet,
const std::function<bool(const CellSlice&, unsigned, unsigned)>& func) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute " << name;
stack.check_underflow(3);
unsigned y = stack.pop_smallint_range(max_arg2);
unsigned x = stack.pop_smallint_range(max_arg1);
auto cs = stack.pop_cellslice();
bool res = func(*cs, x, y);
if (quiet) {
stack.push_smallint(res ? -1 : 0);
} else if (!res) {
throw VmError{Excno::cell_und};
}
return 0;
}
int exec_slice_bits_refs(VmState* st, unsigned mode) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute S" << (mode & 1 ? "BIT" : "") << (mode & 2 ? "REF" : "") << "S\n";
stack.check_underflow(1);
auto cs = stack.pop_cellslice();
if (mode & 1) {
stack.push_smallint(cs->size());
}
if (mode & 2) {
stack.push_smallint(cs->size_refs());
}
return 0;
}
int exec_load_le_int(VmState* st, unsigned args) {
unsigned len = (args & 2) ? 8 : 4;
bool sgnd = !(args & 1);
Stack& stack = st->get_stack();
VM_LOG(st) << "execute " << (args & 4 ? "PLD" : "LD") << (sgnd ? 'I' : 'U') << "LE" << len
<< (args & 8 ? "Q\n" : "\n");
stack.check_underflow(1);
auto cs = stack.pop_cellslice();
if (!cs->have(len << 3)) {
if (args & 8) {
if (!(args & 4)) {
stack.push_cellslice(std::move(cs));
}
stack.push_smallint(0);
return 0;
}
throw VmError{Excno::cell_und};
}
unsigned char buff[8];
st->ensure_throw(cs->prefetch_bytes(buff, len));
td::RefInt256 x{true};
st->ensure_throw(x.unique_write().import_bytes_lsb(buff, len, sgnd));
stack.push_int(std::move(x));
if (!(args & 4)) {
st->ensure_throw(cs.write().advance(len << 3));
stack.push_cellslice(std::move(cs));
}
if (args & 8) {
stack.push_smallint(-1);
}
return 0;
}
std::string dump_load_le_int(CellSlice& cs, unsigned args) {
bool sgnd = !(args & 1);
return std::string{args & 4 ? "P" : ""} + "LD" + (sgnd ? 'I' : 'U') + "LE" + ((args & 2) ? '8' : '4') +
(args & 8 ? "Q" : "");
}
int exec_load_same(VmState* st, const char* name, int x) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute " << name;
stack.check_underflow(1 + (x < 0));
if (x < 0) {
x = stack.pop_smallint_range(1);
}
auto cs = stack.pop_cellslice();
unsigned n = cs->count_leading(x);
if (n > 0) {
cs.write().advance(n);
}
stack.push_smallint(n);
stack.push_cellslice(std::move(cs));
return 0;
}
void register_cell_deserialize_ops(OpcodeTable& cp0) {
using namespace std::placeholders;
cp0.insert(OpcodeInstr::mksimple(0xd0, 8, "CTOS", exec_cell_to_slice))
.insert(OpcodeInstr::mksimple(0xd1, 8, "ENDS", exec_slice_chk_empty))
.insert(
OpcodeInstr::mkfixed(0xd2, 8, 8, instr::dump_1c_l_add(1, "LDI "), std::bind(exec_load_int_fixed, _1, _2, 0)))
.insert(
OpcodeInstr::mkfixed(0xd3, 8, 8, instr::dump_1c_l_add(1, "LDU "), std::bind(exec_load_int_fixed, _1, _2, 1)))
.insert(OpcodeInstr::mksimple(0xd4, 8, "LDREF", std::bind(exec_load_ref, _1, 0)))
.insert(OpcodeInstr::mksimple(0xd5, 8, "LDREFRTOS", std::bind(exec_load_ref_rev_to_slice, _1, 0)))
.insert(OpcodeInstr::mkfixed(0xd6, 8, 8, instr::dump_1c_l_add(1, "LDSLICE "), exec_load_slice_fixed))
.insert(OpcodeInstr::mkfixed(0xd700 >> 3, 13, 3, dump_load_int_var, exec_load_int_var))
.insert(OpcodeInstr::mkfixed(0xd708 >> 3, 13, 11, dump_load_int_fixed2, exec_load_int_fixed2))
.insert(OpcodeInstr::mkfixed(0xd710 >> 3, 13, 3, dump_preload_uint_fixed_0e, exec_preload_uint_fixed_0e))
.insert(OpcodeInstr::mkfixed(0xd718 >> 2, 14, 2, dump_load_slice, exec_load_slice))
.insert(OpcodeInstr::mkfixed(0xd71c >> 2, 14, 10, dump_load_slice_fixed2, exec_load_slice_fixed2))
.insert(OpcodeInstr::mksimple(0xd720, 16, "SDCUTFIRST",
std::bind(exec_slice_op_args, _1, "SDCUTFIRST", 1023,
[](auto& cs, unsigned bits) { return cs.only_first(bits); })))
.insert(OpcodeInstr::mksimple(0xd721, 16, "SDSKIPFIRST",
std::bind(exec_slice_op_args, _1, "SDSKIPFIRST", 1023,
[](auto& cs, unsigned bits) { return cs.skip_first(bits); })))
.insert(OpcodeInstr::mksimple(0xd722, 16, "SDCUTLAST",
std::bind(exec_slice_op_args, _1, "SDCUTLAST", 1023,
[](auto& cs, unsigned bits) { return cs.only_last(bits); })))
.insert(OpcodeInstr::mksimple(0xd723, 16, "SDSKIPLAST",
std::bind(exec_slice_op_args, _1, "SDSKIPLAST", 1023,
[](auto& cs, unsigned bits) { return cs.skip_last(bits); })))
.insert(OpcodeInstr::mksimple(
0xd724, 16, "SDSUBSTR",
std::bind(exec_slice_op_args2, _1, "SDSUBSTR", 1023, 1023,
[](auto& cs, unsigned offs, unsigned bits) { return cs.skip_first(offs) && cs.only_first(bits); })))
.insert(OpcodeInstr::mksimple(0xd726, 16, "SDBEGINSX", std::bind(exec_slice_begins_with, _1, false)))
.insert(OpcodeInstr::mksimple(0xd727, 16, "SDBEGINSXQ", std::bind(exec_slice_begins_with, _1, true)))
.insert(OpcodeInstr::mkext(0xd728 >> 3, 13, 8, dump_slice_begins_with_const, exec_slice_begins_with_const,
compute_len_slice_begins_with_const))
.insert(OpcodeInstr::mksimple(
0xd730, 16, "SCUTFIRST",
std::bind(exec_slice_op_args2, _1, "SCUTFIRST", 1023, 4,
[](auto& cs, unsigned bits, unsigned refs) { return cs.only_first(bits, refs); })))
.insert(OpcodeInstr::mksimple(
0xd731, 16, "SSKIPFIRST",
std::bind(exec_slice_op_args2, _1, "SSKIPFIRST", 1023, 4,
[](auto& cs, unsigned bits, unsigned refs) { return cs.skip_first(bits, refs); })))
.insert(OpcodeInstr::mksimple(
0xd732, 16, "SCUTLAST",
std::bind(exec_slice_op_args2, _1, "SCUTLAST", 1023, 4,
[](auto& cs, unsigned bits, unsigned refs) { return cs.only_last(bits, refs); })))
.insert(OpcodeInstr::mksimple(
0xd733, 16, "SSKIPLAST",
std::bind(exec_slice_op_args2, _1, "SSKIPLAST", 1023, 4,
[](auto& cs, unsigned bits, unsigned refs) { return cs.skip_last(bits, refs); })))
.insert(OpcodeInstr::mksimple(0xd734, 16, "SUBSLICE", exec_subslice))
.insert(OpcodeInstr::mksimple(0xd736, 16, "SPLIT", std::bind(exec_split, _1, false)))
.insert(OpcodeInstr::mksimple(0xd737, 16, "SPLITQ", std::bind(exec_split, _1, true)))
.insert(OpcodeInstr::mksimple(0xd739, 16, "XCTOS", exec_cell_to_slice_maybe_special))
.insert(OpcodeInstr::mksimple(0xd73a, 16, "XLOAD", std::bind(exec_load_special_cell, _1, false)))
.insert(OpcodeInstr::mksimple(0xd73b, 16, "XLOADQ", std::bind(exec_load_special_cell, _1, true)))
.insert(OpcodeInstr::mksimple(0xd741, 16, "SCHKBITS",
std::bind(exec_slice_chk_op_args, _1, "SCHKBITS", 1023, false,
[](auto cs, unsigned bits) { return cs.have(bits); })))
.insert(OpcodeInstr::mksimple(0xd742, 16, "SCHKREFS",
std::bind(exec_slice_chk_op_args, _1, "SCHKREFS", 1023, false,
[](auto cs, unsigned refs) { return cs.have_refs(refs); })))
.insert(OpcodeInstr::mksimple(
0xd743, 16, "SCHKBITREFS",
std::bind(exec_slice_chk_op_args2, _1, "SCHKBITREFS", 1023, 4, false,
[](auto cs, unsigned bits, unsigned refs) { return cs.have(bits) && cs.have_refs(refs); })))
.insert(OpcodeInstr::mksimple(0xd745, 16, "SCHKBITSQ",
std::bind(exec_slice_chk_op_args, _1, "SCHKBITSQ", 1023, true,
[](auto cs, unsigned bits) { return cs.have(bits); })))
.insert(OpcodeInstr::mksimple(0xd746, 16, "SCHKREFSQ",
std::bind(exec_slice_chk_op_args, _1, "SCHKREFSQ", 1023, true,
[](auto cs, unsigned refs) { return cs.have_refs(refs); })))
.insert(OpcodeInstr::mksimple(
0xd747, 16, "SCHKBITREFSQ",
std::bind(exec_slice_chk_op_args2, _1, "SCHKBITREFSQ", 1023, 4, true,
[](auto cs, unsigned bits, unsigned refs) { return cs.have(bits) && cs.have_refs(refs); })))
.insert(OpcodeInstr::mksimple(0xd748, 16, "PLDREFVAR", exec_preload_ref))
.insert(OpcodeInstr::mksimple(0xd749, 16, "SBITS", std::bind(exec_slice_bits_refs, _1, 1)))
.insert(OpcodeInstr::mksimple(0xd74a, 16, "SREFS", std::bind(exec_slice_bits_refs, _1, 2)))
.insert(OpcodeInstr::mksimple(0xd74b, 16, "SBITREFS", std::bind(exec_slice_bits_refs, _1, 3)))
.insert(OpcodeInstr::mkfixed(0xd74c >> 2, 14, 2, instr::dump_1c_and(3, "PLDREFIDX "), exec_preload_ref_fixed))
.insert(OpcodeInstr::mkfixed(0xd75, 12, 4, dump_load_le_int, exec_load_le_int))
.insert(OpcodeInstr::mksimple(0xd760, 16, "LDZEROES", std::bind(exec_load_same, _1, "LDZEROES", 0)))
.insert(OpcodeInstr::mksimple(0xd761, 16, "LDONES", std::bind(exec_load_same, _1, "LDONES", 1)))
.insert(OpcodeInstr::mksimple(0xd762, 16, "LDSAME", std::bind(exec_load_same, _1, "LDSAME", -1)));
}
void register_cell_ops(OpcodeTable& cp0) {
register_cell_const_ops(cp0);
register_cell_cmp_ops(cp0);
register_cell_serialize_ops(cp0);
register_cell_deserialize_ops(cp0);
}
} // namespace vm