mirror of
https://github.com/ton-blockchain/ton
synced 2025-02-12 11:12:16 +00:00
updated func/fift
- updated func/fift - updated liteclient/liteserver - bugfixes
This commit is contained in:
parent
d41ce55305
commit
acf16718e6
45 changed files with 1360 additions and 185 deletions
|
@ -241,6 +241,28 @@ bool MsgAddressInt::extract_std_address(vm::CellSlice& cs, ton::WorkchainId& wor
|
|||
return false;
|
||||
}
|
||||
|
||||
bool MsgAddressInt::store_std_address(vm::CellBuilder& cb, ton::WorkchainId workchain,
|
||||
const ton::StdSmcAddress& addr) const {
|
||||
if (workchain >= -128 && workchain < 128) {
|
||||
return cb.store_long_bool(4, 3) // addr_std$10 anycast:(Maybe Anycast)
|
||||
&& cb.store_long_bool(workchain, 8) // workchain_id:int8
|
||||
&& cb.store_bits_bool(addr); // address:bits256 = MsgAddressInt;
|
||||
} else {
|
||||
return cb.store_long_bool(0xd00, 12) // addr_var$11 anycast:(Maybe Anycast) addr_len:(## 9)
|
||||
&& cb.store_long_bool(workchain, 32) // workchain_id:int32
|
||||
&& cb.store_bits_bool(addr); // address:(bits addr_len) = MsgAddressInt;
|
||||
}
|
||||
}
|
||||
|
||||
Ref<vm::CellSlice> MsgAddressInt::pack_std_address(ton::WorkchainId workchain, const ton::StdSmcAddress& addr) const {
|
||||
vm::CellBuilder cb;
|
||||
if (store_std_address(cb, workchain, addr)) {
|
||||
return vm::load_cell_slice_ref(cb.finalize());
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const MsgAddressInt t_MsgAddressInt;
|
||||
|
||||
bool MsgAddress::validate_skip(vm::CellSlice& cs, bool weak) const {
|
||||
|
|
|
@ -285,6 +285,8 @@ struct MsgAddressInt final : TLB_Complex {
|
|||
bool rewrite = true) const;
|
||||
bool extract_std_address(vm::CellSlice& cs, ton::WorkchainId& workchain, ton::StdSmcAddress& addr,
|
||||
bool rewrite = true) const;
|
||||
bool store_std_address(vm::CellBuilder& cb, ton::WorkchainId workchain, const ton::StdSmcAddress& addr) const;
|
||||
Ref<vm::CellSlice> pack_std_address(ton::WorkchainId workchain, const ton::StdSmcAddress& addr) const;
|
||||
};
|
||||
|
||||
extern const MsgAddressInt t_MsgAddressInt;
|
||||
|
|
|
@ -727,3 +727,33 @@ after:^VmCont = VmCont;
|
|||
vmc_while_body$110011 cond:^VmCont body:^VmCont
|
||||
after:^VmCont = VmCont;
|
||||
vmc_pushint$1111 value:int32 next:^VmCont = VmCont;
|
||||
|
||||
//
|
||||
// DNS RECORDS
|
||||
//
|
||||
_ (HashmapE 16 ^DNSRecord) = DNS_RecordSet;
|
||||
|
||||
chunk_ref$_ {n:#} ref:^(TextChunks (n + 1)) = TextChunkRef (n + 1);
|
||||
chunk_ref_empty$_ = TextChunkRef 0;
|
||||
text_chunk$_ {n:#} len:(## 8) data:(bits (len * 8)) next:(TextChunkRef n) = TextChunks (n + 1);
|
||||
text_chunk_empty$_ = TextChunks 0;
|
||||
text$_ chunks:(## 8) rest:(TextChunks chunks) = Text;
|
||||
dns_text#1eda _:Text = DNSRecord;
|
||||
|
||||
dns_next_resolver#ba93 resolver:MsgAddressInt = DNSRecord; // usually in record #-1
|
||||
|
||||
dns_adnl_address#ad01 adnl_addr:bits256 flags:(## 8) { flags <= 1 }
|
||||
proto_list:flags . 0?ProtoList = DNSRecord; // often in record #2
|
||||
proto_list_nil$0 = ProtoList;
|
||||
proto_list_next$1 head:Protocol tail:ProtoList = ProtoList;
|
||||
proto_http#4854 = Protocol;
|
||||
|
||||
dns_smc_address#9fd3 smc_addr:MsgAddressInt flags:(## 8) { flags <= 1 }
|
||||
cap_list:flags . 0?SmcCapList = DNSRecord; // often in record #1
|
||||
cap_list_nil$0 = SmcCapList;
|
||||
cap_list_next$1 head:SmcCapability tail:SmcCapList = SmcCapList;
|
||||
cap_method_seqno#5371 = SmcCapability;
|
||||
cap_method_pubkey#71f4 = SmcCapability;
|
||||
cap_is_wallet#2177 = SmcCapability;
|
||||
cap_name#ff name:Text = SmcCapability;
|
||||
|
||||
|
|
|
@ -633,6 +633,88 @@ void init_words_custom(fift::Dictionary& d) {
|
|||
d.def_stack_word("isWorkchainDescr? ", interpret_is_workchain_descr);
|
||||
}
|
||||
|
||||
tlb::TypenameLookup tlb_dict;
|
||||
|
||||
// ( S -- T -1 or 0 ) Looks up TLB type by name
|
||||
void interpret_tlb_type_lookup(vm::Stack& stack) {
|
||||
auto ptr = tlb_dict.lookup(stack.pop_string());
|
||||
if (ptr) {
|
||||
stack.push_make_object<tlb::TlbTypeHolder>(ptr);
|
||||
}
|
||||
stack.push_bool(ptr);
|
||||
}
|
||||
|
||||
td::Ref<tlb::TlbTypeHolder> pop_tlb_type(vm::Stack& stack) {
|
||||
auto res = stack.pop_object<tlb::TlbTypeHolder>();
|
||||
if (res.is_null()) {
|
||||
throw vm::VmError{vm::Excno::type_chk, "not a TLB type"};
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// ( T -- S ) Gets TLB type name
|
||||
void interpret_tlb_type_name(vm::Stack& stack) {
|
||||
stack.push_string((*pop_tlb_type(stack))->get_type_name());
|
||||
}
|
||||
|
||||
// ( T -- ) Prints TLB type name
|
||||
void interpret_print_tlb_type(vm::Stack& stack) {
|
||||
std::cout << (*pop_tlb_type(stack))->get_type_name();
|
||||
}
|
||||
|
||||
// ( s T -- ) Dumps (part of) slice s as a value of TLB type T
|
||||
void interpret_tlb_dump_as(vm::Stack& stack) {
|
||||
auto tp = pop_tlb_type(stack);
|
||||
(*tp)->print(std::cout, stack.pop_cellslice());
|
||||
}
|
||||
|
||||
// ( s T -- s' S -1 or 0 )
|
||||
// Detects prefix of slice s that is a value of TLB type T, returns the remainder as s', and prints the value into String S.
|
||||
void interpret_tlb_dump_to_str(vm::Stack& stack) {
|
||||
auto tp = pop_tlb_type(stack);
|
||||
auto cs = stack.pop_cellslice();
|
||||
std::ostringstream os;
|
||||
bool ok = (*tp)->print_skip(os, cs.write());
|
||||
if (ok) {
|
||||
stack.push(std::move(cs));
|
||||
stack.push_string(os.str());
|
||||
}
|
||||
stack.push_bool(ok);
|
||||
}
|
||||
|
||||
// ( s T -- s' -1 or 0 ) Skips the only prefix of slice s that can be a value of TLB type T
|
||||
void interpret_tlb_skip(vm::Stack& stack) {
|
||||
auto tp = pop_tlb_type(stack);
|
||||
auto cs = stack.pop_cellslice();
|
||||
bool ok = (*tp)->skip(cs.write());
|
||||
if (ok) {
|
||||
stack.push(std::move(cs));
|
||||
}
|
||||
stack.push_bool(ok);
|
||||
}
|
||||
|
||||
// ( s T -- s' -1 or 0 ) Checks whether a prefix of slice s is a valid value of TLB type T, and skips it
|
||||
void interpret_tlb_validate_skip(vm::Stack& stack) {
|
||||
auto tp = pop_tlb_type(stack);
|
||||
auto cs = stack.pop_cellslice();
|
||||
bool ok = (*tp)->validate_skip(cs.write());
|
||||
if (ok) {
|
||||
stack.push(std::move(cs));
|
||||
}
|
||||
stack.push_bool(ok);
|
||||
}
|
||||
|
||||
void init_words_tlb(fift::Dictionary& d) {
|
||||
tlb_dict.register_types(block::gen::register_simple_types);
|
||||
d.def_stack_word("tlb-type-lookup ", interpret_tlb_type_lookup);
|
||||
d.def_stack_word("tlb-type-name ", interpret_tlb_type_name);
|
||||
d.def_stack_word("tlb. ", interpret_print_tlb_type);
|
||||
d.def_stack_word("tlb-dump-as ", interpret_tlb_dump_as);
|
||||
d.def_stack_word("(tlb-dump-str?) ", interpret_tlb_dump_to_str);
|
||||
d.def_stack_word("tlb-skip ", interpret_tlb_skip);
|
||||
d.def_stack_word("tlb-validate-skip ", interpret_tlb_validate_skip);
|
||||
}
|
||||
|
||||
void usage(const char* progname) {
|
||||
std::cerr
|
||||
<< "Creates initial state for a TON blockchain, using configuration defined by Fift-language source files\n";
|
||||
|
@ -739,6 +821,7 @@ int main(int argc, char* const argv[]) {
|
|||
fift::init_words_vm(config.dictionary);
|
||||
fift::init_words_ton(config.dictionary);
|
||||
init_words_custom(config.dictionary);
|
||||
init_words_tlb(config.dictionary);
|
||||
|
||||
if (script_mode) {
|
||||
fift::import_cmdline_args(config.dictionary, source_list.empty() ? "" : source_list[0], argc - optind,
|
||||
|
|
|
@ -191,7 +191,8 @@ void test2(vm::CellSlice& cs) {
|
|||
}
|
||||
|
||||
void usage() {
|
||||
std::cout << "usage: dump-block [-S][<boc-file>]\n\tor dump-block -h\n\tDumps specified blockchain block or state "
|
||||
std::cout << "usage: dump-block [-t<typename>][-S][<boc-file>]\n\tor dump-block -h\n\tDumps specified blockchain "
|
||||
"block or state "
|
||||
"from <boc-file>, or runs some tests\n\t-S\tDump a blockchain state instead of a block\n";
|
||||
std::exit(2);
|
||||
}
|
||||
|
@ -199,15 +200,20 @@ void usage() {
|
|||
int main(int argc, char* const argv[]) {
|
||||
int i;
|
||||
int new_verbosity_level = VERBOSITY_NAME(INFO);
|
||||
bool dump_state = false, dump_vmcont = false;
|
||||
const char* tname = nullptr;
|
||||
const tlb::TLB* type = &block::gen::t_Block;
|
||||
auto zerostate = std::make_unique<block::ZerostateInfo>();
|
||||
while ((i = getopt(argc, argv, "CShv:")) != -1) {
|
||||
while ((i = getopt(argc, argv, "CSt:hv:")) != -1) {
|
||||
switch (i) {
|
||||
case 'C':
|
||||
dump_vmcont = true;
|
||||
type = &block::gen::t_VmCont;
|
||||
break;
|
||||
case 'S':
|
||||
dump_state = true;
|
||||
type = &block::gen::t_ShardStateUnsplit;
|
||||
break;
|
||||
case 't':
|
||||
tname = optarg;
|
||||
type = nullptr;
|
||||
break;
|
||||
case 'v':
|
||||
new_verbosity_level = VERBOSITY_NAME(FATAL) + (verbosity = td::to_integer<int>(td::Slice(optarg)));
|
||||
|
@ -233,13 +239,18 @@ int main(int argc, char* const argv[]) {
|
|||
vm::CellSlice cs{vm::NoVm(), boc};
|
||||
cs.print_rec(std::cout);
|
||||
std::cout << std::endl;
|
||||
auto& type = !dump_vmcont
|
||||
? (dump_state ? (const tlb::TLB&)block::gen::t_ShardStateUnsplit : block::gen::t_Block)
|
||||
: block::gen::t_VmCont;
|
||||
type.print_ref(std::cout, boc);
|
||||
if (!type) {
|
||||
tlb::TypenameLookup dict(block::gen::register_simple_types);
|
||||
type = dict.lookup(tname);
|
||||
if (!type) {
|
||||
std::cerr << "unknown TL-B type " << tname << std::endl;
|
||||
std::exit(3);
|
||||
}
|
||||
}
|
||||
type->print_ref(std::cout, boc);
|
||||
std::cout << std::endl;
|
||||
bool ok = type.validate_ref(boc);
|
||||
std::cout << "(" << (ok ? "" : "in") << "valid " << type << ")" << std::endl;
|
||||
bool ok = type->validate_ref(boc);
|
||||
std::cout << "(" << (ok ? "" : "in") << "valid " << *type << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
if (!done) {
|
||||
|
|
|
@ -1064,19 +1064,23 @@ x{FFF0} @Defop SETCPX
|
|||
variable @proccnt
|
||||
variable @proclist
|
||||
variable @procdict
|
||||
variable @gvarcnt
|
||||
19 constant @procdictkeylen
|
||||
{ @proclist @ cons @proclist ! } : @proclistadd
|
||||
{ dup @procdictkeylen fits not abort"procedure index out of range"
|
||||
1 'nop does swap dup @proclistadd 0 (create)
|
||||
} : @declproc
|
||||
{ 1 'nop does swap 0 (create) } : @declglobvar
|
||||
{ @proccnt @ 1+ dup @proccnt ! @declproc } : @newproc
|
||||
{ 0 =: main @proclist null! @proccnt 0!
|
||||
{ @gvarcnt @ 1+ dup @gvarcnt ! @declglobvar } : @newglobvar
|
||||
{ 0 =: main @proclist null! @proccnt 0! @gvarcnt 0!
|
||||
{ bl word @newproc } : NEWPROC
|
||||
{ bl word dup (def?) ' drop ' @newproc cond } : DECLPROC
|
||||
{ bl word dup find
|
||||
{ nip execute <> abort"method redefined with different id" }
|
||||
{ swap @declproc }
|
||||
cond } : DECLMETHOD
|
||||
{ bl word @newglobvar } : DECLGLOBVAR
|
||||
"main" @proclistadd
|
||||
dictnew @procdict !
|
||||
} : PROGRAM{
|
||||
|
|
|
@ -116,3 +116,15 @@ variable base
|
|||
{ 9 hold } : +tab
|
||||
{ "" swap { 0 word 2dup $cmp } { rot swap $+ +cr swap } while 2drop } : scan-until-word
|
||||
{ 0 word -trailing scan-until-word 1 'nop } ::_ $<<
|
||||
{ 0x40 runvmx } : runvmcode
|
||||
{ 0x48 runvmx } : gasrunvmcode
|
||||
{ 0x43 runvmx } : runvmdict
|
||||
{ 0x4b runvmx } : gasrunvmdict
|
||||
{ 0x45 runvmx } : runvm
|
||||
{ 0x4d runvmx } : gasrunvm
|
||||
{ 0x55 runvmx } : runvmctx
|
||||
{ 0x5d runvmx } : gasrunvmctx
|
||||
{ 0x75 runvmx } : runvmctxact
|
||||
{ 0x7d runvmx } : gasrunvmctxact
|
||||
{ 0x35 runvmx } : runvmctxactq
|
||||
{ 0x3d runvmx } : gasrunvmctxactq
|
||||
|
|
|
@ -1512,11 +1512,12 @@ void interpret_store_dict(vm::Stack& stack) {
|
|||
}
|
||||
|
||||
// val key dict keylen -- dict' ?
|
||||
void interpret_dict_add_u(vm::Stack& stack, vm::Dictionary::SetMode mode, bool add_builder, bool sgnd) {
|
||||
void interpret_dict_add(vm::Stack& stack, vm::Dictionary::SetMode mode, bool add_builder, int 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);
|
||||
vm::BitSlice key =
|
||||
(sgnd >= 0) ? dict.integer_key(stack.pop_int(), n, sgnd, buffer) : stack.pop_cellslice()->prefetch_bits(n);
|
||||
if (!key.is_valid()) {
|
||||
throw IntError{"not enough bits for a dictionary key"};
|
||||
}
|
||||
|
@ -1530,11 +1531,12 @@ void interpret_dict_add_u(vm::Stack& stack, vm::Dictionary::SetMode mode, bool a
|
|||
stack.push_bool(res);
|
||||
}
|
||||
|
||||
void interpret_dict_get_u(vm::Stack& stack, bool sgnd) {
|
||||
void interpret_dict_get(vm::Stack& stack, int 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);
|
||||
vm::BitSlice key =
|
||||
(sgnd >= 0) ? dict.integer_key(stack.pop_int(), n, sgnd, buffer) : stack.pop_cellslice()->prefetch_bits(n);
|
||||
if (!key.is_valid()) {
|
||||
throw IntError{"not enough bits for a dictionary key"};
|
||||
}
|
||||
|
@ -2169,6 +2171,7 @@ class StringLogger : public td::LogInterface {
|
|||
}
|
||||
std::string res;
|
||||
};
|
||||
|
||||
class OstreamLogger : public td::LogInterface {
|
||||
public:
|
||||
explicit OstreamLogger(std::ostream* stream) : stream_(stream) {
|
||||
|
@ -2191,59 +2194,42 @@ std::vector<Ref<vm::Cell>> get_vm_libraries() {
|
|||
}
|
||||
}
|
||||
|
||||
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());
|
||||
// mode: -1 = pop from stack
|
||||
// +1 = same_c3 (set c3 to code)
|
||||
// +2 = push_0 (push an implicit 0 before running the code)
|
||||
// +4 = load c4 (persistent data) from stack and return its final value
|
||||
// +8 = load gas limit from stack and return consumed gas
|
||||
// +16 = load c7 (smart-contract context)
|
||||
// +32 = return c5 (actions)
|
||||
// +64 = log vm ops to stderr
|
||||
void interpret_run_vm(IntCtx& ctx, int mode) {
|
||||
if (mode < 0) {
|
||||
mode = ctx.stack.pop_smallint_range(0xff);
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
bool with_data = mode & 4;
|
||||
Ref<vm::Tuple> c7;
|
||||
Ref<vm::Cell> data, actions;
|
||||
long long gas_limit = (mode & 8) ? ctx.stack.pop_long_range(vm::GasLimits::infty) : vm::GasLimits::infty;
|
||||
if (mode & 16) {
|
||||
c7 = ctx.stack.pop_tuple();
|
||||
}
|
||||
}
|
||||
|
||||
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, 1, &data, log, nullptr, &gas, get_vm_libraries());
|
||||
ctx.stack.push_smallint(res);
|
||||
ctx.stack.push_cell(std::move(data));
|
||||
if (with_gas) {
|
||||
ctx.stack.push_smallint(gas.gas_consumed());
|
||||
if (with_data) {
|
||||
data = ctx.stack.pop_cell();
|
||||
}
|
||||
}
|
||||
|
||||
void interpret_run_vm_c7(IntCtx& ctx, bool with_gas) {
|
||||
long long gas_limit = with_gas ? ctx.stack.pop_long_range(vm::GasLimits::infty) : vm::GasLimits::infty;
|
||||
auto c7 = ctx.stack.pop_tuple();
|
||||
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);
|
||||
auto log = create_vm_log((mode & 64) && ctx.error_stream ? &ostream_logger : nullptr);
|
||||
vm::GasLimits gas{gas_limit};
|
||||
int res = vm::run_vm_code(cs, ctx.stack, 1, &data, log, nullptr, &gas, get_vm_libraries(), std::move(c7));
|
||||
int res =
|
||||
vm::run_vm_code(cs, ctx.stack, mode & 3, &data, log, nullptr, &gas, get_vm_libraries(), std::move(c7), &actions);
|
||||
ctx.stack.push_smallint(res);
|
||||
ctx.stack.push_cell(std::move(data));
|
||||
if (with_gas) {
|
||||
if (with_data) {
|
||||
ctx.stack.push_cell(std::move(data));
|
||||
}
|
||||
if (mode & 32) {
|
||||
ctx.stack.push_cell(std::move(actions));
|
||||
}
|
||||
if (mode & 8) {
|
||||
ctx.stack.push_smallint(gas.gas_consumed());
|
||||
}
|
||||
}
|
||||
|
@ -2759,16 +2745,21 @@ void init_words_common(Dictionary& d) {
|
|||
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("sdict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, false, -1));
|
||||
d.def_stack_word("sdict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, false, -1));
|
||||
d.def_stack_word("b>sdict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, true, -1));
|
||||
d.def_stack_word("b>sdict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, true, -1));
|
||||
d.def_stack_word("sdict@ ", std::bind(interpret_dict_get, _1, -1));
|
||||
d.def_stack_word("udict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, false, 0));
|
||||
d.def_stack_word("udict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, false, 0));
|
||||
d.def_stack_word("b>udict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, true, 0));
|
||||
d.def_stack_word("b>udict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, true, 0));
|
||||
d.def_stack_word("udict@ ", std::bind(interpret_dict_get, _1, 0));
|
||||
d.def_stack_word("idict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, false, 1));
|
||||
d.def_stack_word("idict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, false, 1));
|
||||
d.def_stack_word("b>idict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, true, 1));
|
||||
d.def_stack_word("b>idict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, true, 1));
|
||||
d.def_stack_word("idict@ ", std::bind(interpret_dict_get, _1, 1));
|
||||
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);
|
||||
|
@ -2867,14 +2858,9 @@ void init_words_vm(Dictionary& d) {
|
|||
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("runvmctx ", std::bind(interpret_run_vm_c7, _1, false));
|
||||
d.def_ctx_word("gasrunvmctx ", std::bind(interpret_run_vm_c7, _1, true));
|
||||
// d.def_ctx_word("runvmcode ", std::bind(interpret_run_vm, _1, 0x40));
|
||||
// d.def_ctx_word("runvm ", std::bind(interpret_run_vm, _1, 0x45));
|
||||
d.def_ctx_word("runvmx ", std::bind(interpret_run_vm, _1, -1));
|
||||
d.def_ctx_word("dbrunvm ", interpret_db_run_vm);
|
||||
d.def_ctx_word("dbrunvm-parallel ", interpret_db_run_vm_parallel);
|
||||
d.def_stack_word("vmcont, ", interpret_store_vm_cont);
|
||||
|
|
|
@ -343,6 +343,12 @@ void Op::show(std::ostream& os, const std::vector<TmpVar>& vars, std::string pfx
|
|||
show_var_list(os, left, vars);
|
||||
os << " := " << (fun_ref ? fun_ref->name() : "(null)") << std::endl;
|
||||
break;
|
||||
case _SetGlob:
|
||||
os << pfx << dis << "SETGLOB ";
|
||||
os << (fun_ref ? fun_ref->name() : "(null)") << " := ";
|
||||
show_var_list(os, right, vars);
|
||||
os << std::endl;
|
||||
break;
|
||||
case _Repeat:
|
||||
os << pfx << dis << "REPEAT ";
|
||||
show_var_list(os, left, vars);
|
||||
|
|
|
@ -365,6 +365,13 @@ bool Op::compute_used_vars(const CodeBlob& code, bool edit) {
|
|||
}
|
||||
return std_compute_used_vars();
|
||||
}
|
||||
case _SetGlob: {
|
||||
// GLOB = right
|
||||
if (right.empty() && edit) {
|
||||
disable();
|
||||
}
|
||||
return std_compute_used_vars(right.empty());
|
||||
}
|
||||
case _Let: {
|
||||
// left = right
|
||||
std::size_t cnt = next_var_info.count_used(left);
|
||||
|
@ -531,6 +538,7 @@ bool prune_unreachable(std::unique_ptr<Op>& ops) {
|
|||
switch (op.cl) {
|
||||
case Op::_IntConst:
|
||||
case Op::_GlobVar:
|
||||
case Op::_SetGlob:
|
||||
case Op::_Call:
|
||||
case Op::_CallInd:
|
||||
case Op::_Import:
|
||||
|
@ -694,7 +702,6 @@ VarDescrList Op::fwd_analyze(VarDescrList values) {
|
|||
values.add_newval(left[0]).set_const(int_const);
|
||||
break;
|
||||
}
|
||||
case _GlobVar:
|
||||
case _Call: {
|
||||
prepare_args(values);
|
||||
auto func = dynamic_cast<const SymValAsmFunc*>(fun_ref->value);
|
||||
|
@ -717,12 +724,15 @@ VarDescrList Op::fwd_analyze(VarDescrList values) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case _GlobVar:
|
||||
case _CallInd: {
|
||||
for (var_idx_t i : left) {
|
||||
values.add_newval(i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case _SetGlob:
|
||||
break;
|
||||
case _Let: {
|
||||
std::vector<VarDescr> old_val;
|
||||
assert(left.size() == right.size());
|
||||
|
@ -832,6 +842,7 @@ bool Op::mark_noreturn() {
|
|||
case _Import:
|
||||
case _IntConst:
|
||||
case _Let:
|
||||
case _SetGlob:
|
||||
case _GlobVar:
|
||||
case _CallInd:
|
||||
case _Call:
|
||||
|
|
|
@ -27,8 +27,8 @@ using namespace std::literals::string_literals;
|
|||
*
|
||||
*/
|
||||
|
||||
int glob_func_cnt, undef_func_cnt;
|
||||
std::vector<SymDef*> glob_func;
|
||||
int glob_func_cnt, undef_func_cnt, glob_var_cnt;
|
||||
std::vector<SymDef*> glob_func, glob_vars;
|
||||
|
||||
SymDef* predefine_builtin_func(std::string name, TypeExpr* func_type) {
|
||||
sym_idx_t name_idx = sym::symbols.lookup(name, 1);
|
||||
|
@ -44,30 +44,49 @@ SymDef* predefine_builtin_func(std::string name, TypeExpr* func_type) {
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
void define_builtin_func(std::string name, TypeExpr* func_type, const T& func, bool impure = false) {
|
||||
SymDef* define_builtin_func(std::string name, TypeExpr* func_type, const T& func, bool impure = false) {
|
||||
SymDef* def = predefine_builtin_func(name, func_type);
|
||||
def->value = new SymValAsmFunc{func_type, func, impure};
|
||||
return def;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void define_builtin_func(std::string name, TypeExpr* func_type, const T& func, std::initializer_list<int> arg_order,
|
||||
std::initializer_list<int> ret_order = {}, bool impure = false) {
|
||||
SymDef* define_builtin_func(std::string name, TypeExpr* func_type, const T& func, std::initializer_list<int> arg_order,
|
||||
std::initializer_list<int> ret_order = {}, bool impure = false) {
|
||||
SymDef* def = predefine_builtin_func(name, func_type);
|
||||
def->value = new SymValAsmFunc{func_type, func, arg_order, ret_order, impure};
|
||||
return def;
|
||||
}
|
||||
|
||||
void define_builtin_func(std::string name, TypeExpr* func_type, const AsmOp& macro,
|
||||
std::initializer_list<int> arg_order, std::initializer_list<int> ret_order = {},
|
||||
bool impure = false) {
|
||||
SymDef* define_builtin_func(std::string name, TypeExpr* func_type, const AsmOp& macro,
|
||||
std::initializer_list<int> arg_order, std::initializer_list<int> ret_order = {},
|
||||
bool impure = false) {
|
||||
SymDef* def = predefine_builtin_func(name, func_type);
|
||||
def->value = new SymValAsmFunc{func_type, make_simple_compile(macro), arg_order, ret_order, impure};
|
||||
return def;
|
||||
}
|
||||
|
||||
bool SymValAsmFunc::compile(AsmOpList& dest, std::vector<VarDescr>& in, std::vector<VarDescr>& out) const {
|
||||
SymDef* force_autoapply(SymDef* def) {
|
||||
if (def) {
|
||||
auto val = dynamic_cast<SymVal*>(def->value);
|
||||
if (val) {
|
||||
val->auto_apply = true;
|
||||
}
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
SymDef* define_builtin_const(std::string name, TypeExpr* const_type, Args&&... args) {
|
||||
return force_autoapply(
|
||||
define_builtin_func(name, TypeExpr::new_map(TypeExpr::new_unit(), const_type), std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
bool SymValAsmFunc::compile(AsmOpList& dest, std::vector<VarDescr>& out, std::vector<VarDescr>& in) const {
|
||||
if (simple_compile) {
|
||||
return dest.append(simple_compile(in, out));
|
||||
return dest.append(simple_compile(out, in));
|
||||
} else if (ext_compile) {
|
||||
return ext_compile(dest, in, out);
|
||||
return ext_compile(dest, out, in);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
@ -317,6 +336,12 @@ AsmOp exec_arg_op(std::string op, td::RefInt256 arg, int args, int retv) {
|
|||
return AsmOp::Custom(os.str(), args, retv);
|
||||
}
|
||||
|
||||
AsmOp exec_arg2_op(std::string op, long long imm1, long long imm2, int args, int retv) {
|
||||
std::ostringstream os;
|
||||
os << imm1 << ' ' << imm2 << ' ' << op;
|
||||
return AsmOp::Custom(os.str(), args, retv);
|
||||
}
|
||||
|
||||
AsmOp push_const(td::RefInt256 x) {
|
||||
return AsmOp::IntConst(std::move(x));
|
||||
}
|
||||
|
@ -918,11 +943,11 @@ void define_builtins() {
|
|||
define_builtin_func("_<=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 6));
|
||||
define_builtin_func("_>=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 3));
|
||||
define_builtin_func("_<=>_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 7));
|
||||
define_builtin_func("true", Int, /* AsmOp::Const("TRUE") */ std::bind(compile_bool_const, _1, _2, true));
|
||||
define_builtin_func("false", Int, /* AsmOp::Const("FALSE") */ std::bind(compile_bool_const, _1, _2, false));
|
||||
define_builtin_const("true", Int, /* AsmOp::Const("TRUE") */ std::bind(compile_bool_const, _1, _2, true));
|
||||
define_builtin_const("false", Int, /* AsmOp::Const("FALSE") */ std::bind(compile_bool_const, _1, _2, false));
|
||||
// define_builtin_func("null", Null, AsmOp::Const("PUSHNULL"));
|
||||
define_builtin_func("nil", Tuple, AsmOp::Const("PUSHNULL"));
|
||||
define_builtin_func("Nil", Tuple, AsmOp::Const("NIL"));
|
||||
define_builtin_const("nil", Tuple, AsmOp::Const("PUSHNULL"));
|
||||
define_builtin_const("Nil", Tuple, AsmOp::Const("NIL"));
|
||||
define_builtin_func("null?", TypeExpr::new_forall({X}, TypeExpr::new_map(X, Int)), compile_is_null);
|
||||
define_builtin_func("throw", impure_un_op, compile_throw, true);
|
||||
define_builtin_func("throw_if", impure_bin_op, std::bind(compile_cond_throw, _1, _2, true), true);
|
||||
|
|
|
@ -84,7 +84,9 @@ void Stack::forget_const() {
|
|||
|
||||
void Stack::issue_pop(int i) {
|
||||
validate(i);
|
||||
o << AsmOp::Pop(i);
|
||||
if (output_enabled()) {
|
||||
o << AsmOp::Pop(i);
|
||||
}
|
||||
at(i) = get(0);
|
||||
s.pop_back();
|
||||
modified();
|
||||
|
@ -92,7 +94,9 @@ void Stack::issue_pop(int i) {
|
|||
|
||||
void Stack::issue_push(int i) {
|
||||
validate(i);
|
||||
o << AsmOp::Push(i);
|
||||
if (output_enabled()) {
|
||||
o << AsmOp::Push(i);
|
||||
}
|
||||
s.push_back(get(i));
|
||||
modified();
|
||||
}
|
||||
|
@ -101,7 +105,9 @@ void Stack::issue_xchg(int i, int j) {
|
|||
validate(i);
|
||||
validate(j);
|
||||
if (i != j && get(i) != get(j)) {
|
||||
o << AsmOp::Xchg(i, j);
|
||||
if (output_enabled()) {
|
||||
o << AsmOp::Xchg(i, j);
|
||||
}
|
||||
std::swap(at(i), at(j));
|
||||
modified();
|
||||
}
|
||||
|
@ -183,6 +189,10 @@ void Stack::enforce_state(const StackLayout& req_stack) {
|
|||
if (i < depth() && s[i].first == x) {
|
||||
continue;
|
||||
}
|
||||
while (depth() > 0 && std::find(req_stack.cbegin(), req_stack.cend(), get(0).first) == req_stack.cend()) {
|
||||
// current TOS entry is unused in req_stack, drop it
|
||||
issue_pop(0);
|
||||
}
|
||||
int j = find(x);
|
||||
if (j >= depth() - i) {
|
||||
issue_push(j);
|
||||
|
@ -292,27 +302,61 @@ bool Op::generate_code_step(Stack& stack) {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
case _GlobVar: {
|
||||
assert(left.size() == 1);
|
||||
auto p = next_var_info[left[0]];
|
||||
if (!p || p->is_unused() || disabled()) {
|
||||
case _GlobVar:
|
||||
if (dynamic_cast<const SymValGlobVar*>(fun_ref->value)) {
|
||||
bool used = false;
|
||||
for (auto i : left) {
|
||||
auto p = next_var_info[i];
|
||||
if (p && !p->is_unused()) {
|
||||
used = true;
|
||||
}
|
||||
}
|
||||
if (!used || disabled()) {
|
||||
return true;
|
||||
}
|
||||
std::string name = sym::symbols.get_name(fun_ref->sym_idx);
|
||||
stack.o << AsmOp::Custom(name + " GETGLOB", 0, 1);
|
||||
if (left.size() != 1) {
|
||||
assert(left.size() <= 15);
|
||||
stack.o << exec_arg_op("UNTUPLE", (int)left.size(), 1, (int)left.size());
|
||||
}
|
||||
for (auto i : left) {
|
||||
stack.push_new_var(i);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
assert(left.size() == 1);
|
||||
auto p = next_var_info[left[0]];
|
||||
if (!p || p->is_unused() || disabled()) {
|
||||
return true;
|
||||
}
|
||||
stack.o << "CONT:<{";
|
||||
stack.o.indent();
|
||||
auto func = dynamic_cast<SymValAsmFunc*>(fun_ref->value);
|
||||
if (func) {
|
||||
// TODO: create and compile a true lambda instead of this (so that arg_order and ret_order would work correctly)
|
||||
std::vector<VarDescr> args0, res;
|
||||
TypeExpr::remove_indirect(func->sym_type);
|
||||
assert(func->get_type()->is_map());
|
||||
auto wr = func->get_type()->args.at(0)->get_width();
|
||||
auto wl = func->get_type()->args.at(1)->get_width();
|
||||
assert(wl >= 0 && wr >= 0);
|
||||
for (int i = 0; i < wl; i++) {
|
||||
res.emplace_back(0);
|
||||
}
|
||||
for (int i = 0; i < wr; i++) {
|
||||
args0.emplace_back(0);
|
||||
}
|
||||
func->compile(stack.o, res, args0); // compile res := f (args0)
|
||||
} else {
|
||||
std::string name = sym::symbols.get_name(fun_ref->sym_idx);
|
||||
stack.o << AsmOp::Custom(name + " CALLDICT", (int)right.size(), (int)left.size());
|
||||
}
|
||||
stack.o.undent();
|
||||
stack.o << "}>";
|
||||
stack.push_new_var(left.at(0));
|
||||
return true;
|
||||
}
|
||||
auto func = dynamic_cast<const SymValAsmFunc*>(fun_ref->value);
|
||||
if (func) {
|
||||
std::vector<VarDescr> res;
|
||||
res.reserve(left.size());
|
||||
for (var_idx_t i : left) {
|
||||
res.emplace_back(i);
|
||||
}
|
||||
func->compile(stack.o, res, args); // compile res := f (args)
|
||||
} else {
|
||||
std::string name = sym::symbols.get_name(fun_ref->sym_idx);
|
||||
stack.o << AsmOp::Custom(name + " CALLDICT", (int)right.size(), (int)left.size());
|
||||
}
|
||||
stack.push_new_var(left[0]);
|
||||
return true;
|
||||
}
|
||||
case _Let: {
|
||||
assert(left.size() == right.size());
|
||||
int i = 0;
|
||||
|
@ -395,7 +439,9 @@ bool Op::generate_code_step(Stack& stack) {
|
|||
assert(stack.s[k + i].first == right1[i]);
|
||||
}
|
||||
if (cl == _CallInd) {
|
||||
stack.o << exec_arg_op("CALLARGS", (int)right.size() - 1, (int)right.size(), (int)left.size());
|
||||
// TODO: replace with exec_arg2_op()
|
||||
stack.o << exec_arg2_op("CALLXARGS", (int)right.size() - 1, (int)left.size(), (int)right.size(),
|
||||
(int)left.size());
|
||||
} else {
|
||||
auto func = dynamic_cast<const SymValAsmFunc*>(fun_ref->value);
|
||||
if (func) {
|
||||
|
@ -420,6 +466,32 @@ bool Op::generate_code_step(Stack& stack) {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
case _SetGlob: {
|
||||
assert(fun_ref && dynamic_cast<const SymValGlobVar*>(fun_ref->value));
|
||||
std::vector<bool> last;
|
||||
for (var_idx_t x : right) {
|
||||
last.push_back(var_info[x] && var_info[x]->is_last());
|
||||
}
|
||||
stack.rearrange_top(right, std::move(last));
|
||||
stack.opt_show();
|
||||
int k = (int)stack.depth() - (int)right.size();
|
||||
assert(k >= 0);
|
||||
for (int i = 0; i < (int)right.size(); i++) {
|
||||
if (stack.s[k + i].first != right[i]) {
|
||||
std::cerr << stack.o;
|
||||
}
|
||||
assert(stack.s[k + i].first == right[i]);
|
||||
}
|
||||
if (right.size() > 1) {
|
||||
stack.o << exec_arg_op("TUPLE", (int)right.size(), (int)right.size(), 1);
|
||||
}
|
||||
if (!right.empty()) {
|
||||
std::string name = sym::symbols.get_name(fun_ref->sym_idx);
|
||||
stack.o << AsmOp::Custom(name + " SETGLOB", 1, 0);
|
||||
}
|
||||
stack.s.resize(k);
|
||||
return true;
|
||||
}
|
||||
case _If: {
|
||||
if (block0->is_empty() && block1->is_empty()) {
|
||||
return true;
|
||||
|
@ -448,7 +520,9 @@ bool Op::generate_code_step(Stack& stack) {
|
|||
}
|
||||
stack.o << "IF:<{";
|
||||
stack.o.indent();
|
||||
Stack stack_copy{stack};
|
||||
Stack stack_copy{stack}, stack_target{stack};
|
||||
stack_target.disable_output();
|
||||
stack_target.drop_vars_except(next->var_info);
|
||||
block0->generate_code_all(stack_copy);
|
||||
stack_copy.drop_vars_except(var_info);
|
||||
stack_copy.opt_show();
|
||||
|
@ -457,7 +531,8 @@ bool Op::generate_code_step(Stack& stack) {
|
|||
stack.o << "}>";
|
||||
return true;
|
||||
}
|
||||
stack_copy.drop_vars_except(next->var_info);
|
||||
// stack_copy.drop_vars_except(next->var_info);
|
||||
stack_copy.enforce_state(stack_target.vars());
|
||||
stack_copy.opt_show();
|
||||
if (stack_copy.vars() == stack.vars()) {
|
||||
stack.o.undent();
|
||||
|
@ -487,7 +562,9 @@ bool Op::generate_code_step(Stack& stack) {
|
|||
}
|
||||
stack.o << "IFNOT:<{";
|
||||
stack.o.indent();
|
||||
Stack stack_copy{stack};
|
||||
Stack stack_copy{stack}, stack_target{stack};
|
||||
stack_target.disable_output();
|
||||
stack_target.drop_vars_except(next->var_info);
|
||||
block1->generate_code_all(stack_copy);
|
||||
stack_copy.drop_vars_except(var_info);
|
||||
stack_copy.opt_show();
|
||||
|
@ -497,7 +574,8 @@ bool Op::generate_code_step(Stack& stack) {
|
|||
stack.merge_const(stack_copy);
|
||||
return true;
|
||||
}
|
||||
stack_copy.drop_vars_except(next->var_info);
|
||||
// stack_copy.drop_vars_except(next->var_info);
|
||||
stack_copy.enforce_state(stack_target.vars());
|
||||
stack_copy.opt_show();
|
||||
if (stack_copy.vars() == stack.vars()) {
|
||||
stack.o.undent();
|
||||
|
|
|
@ -130,6 +130,11 @@ int generate_output() {
|
|||
*outs << func_val->method_id << " DECLMETHOD " << name << "\n";
|
||||
}
|
||||
}
|
||||
for (SymDef* gvar_sym : glob_vars) {
|
||||
assert(dynamic_cast<SymValGlobVar*>(gvar_sym->value));
|
||||
std::string name = sym::symbols.get_name(gvar_sym->sym_idx);
|
||||
*outs << std::string(indent * 2, ' ') << "DECLGLOBVAR " << name << "\n";
|
||||
}
|
||||
int errors = 0;
|
||||
for (SymDef* func_sym : glob_func) {
|
||||
try {
|
||||
|
|
|
@ -97,6 +97,7 @@ enum Keyword {
|
|||
_Forall,
|
||||
_Asm,
|
||||
_Impure,
|
||||
_Global,
|
||||
_Extern,
|
||||
_Inline,
|
||||
_InlineRef,
|
||||
|
@ -181,6 +182,9 @@ struct TypeExpr {
|
|||
bool is_var() const {
|
||||
return constr == te_Var;
|
||||
}
|
||||
bool is_map() const {
|
||||
return constr == te_Map;
|
||||
}
|
||||
bool has_fixed_width() const {
|
||||
return minw == maxw;
|
||||
}
|
||||
|
@ -498,6 +502,7 @@ struct Op {
|
|||
_Let,
|
||||
_IntConst,
|
||||
_GlobVar,
|
||||
_SetGlob,
|
||||
_Import,
|
||||
_Return,
|
||||
_If,
|
||||
|
@ -694,6 +699,7 @@ struct SymVal : sym::SymValBase {
|
|||
TypeExpr* sym_type;
|
||||
td::RefInt256 method_id;
|
||||
bool impure;
|
||||
bool auto_apply{false};
|
||||
short flags; // +1 = inline, +2 = inline_ref
|
||||
SymVal(int _type, int _idx, TypeExpr* _stype = nullptr, bool _impure = false)
|
||||
: sym::SymValBase(_type, _idx), sym_type(_stype), impure(_impure), flags(0) {
|
||||
|
@ -745,8 +751,20 @@ struct SymValType : sym::SymValBase {
|
|||
}
|
||||
};
|
||||
|
||||
extern int glob_func_cnt, undef_func_cnt;
|
||||
extern std::vector<SymDef*> glob_func;
|
||||
struct SymValGlobVar : sym::SymValBase {
|
||||
TypeExpr* sym_type;
|
||||
int out_idx{0};
|
||||
SymValGlobVar(int val, TypeExpr* gvtype, int oidx = 0)
|
||||
: sym::SymValBase(_GlobVar, val), sym_type(gvtype), out_idx(oidx) {
|
||||
}
|
||||
~SymValGlobVar() override = default;
|
||||
TypeExpr* get_type() const {
|
||||
return sym_type;
|
||||
}
|
||||
};
|
||||
|
||||
extern int glob_func_cnt, undef_func_cnt, glob_var_cnt;
|
||||
extern std::vector<SymDef*> glob_func, glob_vars;
|
||||
|
||||
/*
|
||||
*
|
||||
|
@ -775,6 +793,7 @@ struct Expr {
|
|||
_Const,
|
||||
_Var,
|
||||
_Glob,
|
||||
_GlobVar,
|
||||
_Letop,
|
||||
_LetFirst,
|
||||
_Hole,
|
||||
|
@ -1155,6 +1174,7 @@ struct StackTransform {
|
|||
bool apply_push(int i);
|
||||
bool apply_pop(int i = 0);
|
||||
bool apply_push_newconst();
|
||||
bool apply_blkpop(int k);
|
||||
bool apply(const StackTransform& other); // this = this * other
|
||||
bool preapply(const StackTransform& other); // this = other * this
|
||||
// c := a * b
|
||||
|
@ -1246,6 +1266,11 @@ struct StackTransform {
|
|||
bool is_nip_seq(int i, int j = 0) const;
|
||||
bool is_nip_seq(int* i) const;
|
||||
bool is_nip_seq(int* i, int* j) const;
|
||||
bool is_pop_blkdrop(int i, int k) const;
|
||||
bool is_pop_blkdrop(int* i, int* k) const;
|
||||
bool is_2pop_blkdrop(int i, int j, int k) const;
|
||||
bool is_2pop_blkdrop(int* i, int* j, int* k) const;
|
||||
bool is_const_rot() const;
|
||||
|
||||
void show(std::ostream& os, int mode = 0) const;
|
||||
|
||||
|
@ -1306,14 +1331,20 @@ struct Optimizer {
|
|||
bool rewrite_const_push_swap();
|
||||
bool is_const_push_xchgs();
|
||||
bool rewrite_const_push_xchgs();
|
||||
bool is_const_rot() const;
|
||||
bool rewrite_const_rot();
|
||||
bool simple_rewrite(int p, AsmOp&& new_op);
|
||||
bool simple_rewrite(int p, AsmOp&& new_op1, AsmOp&& new_op2);
|
||||
bool simple_rewrite(int p, AsmOp&& new_op1, AsmOp&& new_op2, AsmOp&& new_op3);
|
||||
bool simple_rewrite(AsmOp&& new_op) {
|
||||
return simple_rewrite(p_, std::move(new_op));
|
||||
}
|
||||
bool simple_rewrite(AsmOp&& new_op1, AsmOp&& new_op2) {
|
||||
return simple_rewrite(p_, std::move(new_op1), std::move(new_op2));
|
||||
}
|
||||
bool simple_rewrite(AsmOp&& new_op1, AsmOp&& new_op2, AsmOp&& new_op3) {
|
||||
return simple_rewrite(p_, std::move(new_op1), std::move(new_op2), std::move(new_op3));
|
||||
}
|
||||
bool simple_rewrite_nop();
|
||||
bool is_pred(const std::function<bool(const StackTransform&)>& pred, int min_p = 2);
|
||||
bool is_same_as(const StackTransform& trans, int min_p = 2);
|
||||
|
@ -1345,6 +1376,8 @@ struct Optimizer {
|
|||
bool is_blkdrop(int* i);
|
||||
bool is_reverse(int* i, int* j);
|
||||
bool is_nip_seq(int* i, int* j);
|
||||
bool is_pop_blkdrop(int* i, int* k);
|
||||
bool is_2pop_blkdrop(int* i, int* j, int* k);
|
||||
AsmOpConsList extract_code();
|
||||
};
|
||||
|
||||
|
@ -1355,7 +1388,7 @@ void optimize_code(AsmOpList& ops);
|
|||
struct Stack {
|
||||
StackLayoutExt s;
|
||||
AsmOpList& o;
|
||||
enum { _StkCmt = 1, _CptStkCmt = 2, _DisableOpt = 4, _Shown = 256, _Garbage = -0x10000 };
|
||||
enum { _StkCmt = 1, _CptStkCmt = 2, _DisableOpt = 4, _DisableOut = 128, _Shown = 256, _Garbage = -0x10000 };
|
||||
int mode;
|
||||
Stack(AsmOpList& _o, int _mode = 0) : o(_o), mode(_mode) {
|
||||
}
|
||||
|
@ -1381,6 +1414,15 @@ struct Stack {
|
|||
var_const_idx_t get(int i) const {
|
||||
return at(i);
|
||||
}
|
||||
bool output_disabled() const {
|
||||
return mode & _DisableOut;
|
||||
}
|
||||
bool output_enabled() const {
|
||||
return !output_disabled();
|
||||
}
|
||||
void disable_output() {
|
||||
mode |= _DisableOut;
|
||||
}
|
||||
StackLayout vars() const;
|
||||
int find(var_idx_t var, int from = 0) const;
|
||||
int find(var_idx_t var, int from, int to) const;
|
||||
|
@ -1470,7 +1512,7 @@ struct SymValAsmFunc : SymValFunc {
|
|||
std::initializer_list<int> ret_order = {}, bool impure = false)
|
||||
: SymValFunc(-1, ft, arg_order, ret_order, impure), ext_compile(std::move(_compile)) {
|
||||
}
|
||||
bool compile(AsmOpList& dest, std::vector<VarDescr>& in, std::vector<VarDescr>& out) const;
|
||||
bool compile(AsmOpList& dest, std::vector<VarDescr>& out, std::vector<VarDescr>& in) const;
|
||||
};
|
||||
|
||||
// defined in builtins.cpp
|
||||
|
@ -1478,6 +1520,7 @@ AsmOp exec_arg_op(std::string op, long long arg);
|
|||
AsmOp exec_arg_op(std::string op, long long arg, int args, int retv = 1);
|
||||
AsmOp exec_arg_op(std::string op, td::RefInt256 arg);
|
||||
AsmOp exec_arg_op(std::string op, td::RefInt256 arg, int args, int retv = 1);
|
||||
AsmOp exec_arg2_op(std::string op, long long imm1, long long imm2, int args, int retv = 1);
|
||||
AsmOp push_const(td::RefInt256 x);
|
||||
|
||||
void define_builtins();
|
||||
|
|
|
@ -282,15 +282,22 @@ std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code) const {
|
|||
code.emplace_back(here, Op::_IntConst, rvect, intval);
|
||||
return rvect;
|
||||
}
|
||||
case _Glob: {
|
||||
case _Glob:
|
||||
case _GlobVar: {
|
||||
auto rvect = new_tmp_vect(code);
|
||||
code.emplace_back(here, Op::_GlobVar, rvect, std::vector<var_idx_t>{}, sym);
|
||||
return rvect;
|
||||
}
|
||||
case _Letop: {
|
||||
auto right = args[1]->pre_compile(code);
|
||||
auto left = args[0]->pre_compile(code);
|
||||
code.emplace_back(here, Op::_Let, std::move(left), right);
|
||||
if (args[0]->cls == Expr::_GlobVar) {
|
||||
assert(args[0]->sym);
|
||||
auto& op = code.emplace_back(here, Op::_SetGlob, std::vector<var_idx_t>{}, right, args[0]->sym);
|
||||
op.flags |= Op::_Impure;
|
||||
} else {
|
||||
auto left = args[0]->pre_compile(code);
|
||||
code.emplace_back(here, Op::_Let, std::move(left), right);
|
||||
}
|
||||
return right;
|
||||
}
|
||||
case _LetFirst: {
|
||||
|
|
|
@ -113,6 +113,7 @@ void define_keywords() {
|
|||
.add_keyword("forall", Kw::_Forall);
|
||||
|
||||
sym::symbols.add_keyword("extern", Kw::_Extern)
|
||||
.add_keyword("global", Kw::_Global)
|
||||
.add_keyword("asm", Kw::_Asm)
|
||||
.add_keyword("impure", Kw::_Impure)
|
||||
.add_keyword("inline", Kw::_Inline)
|
||||
|
|
|
@ -150,6 +150,21 @@ bool Optimizer::rewrite_const_push_swap() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Optimizer::is_const_rot() const {
|
||||
return pb_ >= 3 && pb_ <= l2_ && op_[0]->is_gconst() && tr_[pb_ - 1].is_const_rot();
|
||||
}
|
||||
|
||||
bool Optimizer::rewrite_const_rot() {
|
||||
p_ = pb_;
|
||||
q_ = 2;
|
||||
show_left();
|
||||
oq_[0] = std::move(op_[0]);
|
||||
oq_[1] = std::move(op_[1]);
|
||||
*oq_[1] = AsmOp::Custom("ROT", 3, 3);
|
||||
show_right();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Optimizer::is_const_push_xchgs() {
|
||||
if (!(pb_ >= 2 && pb_ <= l2_ && op_[0]->is_gconst())) {
|
||||
return false;
|
||||
|
@ -260,6 +275,21 @@ bool Optimizer::simple_rewrite(int p, AsmOp&& new_op1, AsmOp&& new_op2) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Optimizer::simple_rewrite(int p, AsmOp&& new_op1, AsmOp&& new_op2, AsmOp&& new_op3) {
|
||||
assert(p > 2 && p <= l_);
|
||||
p_ = p;
|
||||
q_ = 3;
|
||||
show_left();
|
||||
oq_[0] = std::move(op_[0]);
|
||||
*oq_[0] = new_op1;
|
||||
oq_[1] = std::move(op_[1]);
|
||||
*oq_[1] = new_op2;
|
||||
oq_[2] = std::move(op_[2]);
|
||||
*oq_[2] = new_op3;
|
||||
show_right();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Optimizer::simple_rewrite_nop() {
|
||||
assert(p_ > 0 && p_ <= l_);
|
||||
q_ = 0;
|
||||
|
@ -402,6 +432,16 @@ bool Optimizer::is_nip_seq(int* i, int* j) {
|
|||
return is_pred([i, j](const auto& t) { return t.is_nip_seq(i, j) && *i >= 3 && *i <= 15; });
|
||||
}
|
||||
|
||||
bool Optimizer::is_pop_blkdrop(int* i, int* k) {
|
||||
return is_pred([i, k](const auto& t) { return t.is_pop_blkdrop(i, k) && *i >= *k && *k >= 2 && *k <= 15; }, 3);
|
||||
}
|
||||
|
||||
bool Optimizer::is_2pop_blkdrop(int* i, int* j, int* k) {
|
||||
return is_pred(
|
||||
[i, j, k](const auto& t) { return t.is_2pop_blkdrop(i, j, k) && *i >= *k && *j >= *k && *k >= 2 && *k <= 15; },
|
||||
3);
|
||||
}
|
||||
|
||||
bool Optimizer::compute_stack_transforms() {
|
||||
StackTransform trans;
|
||||
for (int i = 0; i < l_; i++) {
|
||||
|
@ -450,7 +490,7 @@ bool Optimizer::find_at_least(int pb) {
|
|||
// show_stack_transforms();
|
||||
int i = -100, j = -100, k = -100;
|
||||
return (is_const_push_swap() && 3 >= pb && rewrite_const_push_swap()) || (is_nop() && simple_rewrite_nop()) ||
|
||||
(is_const_push_xchgs() && rewrite_const_push_xchgs()) ||
|
||||
(is_const_rot() && rewrite_const_rot()) || (is_const_push_xchgs() && rewrite_const_push_xchgs()) ||
|
||||
(is_xchg(&i, &j) && simple_rewrite(AsmOp::Xchg(i, j))) || (is_push(&i) && simple_rewrite(AsmOp::Push(i))) ||
|
||||
(is_pop(&i) && simple_rewrite(AsmOp::Pop(i))) || (is_rot() && simple_rewrite(AsmOp::Custom("ROT", 3, 3))) ||
|
||||
(is_rotrev() && simple_rewrite(AsmOp::Custom("-ROT", 3, 3))) ||
|
||||
|
@ -468,6 +508,10 @@ bool Optimizer::find_at_least(int pb) {
|
|||
(is_blkdrop(&i) && simple_rewrite(AsmOp::BlkDrop(i))) ||
|
||||
(is_reverse(&i, &j) && simple_rewrite(AsmOp::BlkReverse(i, j))) ||
|
||||
(is_nip_seq(&i, &j) && simple_rewrite(AsmOp::Xchg(i, j), AsmOp::BlkDrop(i))) ||
|
||||
(is_pop_blkdrop(&i, &k) && simple_rewrite(AsmOp::Pop(i), AsmOp::BlkDrop(k))) ||
|
||||
(is_2pop_blkdrop(&i, &j, &k) && (k >= 3 && k <= 13 && i != j + 1 && i <= 15 && j <= 14
|
||||
? simple_rewrite(AsmOp::Xchg2(j + 1, i), AsmOp::BlkDrop(k + 2))
|
||||
: simple_rewrite(AsmOp::Pop(i), AsmOp::Pop(j), AsmOp::BlkDrop(k)))) ||
|
||||
(is_xchg3(&i, &j, &k) && simple_rewrite(AsmOp::Xchg3(i, j, k))) ||
|
||||
(is_xc2pu(&i, &j, &k) && simple_rewrite(AsmOp::Xc2Pu(i, j, k))) ||
|
||||
(is_xcpuxc(&i, &j, &k) && simple_rewrite(AsmOp::XcPuXc(i, j, k))) ||
|
||||
|
|
|
@ -174,6 +174,53 @@ FormalArg parse_formal_arg(Lexer& lex, int fa_idx) {
|
|||
return std::make_tuple(arg_type, new_sym_def, loc);
|
||||
}
|
||||
|
||||
void parse_global_var_decl(Lexer& lex) {
|
||||
TypeExpr* var_type = 0;
|
||||
SrcLocation loc = lex.cur().loc;
|
||||
if (lex.tp() == '_') {
|
||||
lex.next();
|
||||
var_type = TypeExpr::new_hole();
|
||||
loc = lex.cur().loc;
|
||||
} else if (lex.tp() != _Ident) {
|
||||
var_type = parse_type(lex);
|
||||
} else {
|
||||
auto sym = sym::lookup_symbol(lex.cur().val);
|
||||
if (sym && dynamic_cast<SymValType*>(sym->value)) {
|
||||
auto val = dynamic_cast<SymValType*>(sym->value);
|
||||
lex.next();
|
||||
var_type = val->get_type();
|
||||
} else {
|
||||
var_type = TypeExpr::new_hole();
|
||||
}
|
||||
}
|
||||
if (lex.tp() != _Ident) {
|
||||
lex.expect(_Ident, "global variable name");
|
||||
}
|
||||
loc = lex.cur().loc;
|
||||
SymDef* sym_def = sym::define_global_symbol(lex.cur().val, false, loc);
|
||||
if (!sym_def) {
|
||||
lex.cur().error_at("cannot define global symbol `", "`");
|
||||
}
|
||||
if (sym_def->value) {
|
||||
auto val = dynamic_cast<SymValGlobVar*>(sym_def->value);
|
||||
if (!val) {
|
||||
lex.cur().error_at("symbol `", "` cannot be redefined as a global variable");
|
||||
}
|
||||
try {
|
||||
unify(var_type, val->sym_type);
|
||||
} catch (UnifyError& ue) {
|
||||
std::ostringstream os;
|
||||
os << "cannot unify new type " << var_type << " of global variable `" << sym_def->name()
|
||||
<< "` with its previous type " << val->sym_type << ": " << ue;
|
||||
lex.cur().error(os.str());
|
||||
}
|
||||
} else {
|
||||
sym_def->value = new SymValGlobVar{glob_var_cnt++, var_type};
|
||||
glob_vars.push_back(sym_def);
|
||||
}
|
||||
lex.next();
|
||||
}
|
||||
|
||||
FormalArgList parse_formal_args(Lexer& lex) {
|
||||
FormalArgList args;
|
||||
lex.expect('(', "formal argument list");
|
||||
|
@ -205,6 +252,18 @@ TypeExpr* extract_total_arg_type(const FormalArgList& arg_list) {
|
|||
return TypeExpr::new_tensor(std::move(type_list));
|
||||
}
|
||||
|
||||
void parse_global_var_decls(Lexer& lex) {
|
||||
lex.expect(_Global);
|
||||
while (true) {
|
||||
parse_global_var_decl(lex);
|
||||
if (lex.tp() != ',') {
|
||||
break;
|
||||
}
|
||||
lex.expect(',');
|
||||
}
|
||||
lex.expect(';');
|
||||
}
|
||||
|
||||
SymValCodeFunc* make_new_glob_func(SymDef* func_sym, TypeExpr* func_type, bool impure = false) {
|
||||
SymValCodeFunc* res = new SymValCodeFunc{glob_func_cnt, func_type, impure};
|
||||
func_sym->value = res;
|
||||
|
@ -239,6 +298,22 @@ bool check_global_func(const Lexem& cur, sym_idx_t func_name = 0) {
|
|||
}
|
||||
}
|
||||
|
||||
Expr* make_func_apply(Expr* fun, Expr* x) {
|
||||
Expr* res;
|
||||
if (fun->cls == Expr::_Glob) {
|
||||
if (x->cls == Expr::_Tuple) {
|
||||
res = new Expr{Expr::_Apply, fun->sym, x->args};
|
||||
} else {
|
||||
res = new Expr{Expr::_Apply, fun->sym, {x}};
|
||||
}
|
||||
res->flags = Expr::_IsRvalue | (fun->flags & Expr::_IsImpure);
|
||||
} else {
|
||||
res = new Expr{Expr::_VarApply, {fun, x}};
|
||||
res->flags = Expr::_IsRvalue;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Expr* parse_expr(Lexer& lex, CodeBlob& code, bool nv = false);
|
||||
|
||||
// parse ( E { , E } ) | () | id | num | _
|
||||
|
@ -323,6 +398,16 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
|
|||
lex.next();
|
||||
return res;
|
||||
}
|
||||
if (sym && dynamic_cast<SymValGlobVar*>(sym->value)) {
|
||||
auto val = dynamic_cast<SymValGlobVar*>(sym->value);
|
||||
Expr* res = new Expr{Expr::_GlobVar, lex.cur().loc};
|
||||
res->e_type = val->get_type();
|
||||
res->sym = sym;
|
||||
res->flags = Expr::_IsLvalue | Expr::_IsRvalue | Expr::_IsImpure;
|
||||
lex.next();
|
||||
return res;
|
||||
}
|
||||
bool auto_apply = false;
|
||||
Expr* res = new Expr{Expr::_Var, lex.cur().loc};
|
||||
if (nv) {
|
||||
res->val = ~lex.cur().val;
|
||||
|
@ -344,6 +429,7 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
|
|||
} else if (val->type == SymVal::_Func) {
|
||||
res->e_type = val->get_type();
|
||||
res->cls = Expr::_Glob;
|
||||
auto_apply = val->auto_apply;
|
||||
} else if (val->idx < 0) {
|
||||
lex.cur().error_at("accessing variable `", "` being defined");
|
||||
} else {
|
||||
|
@ -354,6 +440,12 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
|
|||
// std::cerr << "accessing symbol " << lex.cur().str << " : " << res->e_type << (val->impure ? " (impure)" : " (pure)") << std::endl;
|
||||
res->flags = Expr::_IsLvalue | Expr::_IsRvalue | (val->impure ? Expr::_IsImpure : 0);
|
||||
}
|
||||
if (auto_apply) {
|
||||
int impure = res->flags & Expr::_IsImpure;
|
||||
delete res;
|
||||
res = new Expr{Expr::_Apply, sym, {}};
|
||||
res->flags = Expr::_IsRvalue | impure;
|
||||
}
|
||||
res->deduce_type(lex.cur());
|
||||
lex.next();
|
||||
return res;
|
||||
|
@ -362,22 +454,6 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
Expr* make_func_apply(Expr* fun, Expr* x) {
|
||||
Expr* res;
|
||||
if (fun->cls == Expr::_Glob) {
|
||||
if (x->cls == Expr::_Tuple) {
|
||||
res = new Expr{Expr::_Apply, fun->sym, x->args};
|
||||
} else {
|
||||
res = new Expr{Expr::_Apply, fun->sym, {x}};
|
||||
}
|
||||
res->flags = Expr::_IsRvalue | (fun->flags & Expr::_IsImpure);
|
||||
} else {
|
||||
res = new Expr{Expr::_VarApply, {fun, x}};
|
||||
res->flags = Expr::_IsRvalue;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// parse E { E }
|
||||
Expr* parse_expr90(Lexer& lex, CodeBlob& code, bool nv) {
|
||||
Expr* res = parse_expr100(lex, code, nv);
|
||||
|
@ -1193,7 +1269,11 @@ bool parse_source(std::istream* is, const src::FileDescr* fdescr) {
|
|||
src::SourceReader reader{is, fdescr};
|
||||
Lexer lex{reader, true};
|
||||
while (lex.tp() != _Eof) {
|
||||
parse_func_def(lex);
|
||||
if (lex.tp() == _Global) {
|
||||
parse_global_var_decls(lex);
|
||||
} else {
|
||||
parse_func_def(lex);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -334,6 +334,13 @@ bool StackTransform::apply_pop(int i) {
|
|||
}
|
||||
}
|
||||
|
||||
bool StackTransform::apply_blkpop(int k) {
|
||||
if (!is_valid() || k < 0) {
|
||||
return invalidate();
|
||||
}
|
||||
return !k || (touch(k - 1) && shift(k));
|
||||
}
|
||||
|
||||
bool StackTransform::equal(const StackTransform &other, bool relaxed) const {
|
||||
if (!is_valid() || !other.is_valid()) {
|
||||
return false;
|
||||
|
@ -800,6 +807,49 @@ bool StackTransform::is_nip_seq(int *i, int *j) const {
|
|||
}
|
||||
}
|
||||
|
||||
// POP s(i); BLKDROP k (usually for i >= k >= 0)
|
||||
bool StackTransform::is_pop_blkdrop(int i, int k) const {
|
||||
StackTransform t;
|
||||
return is_valid() && d == k + 1 && t.apply_pop(i) && t.apply_blkpop(k) && t <= *this;
|
||||
}
|
||||
|
||||
// POP s(i); BLKDROP k == XCHG s0,s(i); BLKDROP k+1 for i >= k >= 0
|
||||
// k+1 k+2 .. i-1 0 i+1 ..
|
||||
bool StackTransform::is_pop_blkdrop(int *i, int *k) const {
|
||||
if (is_valid() && n == 1 && d > 0 && !A[0].second) {
|
||||
*k = d - 1;
|
||||
*i = A[0].first;
|
||||
return is_pop_blkdrop(*i, *k);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// POP s(i); POP s(j); BLKDROP k (usually for i<>j >= k >= 0)
|
||||
bool StackTransform::is_2pop_blkdrop(int i, int j, int k) const {
|
||||
StackTransform t;
|
||||
return is_valid() && d == k + 2 && t.apply_pop(i) && t.apply_pop(j) && t.apply_blkpop(k) && t <= *this;
|
||||
}
|
||||
|
||||
// POP s(i); POP s(j); BLKDROP k == XCHG s0,s(i); XCHG s1,s(j+1); BLKDROP k+2 (usually for i<>j >= k >= 2)
|
||||
// k+2 k+3 .. i-1 0 i+1 ... j 1 j+2 ...
|
||||
bool StackTransform::is_2pop_blkdrop(int *i, int *j, int *k) const {
|
||||
if (is_valid() && n == 2 && d >= 2 && A[0].second + A[1].second == 1) {
|
||||
*k = d - 2;
|
||||
int t = (A[0].second > 0);
|
||||
*i = A[t].first;
|
||||
*j = A[1 - t].first - 1;
|
||||
return is_2pop_blkdrop(*i, *j, *k);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// PUSHCONST c ; ROT == 1 -1000 0 2 3
|
||||
bool StackTransform::is_const_rot() const {
|
||||
return is_valid() && d == -1 && is_trivial_after(3) && get(0) == 1 && get(1) <= c_start && get(2) == 0;
|
||||
}
|
||||
|
||||
void StackTransform::show(std::ostream &os, int mode) const {
|
||||
if (!is_valid()) {
|
||||
os << "<invalid>";
|
||||
|
|
28
crypto/func/test/a11.fc
Normal file
28
crypto/func/test/a11.fc
Normal file
|
@ -0,0 +1,28 @@
|
|||
_ f(slice ds, int total_weight, int elect_at, cell frozen, int total_stakes, int cnt, cell credits, cell vdict, int elect) {
|
||||
return (ds, elect, credits);
|
||||
}
|
||||
|
||||
_ g(slice ds, int total_weight, int elect_at, cell frozen, int total_stakes, int cnt, cell credits, int elect) {
|
||||
return (ds, elect, credits);
|
||||
}
|
||||
|
||||
_ h(slice ds, int total_weight, int elect_at, cell frozen, int total_stakes, cell credits, int elect) {
|
||||
return (ds, elect, credits);
|
||||
}
|
||||
|
||||
_ h2(slice ds, int total_weight, int elect_at, cell frozen, int total_stakes, cell credits, int elect) {
|
||||
return (ds, credits, elect);
|
||||
}
|
||||
|
||||
_ h3(int pubkey, int adnl_addr, int stake, int tot_weight, tuple vinfo) {
|
||||
return (pubkey, tot_weight, stake, vinfo);
|
||||
}
|
||||
|
||||
_ z(int a, int b) {
|
||||
return (b, 0, a);
|
||||
}
|
||||
|
||||
_ z2(int a, int b) {
|
||||
return (0, a, b);
|
||||
}
|
||||
|
20
crypto/func/test/a6_5.fc
Normal file
20
crypto/func/test/a6_5.fc
Normal file
|
@ -0,0 +1,20 @@
|
|||
var twice(f, x) {
|
||||
return f (f x);
|
||||
}
|
||||
|
||||
_ sqr(x) {
|
||||
return x * x;
|
||||
}
|
||||
|
||||
var main(x) {
|
||||
var f = sqr;
|
||||
return twice(f, x) * f(x);
|
||||
}
|
||||
|
||||
var pow6(x) {
|
||||
return twice(sqr, x) * sqr(x);
|
||||
}
|
||||
|
||||
_ q() {
|
||||
return false;
|
||||
}
|
37
crypto/func/test/c1.fc
Normal file
37
crypto/func/test/c1.fc
Normal file
|
@ -0,0 +1,37 @@
|
|||
global int x, y, z;
|
||||
global (cell, slice) y;
|
||||
global ((int, int) -> int) op;
|
||||
|
||||
_ get() {
|
||||
var t = z + 1;
|
||||
return x;
|
||||
}
|
||||
|
||||
_ pre_next() {
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
() init() impure {
|
||||
;; global x;
|
||||
x = 0;
|
||||
}
|
||||
|
||||
int next() impure {
|
||||
;; global x;
|
||||
return x += 1;
|
||||
}
|
||||
|
||||
_ set_y(x, w) {
|
||||
y = (w, x);
|
||||
}
|
||||
|
||||
_ get_y() impure {
|
||||
return y;
|
||||
}
|
||||
|
||||
int main(int c) {
|
||||
init();
|
||||
c += next();
|
||||
return c + pre_next();
|
||||
}
|
||||
|
10
crypto/func/test/c2.fc
Normal file
10
crypto/func/test/c2.fc
Normal file
|
@ -0,0 +1,10 @@
|
|||
global ((int, int) -> int) op;
|
||||
|
||||
int check_assoc(int a, int b, int c) {
|
||||
return op(op(a, b), c) == op(a, op(b, c));
|
||||
}
|
||||
|
||||
int main() {
|
||||
op = _+_;
|
||||
return check_assoc(2, 3, 9);
|
||||
}
|
7
crypto/func/test/c2_1.fc
Normal file
7
crypto/func/test/c2_1.fc
Normal file
|
@ -0,0 +1,7 @@
|
|||
_ check_assoc(op, a, b, c) {
|
||||
return op(op(a, b), c) == op(a, op(b, c));
|
||||
}
|
||||
|
||||
int main() {
|
||||
return check_assoc(_+_, 2, 3, 9);
|
||||
}
|
|
@ -32,7 +32,7 @@ namespace sym {
|
|||
typedef int var_idx_t;
|
||||
|
||||
struct SymValBase {
|
||||
enum { _Param, _Var, _Func, _Typename };
|
||||
enum { _Param, _Var, _Func, _Typename, _GlobVar };
|
||||
int type;
|
||||
int idx;
|
||||
SymValBase(int _type, int _idx) : type(_type), idx(_idx) {
|
||||
|
|
|
@ -242,3 +242,7 @@ dictnew constant special-dict
|
|||
register_smc
|
||||
Masterchain swap 6 .Addr cr
|
||||
} : create-wallet0
|
||||
|
||||
{ dup tlb-type-lookup { nip } { "unknown TLB type " swap $+ abort } cond } : $>tlb
|
||||
{ bl word $>tlb 1 'nop } ::_ tlb:
|
||||
{ <b swap vmcont, b> <s tlb:VmCont tlb-dump-as } : vmcont.
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "td/utils/port/path.h"
|
||||
#include "td/utils/format.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/optional.h"
|
||||
#include "td/utils/tests.h"
|
||||
#include "td/utils/tl_parsers.h"
|
||||
#include "td/utils/tl_helpers.h"
|
||||
|
@ -529,16 +530,20 @@ TEST(Cell, MerkleProofCombine) {
|
|||
ASSERT_EQ(exploration_b.log, exploration2.log);
|
||||
}
|
||||
|
||||
Ref<Cell> proof_union;
|
||||
{
|
||||
proof_union = MerkleProof::combine(proof1, proof2);
|
||||
auto check = [&](auto proof_union) {
|
||||
auto virtualized_proof = MerkleProof::virtualize(proof_union, 1);
|
||||
auto exploration_a = CellExplorer::explore(virtualized_proof, exploration1.ops);
|
||||
auto exploration_b = CellExplorer::explore(virtualized_proof, exploration2.ops);
|
||||
ASSERT_EQ(exploration_a.log, exploration1.log);
|
||||
ASSERT_EQ(exploration_b.log, exploration2.log);
|
||||
};
|
||||
auto proof_union = MerkleProof::combine(proof1, proof2);
|
||||
ASSERT_EQ(proof_union->get_hash(), proof12->get_hash());
|
||||
check(proof_union);
|
||||
|
||||
auto virtualized_proof = MerkleProof::virtualize(proof_union, 1);
|
||||
auto exploration_a = CellExplorer::explore(virtualized_proof, exploration1.ops);
|
||||
auto exploration_b = CellExplorer::explore(virtualized_proof, exploration2.ops);
|
||||
ASSERT_EQ(exploration_a.log, exploration1.log);
|
||||
ASSERT_EQ(exploration_b.log, exploration2.log);
|
||||
auto proof_union_fast = MerkleProof::combine_fast(proof1, proof2);
|
||||
check(proof_union_fast);
|
||||
}
|
||||
{
|
||||
auto cell = MerkleProof::virtualize(proof12, 1);
|
||||
|
@ -1194,6 +1199,88 @@ TEST(Cell, MerkleProofArrayHands) {
|
|||
test_boc_deserializer_full(proof).ensure();
|
||||
test_boc_deserializer_full(CellBuilder::create_merkle_proof(proof)).ensure();
|
||||
}
|
||||
|
||||
TEST(Cell, MerkleProofCombineArray) {
|
||||
size_t n = 1 << 15;
|
||||
std::vector<td::uint64> data;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
data.push_back(i / 3);
|
||||
}
|
||||
CompactArray arr(data);
|
||||
|
||||
td::Ref<vm::Cell> root = vm::CellBuilder::create_merkle_proof(arr.merkle_proof({}));
|
||||
td::Timer timer;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
auto new_root = vm::CellBuilder::create_merkle_proof(arr.merkle_proof({i}));
|
||||
root = vm::MerkleProof::combine_fast(root, new_root);
|
||||
if ((i - 1) % 100 == 0) {
|
||||
LOG(ERROR) << timer;
|
||||
timer = {};
|
||||
}
|
||||
}
|
||||
|
||||
CompactArray arr2(n, vm::MerkleProof::virtualize(root, 1));
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
CHECK(arr.get(i) == arr2.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Cell, MerkleProofCombineArray2) {
|
||||
auto a = vm::CellBuilder().store_long(1, 8).finalize();
|
||||
auto b = vm::CellBuilder().store_long(2, 8).finalize();
|
||||
auto c = vm::CellBuilder().store_long(3, 8).finalize();
|
||||
auto d = vm::CellBuilder().store_long(4, 8).finalize();
|
||||
auto left = vm::CellBuilder().store_ref(a).store_ref(b).finalize();
|
||||
auto right = vm::CellBuilder().store_ref(c).store_ref(d).finalize();
|
||||
auto x = vm::CellBuilder().store_ref(left).store_ref(right).finalize();
|
||||
size_t n = 18;
|
||||
//TODO: n = 100, currently TL
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
x = vm::CellBuilder().store_ref(x).store_ref(x).finalize();
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> root;
|
||||
auto apply_op = [&](auto op) {
|
||||
auto usage_tree = std::make_shared<CellUsageTree>();
|
||||
auto usage_cell = UsageCell::create(x, usage_tree->root_ptr());
|
||||
root = usage_cell;
|
||||
op();
|
||||
return MerkleProof::generate(root, usage_tree.get());
|
||||
};
|
||||
|
||||
auto first = apply_op([&] {
|
||||
auto x = root;
|
||||
while (true) {
|
||||
auto cs = vm::load_cell_slice(x);
|
||||
if (cs.size_refs() == 0) {
|
||||
break;
|
||||
}
|
||||
x = cs.prefetch_ref(0);
|
||||
}
|
||||
});
|
||||
auto second = apply_op([&] {
|
||||
auto x = root;
|
||||
while (true) {
|
||||
auto cs = vm::load_cell_slice(x);
|
||||
if (cs.size_refs() == 0) {
|
||||
break;
|
||||
}
|
||||
x = cs.prefetch_ref(1);
|
||||
}
|
||||
});
|
||||
|
||||
{
|
||||
td::Timer t;
|
||||
auto x = vm::MerkleProof::combine(first, second);
|
||||
LOG(ERROR) << "slow " << t;
|
||||
}
|
||||
{
|
||||
td::Timer t;
|
||||
auto x = vm::MerkleProof::combine_fast(first, second);
|
||||
LOG(ERROR) << "fast " << t;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Cell, MerkleUpdateHands) {
|
||||
auto data = CellBuilder{}.store_bytes("pruned data").store_ref(CellBuilder{}.finalize()).finalize();
|
||||
auto node = CellBuilder{}.store_bytes("protected data").store_ref(data).finalize();
|
||||
|
@ -1965,6 +2052,222 @@ TEST(Ref, AtomicRef) {
|
|||
LOG(ERROR) << String::total_strings.sum();
|
||||
}
|
||||
|
||||
class FileMerkleTree {
|
||||
public:
|
||||
FileMerkleTree(size_t chunks_count, td::Ref<vm::Cell> root = {}) {
|
||||
log_n_ = 0;
|
||||
while ((size_t(1) << log_n_) < chunks_count) {
|
||||
log_n_++;
|
||||
}
|
||||
n_ = size_t(1) << log_n_;
|
||||
mark_.resize(n_ * 2);
|
||||
proof_.resize(n_ * 2);
|
||||
|
||||
CHECK(n_ == chunks_count); // TODO: support other chunks_count
|
||||
//auto x = vm::CellBuilder().finalize();
|
||||
root_ = std::move(root);
|
||||
}
|
||||
|
||||
struct Chunk {
|
||||
td::size_t index{0};
|
||||
td::Slice hash;
|
||||
};
|
||||
|
||||
void remove_chunk(td::size_t index) {
|
||||
CHECK(index < n_);
|
||||
index += n_;
|
||||
while (proof_[index].not_null()) {
|
||||
proof_[index] = {};
|
||||
index /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
bool has_chunk(td::size_t index) const {
|
||||
CHECK(index < n_);
|
||||
index += n_;
|
||||
return proof_[index].not_null();
|
||||
}
|
||||
|
||||
void add_chunk(td::size_t index, td::Slice hash) {
|
||||
CHECK(hash.size() == 32);
|
||||
CHECK(index < n_);
|
||||
index += n_;
|
||||
auto cell = vm::CellBuilder().store_bytes(hash).finalize();
|
||||
CHECK(proof_[index].is_null());
|
||||
proof_[index] = std::move(cell);
|
||||
for (index /= 2; index != 0; index /= 2) {
|
||||
CHECK(proof_[index].is_null());
|
||||
auto &left = proof_[index * 2];
|
||||
auto &right = proof_[index * 2 + 1];
|
||||
if (left.not_null() && right.not_null()) {
|
||||
proof_[index] = vm::CellBuilder().store_ref(left).store_ref(right).finalize();
|
||||
} else {
|
||||
mark_[index] = mark_id_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
td::Status validate_proof(td::Ref<vm::Cell> new_root) {
|
||||
// TODO: check structure
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Status add_proof(td::Ref<vm::Cell> new_root) {
|
||||
TRY_STATUS(validate_proof(new_root));
|
||||
auto combined = vm::MerkleProof::combine_fast_raw(root_, new_root);
|
||||
if (combined.is_null()) {
|
||||
return td::Status::Error("Can't combine proofs");
|
||||
}
|
||||
root_ = std::move(combined);
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Status try_add_chunks(td::Span<Chunk> chunks) {
|
||||
for (auto chunk : chunks) {
|
||||
if (has_chunk(chunk.index)) {
|
||||
return td::Status::Error("Already has chunk");
|
||||
}
|
||||
}
|
||||
mark_id_++;
|
||||
for (auto chunk : chunks) {
|
||||
add_chunk(chunk.index, chunk.hash);
|
||||
}
|
||||
auto r_new_root = merge(root_, 1);
|
||||
if (r_new_root.is_error()) {
|
||||
for (auto chunk : chunks) {
|
||||
remove_chunk(chunk.index);
|
||||
}
|
||||
return r_new_root.move_as_error();
|
||||
}
|
||||
root_ = r_new_root.move_as_ok();
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Result<td::Ref<vm::Cell>> merge(td::Ref<vm::Cell> root, size_t index) {
|
||||
const auto &down = proof_[index];
|
||||
if (down.not_null()) {
|
||||
if (down->get_hash() != root->get_hash(0)) {
|
||||
return td::Status::Error("Hash mismatch");
|
||||
}
|
||||
return down;
|
||||
}
|
||||
|
||||
if (mark_[index] != mark_id_) {
|
||||
return root;
|
||||
}
|
||||
|
||||
vm::CellSlice cs(vm::NoVm(), root);
|
||||
if (cs.is_special()) {
|
||||
return td::Status::Error("Proof is not enough to validate chunks");
|
||||
}
|
||||
|
||||
CHECK(cs.size_refs() == 2);
|
||||
vm::CellBuilder cb;
|
||||
cb.store_bits(cs.fetch_bits(cs.size()));
|
||||
TRY_RESULT(left, merge(cs.fetch_ref(), index * 2));
|
||||
TRY_RESULT(right, merge(cs.fetch_ref(), index * 2 + 1));
|
||||
cb.store_ref(std::move(left)).store_ref(std::move(right));
|
||||
return cb.finalize();
|
||||
}
|
||||
|
||||
void init_proof() {
|
||||
CHECK(proof_[1].not_null());
|
||||
root_ = proof_[1];
|
||||
}
|
||||
|
||||
td::Result<td::Ref<vm::Cell>> gen_proof(size_t l, size_t r) {
|
||||
auto usage_tree = std::make_shared<vm::CellUsageTree>();
|
||||
auto usage_cell = vm::UsageCell::create(root_, usage_tree->root_ptr());
|
||||
TRY_STATUS(do_gen_proof(std::move(usage_cell), 0, n_ - 1, l, r));
|
||||
auto res = vm::MerkleProof::generate_raw(root_, usage_tree.get());
|
||||
CHECK(res.not_null());
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
td::size_t n_; // n = 2^log_n
|
||||
td::size_t log_n_;
|
||||
td::size_t mark_id_{0};
|
||||
std::vector<td::size_t> mark_; // n_ * 2
|
||||
std::vector<td::Ref<vm::Cell>> proof_; // n_ * 2
|
||||
td::Ref<vm::Cell> root_;
|
||||
|
||||
td::Status do_gen_proof(td::Ref<vm::Cell> node, size_t il, size_t ir, size_t l, size_t r) {
|
||||
if (ir < l || il > r) {
|
||||
return td::Status::OK();
|
||||
}
|
||||
if (l <= il && ir <= r) {
|
||||
return td::Status::OK();
|
||||
}
|
||||
vm::CellSlice cs(vm::NoVm(), std::move(node));
|
||||
if (cs.is_special()) {
|
||||
return td::Status::Error("Can't generate a proof");
|
||||
}
|
||||
CHECK(cs.size_refs() == 2);
|
||||
auto ic = (il + ir) / 2;
|
||||
TRY_STATUS(do_gen_proof(cs.fetch_ref(), il, ic, l, r));
|
||||
TRY_STATUS(do_gen_proof(cs.fetch_ref(), ic + 1, ir, l, r));
|
||||
return td::Status::OK();
|
||||
}
|
||||
};
|
||||
|
||||
TEST(FileMerkleTree, Manual) {
|
||||
// create big random file
|
||||
size_t chunk_size = 768;
|
||||
// for simplicity numer of chunks in a file is a power of two
|
||||
size_t chunks_count = 1 << 16;
|
||||
size_t file_size = chunk_size * chunks_count;
|
||||
td::Timer timer;
|
||||
LOG(INFO) << "Generate random string";
|
||||
const auto file = td::rand_string('a', 'z', td::narrow_cast<int>(file_size));
|
||||
LOG(INFO) << timer;
|
||||
|
||||
timer = {};
|
||||
LOG(INFO) << "Calculate all hashes";
|
||||
std::vector<td::UInt256> hashes(chunks_count);
|
||||
for (size_t i = 0; i < chunks_count; i++) {
|
||||
td::sha256(td::Slice(file).substr(i * chunk_size, chunk_size), hashes[i].as_slice());
|
||||
}
|
||||
LOG(INFO) << timer;
|
||||
|
||||
timer = {};
|
||||
LOG(INFO) << "Init merkle tree";
|
||||
FileMerkleTree tree(chunks_count);
|
||||
for (size_t i = 0; i < chunks_count; i++) {
|
||||
tree.add_chunk(i, hashes[i].as_slice());
|
||||
}
|
||||
tree.init_proof();
|
||||
LOG(INFO) << timer;
|
||||
|
||||
auto root_proof = tree.gen_proof(0, chunks_count - 1).move_as_ok();
|
||||
|
||||
// first download each chunk one by one
|
||||
|
||||
for (size_t stride : {1 << 6, 1}) {
|
||||
timer = {};
|
||||
LOG(INFO) << "Gen all proofs, stride = " << stride;
|
||||
for (size_t i = 0; i < chunks_count; i += stride) {
|
||||
tree.gen_proof(i, i + stride - 1).move_as_ok();
|
||||
}
|
||||
LOG(INFO) << timer;
|
||||
timer = {};
|
||||
LOG(INFO) << "Proof size: " << vm::std_boc_serialize(tree.gen_proof(0, stride - 1).move_as_ok()).ok().size();
|
||||
LOG(INFO) << "Download file, stride = " << stride;
|
||||
{
|
||||
FileMerkleTree new_tree(chunks_count, root_proof);
|
||||
for (size_t i = 0; i < chunks_count; i += stride) {
|
||||
new_tree.add_proof(tree.gen_proof(i, i + stride - 1).move_as_ok()).ensure();
|
||||
std::vector<FileMerkleTree::Chunk> chunks;
|
||||
for (size_t j = 0; j < stride; j++) {
|
||||
chunks.push_back({i + j, hashes[i + j].as_slice()});
|
||||
}
|
||||
new_tree.try_add_chunks(chunks).ensure();
|
||||
}
|
||||
}
|
||||
LOG(INFO) << timer;
|
||||
}
|
||||
}
|
||||
|
||||
//TEST(Tmp, Boc) {
|
||||
//LOG(ERROR) << "A";
|
||||
//auto data = td::read_file("boc");
|
||||
|
|
|
@ -3257,6 +3257,31 @@ void generate_type_constants(std::ostream& os, int mode) {
|
|||
}
|
||||
}
|
||||
|
||||
void generate_register_function(std::ostream& os, int mode) {
|
||||
os << "\n// " << (mode ? "definition" : "declaration") << " of type name registration function\n";
|
||||
if (!mode) {
|
||||
os << "extern bool register_simple_types(std::function<bool(const char*, const TLB*)> func);\n";
|
||||
return;
|
||||
}
|
||||
os << "bool register_simple_types(std::function<bool(const char*, const TLB*)> func) {\n";
|
||||
os << " return ";
|
||||
int k = 0;
|
||||
for (int i = builtin_types_num; i < types_num; i++) {
|
||||
Type& type = types[i];
|
||||
CppTypeCode& cc = *cpp_type[i];
|
||||
if (!cc.cpp_type_var_name.empty() && type.type_name) {
|
||||
if (k++) {
|
||||
os << "\n && ";
|
||||
}
|
||||
os << "func(\"" << type.get_name() << "\", &" << cc.cpp_type_var_name << ")";
|
||||
}
|
||||
}
|
||||
if (!k) {
|
||||
os << "true";
|
||||
}
|
||||
os << ";\n}\n\n";
|
||||
}
|
||||
|
||||
void assign_const_type_cpp_idents() {
|
||||
const_type_expr_cpp_idents.resize(const_type_expr_num + 1, "");
|
||||
const_type_expr_simple.resize(const_type_expr_num + 1, false);
|
||||
|
@ -3389,6 +3414,7 @@ void generate_cpp_output_to(std::ostream& os, int options = 0, std::vector<std::
|
|||
cc.generate(os, (options & -4) | pass);
|
||||
}
|
||||
generate_type_constants(os, pass - 1);
|
||||
generate_register_function(os, pass - 1);
|
||||
}
|
||||
}
|
||||
for (auto it = cpp_namespace_list.rbegin(); it != cpp_namespace_list.rend(); ++it) {
|
||||
|
|
|
@ -346,3 +346,24 @@ bool PrettyPrinter::fetch_uint256_field(vm::CellSlice& cs, int n, std::string na
|
|||
}
|
||||
|
||||
} // namespace tlb
|
||||
|
||||
namespace tlb {
|
||||
|
||||
bool TypenameLookup::register_types(typename TypenameLookup::register_func_t func) {
|
||||
return func([this](const char* name, const TLB* tp) { return register_type(name, tp); });
|
||||
}
|
||||
|
||||
bool TypenameLookup::register_type(const char* name, const TLB* tp) {
|
||||
if (!name || !tp) {
|
||||
return false;
|
||||
}
|
||||
auto res = types.emplace(name, tp);
|
||||
return res.second;
|
||||
}
|
||||
|
||||
const TLB* TypenameLookup::lookup(std::string str) const {
|
||||
auto it = types.find(str);
|
||||
return it != types.end() ? it->second : nullptr;
|
||||
}
|
||||
|
||||
} // namespace tlb
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
#pragma once
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include "vm/cellslice.h"
|
||||
|
||||
namespace tlb {
|
||||
|
@ -268,6 +269,30 @@ struct TLB_Complex : TLB {
|
|||
}
|
||||
};
|
||||
|
||||
class TlbTypeHolder : public td::CntObject {
|
||||
const TLB* type{nullptr};
|
||||
char* data{nullptr};
|
||||
|
||||
public:
|
||||
TlbTypeHolder() = default;
|
||||
TlbTypeHolder(const TLB* _type) : type(_type), data(nullptr) {
|
||||
}
|
||||
TlbTypeHolder(const TLB* _type, char* _data) : type(_type), data(_data) {
|
||||
}
|
||||
~TlbTypeHolder() override {
|
||||
free(data);
|
||||
}
|
||||
const TLB* get() const {
|
||||
return type;
|
||||
}
|
||||
const TLB& operator*() const {
|
||||
return *type;
|
||||
}
|
||||
const TLB* operator->() const {
|
||||
return type;
|
||||
}
|
||||
};
|
||||
|
||||
static inline bool add_chk(int x, int y, int z) {
|
||||
return x + y == z && z >= 0;
|
||||
}
|
||||
|
@ -508,6 +533,25 @@ struct PrettyPrinter {
|
|||
|
||||
namespace tlb {
|
||||
|
||||
class TypenameLookup {
|
||||
std::map<std::string, const TLB*> types;
|
||||
|
||||
public:
|
||||
typedef std::function<bool(const char*, const TLB*)> simple_register_func_t;
|
||||
typedef std::function<bool(simple_register_func_t)> register_func_t;
|
||||
TypenameLookup() = default;
|
||||
TypenameLookup(register_func_t func) {
|
||||
register_types(func);
|
||||
}
|
||||
bool register_type(const char* name, const TLB* tp);
|
||||
bool register_types(register_func_t func);
|
||||
const TLB* lookup(std::string str) const;
|
||||
};
|
||||
|
||||
} // namespace tlb
|
||||
|
||||
namespace tlb {
|
||||
|
||||
struct False final : TLB {
|
||||
int get_size(const vm::CellSlice& cs) const override {
|
||||
return -1;
|
||||
|
|
|
@ -143,19 +143,80 @@ Ref<Cell> MerkleProof::virtualize(Ref<Cell> cell, int virtualization) {
|
|||
return virtualize_raw(r_raw.move_as_ok(), {0 /*level*/, static_cast<td::uint8>(virtualization)});
|
||||
}
|
||||
|
||||
class MerkleProofCombineFast {
|
||||
public:
|
||||
MerkleProofCombineFast(Ref<Cell> a, Ref<Cell> b) : a_(std::move(a)), b_(std::move(b)) {
|
||||
}
|
||||
td::Result<Ref<Cell>> run() {
|
||||
TRY_RESULT_ASSIGN(a_, unpack_proof(a_));
|
||||
TRY_RESULT_ASSIGN(b_, unpack_proof(b_));
|
||||
TRY_RESULT(res, run_raw());
|
||||
return CellBuilder::create_merkle_proof(std::move(res));
|
||||
}
|
||||
|
||||
td::Result<Ref<Cell>> run_raw() {
|
||||
if (a_->get_hash(0) != b_->get_hash(0)) {
|
||||
return td::Status::Error("Can't combine MerkleProofs with different roots");
|
||||
}
|
||||
return merge(a_, b_, 0);
|
||||
}
|
||||
|
||||
private:
|
||||
Ref<Cell> a_;
|
||||
Ref<Cell> b_;
|
||||
|
||||
Ref<Cell> merge(Ref<Cell> a, Ref<Cell> b, td::uint32 merkle_depth) {
|
||||
if (a->get_hash() == b->get_hash()) {
|
||||
return a;
|
||||
}
|
||||
if (a->get_level() == merkle_depth) {
|
||||
return a;
|
||||
}
|
||||
if (b->get_level() == merkle_depth) {
|
||||
return b;
|
||||
}
|
||||
|
||||
CellSlice csa(NoVm(), a);
|
||||
CellSlice csb(NoVm(), b);
|
||||
|
||||
if (csa.is_special() && csa.special_type() == vm::Cell::SpecialType::PrunnedBranch) {
|
||||
return b;
|
||||
}
|
||||
if (csb.is_special() && csb.special_type() == vm::Cell::SpecialType::PrunnedBranch) {
|
||||
return a;
|
||||
}
|
||||
|
||||
CHECK(csa.size_refs() != 0);
|
||||
|
||||
auto child_merkle_depth = csa.child_merkle_depth(merkle_depth);
|
||||
|
||||
CellBuilder cb;
|
||||
cb.store_bits(csa.fetch_bits(csa.size()));
|
||||
for (unsigned i = 0; i < csa.size_refs(); i++) {
|
||||
cb.store_ref(merge(csa.prefetch_ref(i), csb.prefetch_ref(i), child_merkle_depth));
|
||||
}
|
||||
return cb.finalize(csa.is_special());
|
||||
}
|
||||
};
|
||||
|
||||
class MerkleProofCombine {
|
||||
public:
|
||||
MerkleProofCombine(Ref<Cell> a, Ref<Cell> b) : a_(std::move(a)), b_(std::move(b)) {
|
||||
}
|
||||
td::Result<Ref<Cell>> run() {
|
||||
TRY_RESULT(a, unpack_proof(a_));
|
||||
TRY_RESULT(b, unpack_proof(b_));
|
||||
if (a->get_hash(0) != b->get_hash(0)) {
|
||||
TRY_RESULT_ASSIGN(a_, unpack_proof(a_));
|
||||
TRY_RESULT_ASSIGN(b_, unpack_proof(b_));
|
||||
TRY_RESULT(res, run_raw());
|
||||
return CellBuilder::create_merkle_proof(std::move(res));
|
||||
}
|
||||
|
||||
td::Result<Ref<Cell>> run_raw() {
|
||||
if (a_->get_hash(0) != b_->get_hash(0)) {
|
||||
return td::Status::Error("Can't combine MerkleProofs with different roots");
|
||||
}
|
||||
dfs(a, 0);
|
||||
dfs(b, 0);
|
||||
return CellBuilder::create_merkle_proof(create_A(a, 0, 0));
|
||||
dfs(a_, 0);
|
||||
dfs(b_, 0);
|
||||
return create_A(a_, 0, 0);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -262,6 +323,30 @@ Ref<Cell> MerkleProof::combine(Ref<Cell> a, Ref<Cell> b) {
|
|||
return res.move_as_ok();
|
||||
}
|
||||
|
||||
Ref<Cell> MerkleProof::combine_fast(Ref<Cell> a, Ref<Cell> b) {
|
||||
auto res = MerkleProofCombineFast(std::move(a), std::move(b)).run();
|
||||
if (res.is_error()) {
|
||||
return {};
|
||||
}
|
||||
return res.move_as_ok();
|
||||
}
|
||||
|
||||
Ref<Cell> MerkleProof::combine_raw(Ref<Cell> a, Ref<Cell> b) {
|
||||
auto res = MerkleProofCombine(std::move(a), std::move(b)).run_raw();
|
||||
if (res.is_error()) {
|
||||
return {};
|
||||
}
|
||||
return res.move_as_ok();
|
||||
}
|
||||
|
||||
Ref<Cell> MerkleProof::combine_fast_raw(Ref<Cell> a, Ref<Cell> b) {
|
||||
auto res = MerkleProofCombineFast(std::move(a), std::move(b)).run_raw();
|
||||
if (res.is_error()) {
|
||||
return {};
|
||||
}
|
||||
return res.move_as_ok();
|
||||
}
|
||||
|
||||
MerkleProofBuilder::MerkleProofBuilder(Ref<Cell> root)
|
||||
: usage_tree(std::make_shared<CellUsageTree>()), orig_root(std::move(root)) {
|
||||
usage_root = UsageCell::create(orig_root, usage_tree->root_ptr());
|
||||
|
|
|
@ -38,12 +38,15 @@ class MerkleProof {
|
|||
static Ref<Cell> virtualize(Ref<Cell> cell, int virtualization);
|
||||
|
||||
static Ref<Cell> combine(Ref<Cell> a, Ref<Cell> b);
|
||||
static Ref<Cell> combine_fast(Ref<Cell> a, Ref<Cell> b);
|
||||
|
||||
// works with upwrapped proofs
|
||||
// works fine with cell of non-zero level, but this is not supported (yet?) in MerkeProof special cell
|
||||
static Ref<Cell> generate_raw(Ref<Cell> cell, IsPrunnedFunction is_prunned);
|
||||
static Ref<Cell> generate_raw(Ref<Cell> cell, CellUsageTree *usage_tree);
|
||||
static Ref<Cell> virtualize_raw(Ref<Cell> cell, Cell::VirtualizationParameters virt);
|
||||
static Ref<Cell> combine_raw(Ref<Cell> a, Ref<Cell> b);
|
||||
static Ref<Cell> combine_fast_raw(Ref<Cell> a, Ref<Cell> b);
|
||||
};
|
||||
|
||||
class MerkleProofBuilder {
|
||||
|
|
|
@ -268,7 +268,7 @@ class MerkleCombine {
|
|||
// 1. Create maximum subtrees of X and Z with all cells in A, B, C and D
|
||||
// Max(V) - such maximum subtree
|
||||
//
|
||||
// 2. Max(A) and Max(D) should be merkle update already. But win want to minimize it
|
||||
// 2. Max(A) and Max(D) should be merkle update already. But we want to minimize it
|
||||
// So we cut all branches of Max(D) which are in maxA
|
||||
// When we cut branch q in Max(D) we mark some path to q in Max(A)
|
||||
// Then we cut all branches of Max(A) which are not marked.
|
||||
|
|
|
@ -1055,7 +1055,7 @@ ControlRegs* force_cregs(Ref<Continuation>& cont) {
|
|||
}
|
||||
|
||||
int run_vm_code(Ref<CellSlice> code, Ref<Stack>& stack, int flags, Ref<Cell>* data_ptr, VmLog log, long long* steps,
|
||||
GasLimits* gas_limits, std::vector<Ref<Cell>> libraries, Ref<Tuple> init_c7) {
|
||||
GasLimits* gas_limits, std::vector<Ref<Cell>> libraries, Ref<Tuple> init_c7, Ref<Cell>* actions_ptr) {
|
||||
VmState vm{code,
|
||||
std::move(stack),
|
||||
gas_limits ? *gas_limits : GasLimits{},
|
||||
|
@ -1066,8 +1066,11 @@ int run_vm_code(Ref<CellSlice> code, Ref<Stack>& stack, int flags, Ref<Cell>* da
|
|||
std::move(init_c7)};
|
||||
int res = vm.run();
|
||||
stack = vm.get_stack_ref();
|
||||
if (res == -1 && data_ptr) {
|
||||
*data_ptr = vm.get_c4();
|
||||
if (vm.committed() && data_ptr) {
|
||||
*data_ptr = vm.get_committed_state().c4;
|
||||
}
|
||||
if (vm.committed() && actions_ptr) {
|
||||
*actions_ptr = vm.get_committed_state().c5;
|
||||
}
|
||||
if (steps) {
|
||||
*steps = vm.get_steps_count();
|
||||
|
@ -1090,11 +1093,12 @@ int run_vm_code(Ref<CellSlice> code, Ref<Stack>& stack, int flags, Ref<Cell>* da
|
|||
}
|
||||
|
||||
int run_vm_code(Ref<CellSlice> code, Stack& stack, int flags, Ref<Cell>* data_ptr, VmLog log, long long* steps,
|
||||
GasLimits* gas_limits, std::vector<Ref<Cell>> libraries, Ref<Tuple> init_c7) {
|
||||
GasLimits* gas_limits, std::vector<Ref<Cell>> libraries, Ref<Tuple> init_c7, Ref<Cell>* actions_ptr) {
|
||||
Ref<Stack> stk{true};
|
||||
stk.unique_write().set_contents(std::move(stack));
|
||||
stack.clear();
|
||||
int res = run_vm_code(code, stk, flags, data_ptr, log, steps, gas_limits, std::move(libraries), std::move(init_c7));
|
||||
int res = run_vm_code(code, stk, flags, data_ptr, log, steps, gas_limits, std::move(libraries), std::move(init_c7),
|
||||
actions_ptr);
|
||||
CHECK(stack.is_unique());
|
||||
if (stk.is_null()) {
|
||||
stack.clear();
|
||||
|
|
|
@ -644,12 +644,12 @@ class VmState final : public VmStateInterface {
|
|||
void init_cregs(bool same_c3 = false, bool push_0 = true);
|
||||
};
|
||||
|
||||
int run_vm_code(Ref<CellSlice> _code, Ref<Stack>& _stack, int flags = 0, Ref<Cell>* data_ptr = 0, VmLog log = {},
|
||||
int run_vm_code(Ref<CellSlice> _code, Ref<Stack>& _stack, int flags = 0, Ref<Cell>* data_ptr = nullptr, VmLog log = {},
|
||||
long long* steps = nullptr, GasLimits* gas_limits = nullptr, std::vector<Ref<Cell>> libraries = {},
|
||||
Ref<Tuple> init_c7 = {});
|
||||
int run_vm_code(Ref<CellSlice> _code, Stack& _stack, int flags = 0, Ref<Cell>* data_ptr = 0, VmLog log = {},
|
||||
Ref<Tuple> init_c7 = {}, Ref<Cell>* actions_ptr = nullptr);
|
||||
int run_vm_code(Ref<CellSlice> _code, Stack& _stack, int flags = 0, Ref<Cell>* data_ptr = nullptr, VmLog log = {},
|
||||
long long* steps = nullptr, GasLimits* gas_limits = nullptr, std::vector<Ref<Cell>> libraries = {},
|
||||
Ref<Tuple> init_c7 = {});
|
||||
Ref<Tuple> init_c7 = {}, Ref<Cell>* actions_ptr = nullptr);
|
||||
|
||||
ControlData* force_cdata(Ref<Continuation>& cont);
|
||||
ControlRegs* force_cregs(Ref<Continuation>& cont);
|
||||
|
|
|
@ -481,6 +481,18 @@ class Stack : public td::CntObject {
|
|||
Ref<Atom> pop_atom();
|
||||
std::string pop_string();
|
||||
std::string pop_bytes();
|
||||
template <typename T>
|
||||
Ref<T> pop_object() {
|
||||
return pop_chk().as_object<T>();
|
||||
}
|
||||
template <typename T>
|
||||
Ref<T> pop_object_type_chk() {
|
||||
auto res = pop_object<T>();
|
||||
if (!res) {
|
||||
throw VmError{Excno::type_chk, "not an object of required type"};
|
||||
}
|
||||
return res;
|
||||
}
|
||||
void push_null();
|
||||
void push_int(td::RefInt256 val);
|
||||
void push_int_quiet(td::RefInt256 val, bool quiet = true);
|
||||
|
|
|
@ -1338,8 +1338,10 @@ Fift has several words for {\em hashmap\/} or {\em (TVM) dictionary\/} manipulat
|
|||
\item {\tt idict!+} ($v$ $x$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new key-value pair $(x,v)$ into dictionary $D$ similarly to {\tt idict!}, but fails if the key already exists by returning the unchanged dictionary $D$ and $0$.
|
||||
\item {\tt b>idict!}, {\tt b>idict!+}, variants of {\tt idict!} and {\tt idict!+} accepting the new value $v$ in a {\em Builder\/} instead of a {\em Slice}.
|
||||
\item {\tt udict!}, {\tt udict!+}, {\tt b>udict!}, {\tt b>udict!+}, variants of {\tt idict!}, {\tt idict!+}, {\tt b>idict!}, {\tt b>idict!+}, but with an unsigned $n$-bit integer $x$ used as a key.
|
||||
\item {\tt sdict!}, {\tt sdict!+}, {\tt b>sdict!}, {\tt b>sdict!+}, variants of {\tt idict!}, {\tt idict!+}, {\tt b>idict!}, {\tt b>idict!+}, but with the first $n$ data bits of {\em Slice\/}~$x$ used as a key.
|
||||
\item {\tt idict@} ($x$ $D$ $n$ -- $v$ $-1$ or $0$), looks up the key represented by signed big-endian $n$-bit {\em Integer\/}~$x$ in the dictionary represented by {\em Cell\/}~$D$. If the key is found, returns the corresponding value as a {\em Slice\/}~$v$ and $-1$. Otherwise returns $0$.
|
||||
\item {\tt udict@} ($x$ $D$ $n$ -- $v$ $-1$ or $0$), similar to {\tt idict@}, but with an {\em un}signed big-endian $n$-bit {\em Integer\/}~$x$ used as a key.
|
||||
\item {\tt sdict@} ($k$ $D$ $n$ -- $v$ $-1$ or $0$), similar to {\tt idict@}, but with the key provided in the first $n$ bits of {\em Slice\/}~$k$.
|
||||
\item {\tt dictmap} ($D$ $n$ $e$ -- $s'$), applies execution token $e$ (i.e., an anonymous function) to each of the key-value pairs stored in a dictionary $D$ with $n$-bit keys. The execution token is executed once for each key-value pair, with a {\em Builder\/} $b$ and a {\em Slice\/} $v$ (containing the value) pushed into the stack before executing $e$. After the execution $e$ must leave in the stack either a modified {\em Builder\/} $b'$ (containing all data from~$b$ along with the new value $v'$) and $-1$, or $0$ indicating failure. In the latter case, the corresponding key is omitted from the new dictionary.
|
||||
\item {\tt dictmerge} ($D$ $D'$ $n$ $e$ -- $D''$), combines two dictionaries $D$ and $D'$ with $n$-bit keys into one dictionary $D''$ with the same keys. If a key is present in only one of the dictionaries $D$ and $D'$, this key and the corresponding value are copied verbatim to the new dictionary $D''$. Otherwise the execution token (anonymous function) $e$ is invoked to merge the two values $v$ and $v'$ corresponding to the same key $k$ in $D$ and $D'$, respectively. Before $e$ is invoked, a {\em Builder\/}~$b$ and two {\em Slice}s $v$ and $v'$ representing the two values to be merged are pushed. After the execution $e$ leaves either a modified {\em Builder\/}~$b'$ (containing the original data from $b$ along with the combined value) and $-1$, or $0$ on failure. In the latter case, the corresponding key is omitted from the new dictionary.
|
||||
\end{itemize}
|
||||
|
@ -2001,10 +2003,12 @@ For example, the active prefix word {\tt B\{}, used for defining {\em Bytes\/} l
|
|||
\item {\tt b.} ($x$ -- ), prints the binary representation of an {\em Integer\/}~$x$, followed by a single space. Equivalent to {\tt b.\_ space}.
|
||||
\item {\tt b.\_} ($x$ -- ), prints the binary representation of an {\em Integer\/}~$x$ without any spaces. Equivalent to {\tt (b.)~type}.
|
||||
\item {\tt b>} ($b$ -- $c$), transforms a {\em Builder\/}~$b$ into a new {\em Cell\/}~$c$ containing the same data as~$b$, cf.~\ptref{p:builder.ops}.
|
||||
\item {\tt b>idict!} ($v$ $x$ $s$ $n$ -- $s'$ $-1$ or $s$ $0$), adds a new value $v$ (represented by a {\em Builder}) with key given by signed big-endian $n$-bit integer $x$ into dictionary $s$ with $n$-bit keys, and returns the new dictionary $s'$ and $-1$ on success, cf.~\ptref{p:hashmap.ops}. Otherwise the unchanged dictionary $s$ and $0$ are returned.
|
||||
\item {\tt b>idict!+} ($v$ $x$ $s$ $n$ -- $s'$ $-1$ or $s$ $0$), adds a new key-value pair $(x,v)$ into dictionary $s$ similarly to {\tt b>idict!}, but fails if the key already exists by returning the unchanged dictionary $s$ and $0$, cf.~\ptref{p:hashmap.ops}.
|
||||
\item {\tt b>udict!} ($v$ $x$ $s$ $n$ -- $s'$ $-1$ or $s$ $0$), adds a new value $v$ (represented by a {\em Builder}) with key given by unsigned big-endian $n$-bit integer $x$ into dictionary $s$ with $n$-bit keys, and returns the new dictionary $s'$ and $-1$ on success, cf.~\ptref{p:hashmap.ops}. Otherwise the unchanged dictionary $s$ and $0$ are returned.
|
||||
\item {\tt b>udict!+} ($v$ $x$ $s$ $n$ -- $s'$ $-1$ or $s$ $0$), adds a new key-value pair $(x,v)$ into dictionary $s$ similarly to {\tt b>udict!}, but fails if the key already exists by returning the unchanged dictionary $s$ and $0$, cf.~\ptref{p:hashmap.ops}.
|
||||
\item {\tt b>idict!} ($v$ $x$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new value $v$ (represented by a {\em Builder}) with key given by signed big-endian $n$-bit integer $x$ into dictionary $D$ with $n$-bit keys, and returns the new dictionary $D'$ and $-1$ on success, cf.~\ptref{p:hashmap.ops}. Otherwise the unchanged dictionary $D$ and $0$ are returned.
|
||||
\item {\tt b>idict!+} ($v$ $x$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new key-value pair $(x,v)$ into dictionary $D$ similarly to {\tt b>idict!}, but fails if the key already exists by returning the unchanged dictionary $D$ and $0$, cf.~\ptref{p:hashmap.ops}.
|
||||
\item {\tt b>sdict!} ($v$ $k$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new value $v$ (represented by a {\em Builder}) with key given by the first $n$ bits of {\em Slice\/}~$k$ into dictionary $D$ with $n$-bit keys, and returns the new dictionary $D'$ and $-1$ on success, cf.~\ptref{p:hashmap.ops}. Otherwise the unchanged dictionary $D$ and $0$ are returned.
|
||||
\item {\tt b>sdict!+} ($v$ $k$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new key-value pair $(k,v)$ into dictionary $D$ similarly to {\tt b>sdict!}, but fails if the key already exists by returning the unchanged dictionary $D$ and $0$, cf.~\ptref{p:hashmap.ops}.
|
||||
\item {\tt b>udict!} ($v$ $x$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new value $v$ (represented by a {\em Builder}) with key given by unsigned big-endian $n$-bit integer $x$ into dictionary $D$ with $n$-bit keys, and returns the new dictionary $D'$ and $-1$ on success, cf.~\ptref{p:hashmap.ops}. Otherwise the unchanged dictionary $D$ and $0$ are returned.
|
||||
\item {\tt b>udict!+} ($v$ $x$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new key-value pair $(x,v)$ into dictionary $D$ similarly to {\tt b>udict!}, but fails if the key already exists by returning the unchanged dictionary $D$ and $0$, cf.~\ptref{p:hashmap.ops}.
|
||||
\item {\tt bbitrefs} ($b$ -- $x$ $y$), returns both the number of data bits $x$ and the number of references $y$ already stored in {\em Builder\/}~$b$, cf.~\ptref{p:builder.ops}.
|
||||
\item {\tt bbits} ($b$ -- $x$), returns the number of data bits already stored in {\em Builder\/}~$b$. The result $x$ is an {\em Integer\/} in the range $0\dots1023$, cf.~\ptref{p:builder.ops}.
|
||||
\item {\tt bl} ( -- $x$), pushes the Unicode codepoint of a space, i.e., 32, cf.~\ptref{p:string.ops}.
|
||||
|
@ -2129,6 +2133,9 @@ Typical values of $x$ are $x=0$ or $x=2$ for very small bags of cells (e.g., TON
|
|||
\item {\tt s>c} ($s$ -- $c$), creates a {\em Cell}~$c$ directly from a {\em Slice}~$s$, cf.~\ptref{p:slice.ops}. Equivalent to {\tt <b swap s, b>}.
|
||||
\item {\tt sbitrefs} ($s$ -- $x$ $y$), returns both the number of data bits $x$ and the number of cell references $y$ remaining in {\em Slice}~$s$, cf.~\ptref{p:slice.ops}. Equivalent to {\tt remaining}.
|
||||
\item {\tt sbits} ($s$ -- $x$), returns the number of data bits $x$ remaining in {\em Slice}~$s$, cf.~\ptref{p:slice.ops}.
|
||||
\item {\tt sdict!} ($v$ $k$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new value $v$ (represented by a {\em Slice\/}) with key given by the first $n$ bits of {\em Slice\/}~$k$ into dictionary $D$ with $n$-bit keys, and returns the new dictionary $D'$ and $-1$ on success, cf.~\ptref{p:hashmap.ops}. Otherwise the unchanged dictionary $D$ and $0$ are returned.
|
||||
\item {\tt sdict!+} ($v$ $k$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new key-value pair $(k,v)$ into dictionary $D$ similarly to {\tt sdict!}, but fails if the key already exists by returning the unchanged dictionary $D$ and $0$, cf.~\ptref{p:hashmap.ops}.
|
||||
\item {\tt sdict@} ($k$ $D$ $n$ -- $v$ $-1$ or $0$), looks up the key given by the first $n$ data bits of {\em Slice\/}~$x$ in the dictionary represented by {\em Cell\/} or {\em Null\/}~$D$, cf.~\ptref{p:hashmap.ops}. If the key is found, returns the corresponding value as a {\em Slice\/}~$v$ and $-1$. Otherwise returns $0$.
|
||||
\item {\tt second} ($t$ -- $x$), returns the second component of a {\em Tuple}, cf.~\ptref{p:tuples}. Equivalent to {\tt 1 []}.
|
||||
\item {\tt sgn} ($x$ -- $y$), computes the sign of an {\em Integer\/} $x$ (i.e., pushes $1$ if $x>0$, $-1$ if $x<0$, and $0$ if $x=0$), cf.~\ptref{p:int.comp}. Equivalent to {\tt 0 cmp}.
|
||||
\item {\tt shash} ($s$ -- $B$), computes the $\Sha$-based representation hash of a {\em Slice\/} by first transforming it into a cell, cf.~\ptref{p:hash.ops}. Equivalent to {\tt s>c hashB}.
|
||||
|
|
|
@ -101,10 +101,10 @@ TEST(Actor2, locker) {
|
|||
kill_signal.add_signal(ActorSignals::Kill);
|
||||
|
||||
ActorSignals wakeup_signal;
|
||||
kill_signal.add_signal(ActorSignals::Wakeup);
|
||||
wakeup_signal.add_signal(ActorSignals::Wakeup);
|
||||
|
||||
ActorSignals cpu_signal;
|
||||
kill_signal.add_signal(ActorSignals::Cpu);
|
||||
cpu_signal.add_signal(ActorSignals::Cpu);
|
||||
|
||||
{
|
||||
ActorLocker lockerA(&state);
|
||||
|
|
|
@ -35,6 +35,7 @@ liteServer.blockState id:tonNode.blockIdExt root_hash:int256 file_hash:int256 da
|
|||
liteServer.blockHeader id:tonNode.blockIdExt mode:# header_proof:bytes = liteServer.BlockHeader;
|
||||
liteServer.sendMsgStatus status:int = liteServer.SendMsgStatus;
|
||||
liteServer.accountState id:tonNode.blockIdExt shardblk:tonNode.blockIdExt shard_proof:bytes proof:bytes state:bytes = liteServer.AccountState;
|
||||
liteServer.runMethodResult mode:# id:tonNode.blockIdExt shardblk:tonNode.blockIdExt shard_proof:bytes proof:bytes state_proof:bytes init_c7:bytes lib_extras:bytes exit_code:int result:bytes = liteServer.RunMethodResult;
|
||||
liteServer.shardInfo id:tonNode.blockIdExt shardblk:tonNode.blockIdExt shard_proof:bytes shard_descr:bytes = liteServer.ShardInfo;
|
||||
liteServer.allShardsInfo id:tonNode.blockIdExt proof:bytes data:bytes = liteServer.AllShardsInfo;
|
||||
liteServer.transactionInfo id:tonNode.blockIdExt proof:bytes transaction:bytes = liteServer.TransactionInfo;
|
||||
|
@ -63,6 +64,7 @@ liteServer.getState id:tonNode.blockIdExt = liteServer.BlockState;
|
|||
liteServer.getBlockHeader id:tonNode.blockIdExt mode:# = liteServer.BlockHeader;
|
||||
liteServer.sendMessage body:bytes = liteServer.SendMsgStatus;
|
||||
liteServer.getAccountState id:tonNode.blockIdExt account:liteServer.accountId = liteServer.AccountState;
|
||||
liteServer.runSmcMethod mode:# id:tonNode.blockIdExt account:liteServer.accountId method_id:int params:bytes = liteServer.RunMethodResult;
|
||||
liteServer.getShardInfo id:tonNode.blockIdExt workchain:int shard:long exact:Bool = liteServer.ShardInfo;
|
||||
liteServer.getAllShardsInfo id:tonNode.blockIdExt = liteServer.AllShardsInfo;
|
||||
liteServer.getOneTransaction id:tonNode.blockIdExt account:liteServer.accountId lt:long = liteServer.TransactionInfo;
|
||||
|
|
Binary file not shown.
|
@ -79,8 +79,8 @@ fees in_fwd_fee:int53 storage_fee:int53 gas_fee:int53 fwd_fee:int53= Fees;
|
|||
query.fees source_fees:fees destination_fees:fees = query.Fees;
|
||||
query.info id:int53 valid_until:int53 body_hash:bytes = query.Info;
|
||||
|
||||
tvm.slice bytes:string = tvm.Slice;
|
||||
tvm.cell bytes:string = tvm.Cell;
|
||||
tvm.slice bytes:bytes = tvm.Slice;
|
||||
tvm.cell bytes:bytes = tvm.Cell;
|
||||
tvm.numberDecimal number:string = tvm.Number;
|
||||
tvm.tuple elements:vector<tvm.StackEntry> = tvm.Tuple;
|
||||
tvm.list elements:vector<tvm.StackEntry> = tvm.List;
|
||||
|
|
Binary file not shown.
|
@ -85,6 +85,7 @@ class ValidatorEngineConsole : public td::actor::Actor {
|
|||
void add_cmd(td::BufferSlice data) {
|
||||
ex_mode_ = true;
|
||||
ex_queries_.push_back(std::move(data));
|
||||
set_readline_enabled(false);
|
||||
}
|
||||
void set_fail_timeout(td::Timestamp ts) {
|
||||
fail_timeout_ = ts;
|
||||
|
|
|
@ -129,7 +129,7 @@ void LiteQuery::start_up() {
|
|||
[&](lite_api::liteServer_getState& q) { this->perform_getState(ton::create_block_id(q.id_)); },
|
||||
[&](lite_api::liteServer_getAccountState& q) {
|
||||
this->perform_getAccountState(ton::create_block_id(q.id_), static_cast<WorkchainId>(q.account_->workchain_),
|
||||
q.account_->id_);
|
||||
q.account_->id_, 0);
|
||||
},
|
||||
[&](lite_api::liteServer_getOneTransaction& q) {
|
||||
this->perform_getOneTransaction(ton::create_block_id(q.id_),
|
||||
|
@ -172,6 +172,10 @@ void LiteQuery::start_up() {
|
|||
q.mode_ & 1 ? q.start_after_ : td::Bits256::zero(),
|
||||
q.mode_ & 4 ? q.modified_after_ : 0);
|
||||
},
|
||||
[&](lite_api::liteServer_runSmcMethod& q) {
|
||||
this->perform_runSmcMethod(ton::create_block_id(q.id_), static_cast<WorkchainId>(q.account_->workchain_),
|
||||
q.account_->id_, q.mode_, q.method_id_, std::move(q.params_));
|
||||
},
|
||||
[&](auto& obj) { this->abort_query(td::Status::Error(ErrorCode::protoviolation, "unknown query")); }));
|
||||
}
|
||||
|
||||
|
@ -604,9 +608,9 @@ bool LiteQuery::request_zero_state(BlockIdExt blkid) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void LiteQuery::perform_getAccountState(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr) {
|
||||
LOG(INFO) << "started a getAccountState(" << blkid.to_str() << ", " << workchain << ", " << addr.to_hex()
|
||||
<< ") liteserver query";
|
||||
void LiteQuery::perform_getAccountState(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr, int mode) {
|
||||
LOG(INFO) << "started a getAccountState(" << blkid.to_str() << ", " << workchain << ", " << addr.to_hex() << ", "
|
||||
<< mode << ") liteserver query";
|
||||
if (blkid.id.workchain != masterchainId && blkid.id.workchain != workchain) {
|
||||
fatal_error("reference block for a getAccountState() must belong to the masterchain");
|
||||
return;
|
||||
|
@ -626,6 +630,7 @@ void LiteQuery::perform_getAccountState(BlockIdExt blkid, WorkchainId workchain,
|
|||
}
|
||||
acc_workchain_ = workchain;
|
||||
acc_addr_ = addr;
|
||||
mode_ = mode;
|
||||
if (blkid.id.workchain != masterchainId) {
|
||||
base_blk_id_ = blkid;
|
||||
set_continuation([&]() -> void { finish_getAccountState({}); });
|
||||
|
@ -659,6 +664,45 @@ void LiteQuery::continue_getAccountState_0(Ref<ton::validator::MasterchainState>
|
|||
request_mc_block_data(blkid);
|
||||
}
|
||||
|
||||
void LiteQuery::perform_runSmcMethod(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr, int mode,
|
||||
int method_id, td::BufferSlice params) {
|
||||
LOG(INFO) << "started a runSmcMethod(" << blkid.to_str() << ", " << workchain << ", " << addr.to_hex() << ", "
|
||||
<< method_id << ", " << mode << ") liteserver query with " << params.size() << " parameter bytes";
|
||||
if (params.size() >= 65536) {
|
||||
fatal_error("more than 64k parameter bytes passed");
|
||||
return;
|
||||
}
|
||||
if (mode & ~0xf) {
|
||||
fatal_error("unsupported mode in runSmcMethod");
|
||||
return;
|
||||
}
|
||||
stack_.clear();
|
||||
try {
|
||||
if (params.size()) {
|
||||
auto res = vm::std_boc_deserialize(std::move(params));
|
||||
if (res.is_error()) {
|
||||
fatal_error("cannot deserialize parameter list boc: "s + res.move_as_error().to_string());
|
||||
return;
|
||||
}
|
||||
auto cs = vm::load_cell_slice(res.move_as_ok());
|
||||
if (!(vm::Stack::deserialize_to(cs, stack_, 0) && cs.empty_ext())) {
|
||||
fatal_error("parameter list boc cannot be deserialized as a VmStack");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
stack_ = td::make_ref<vm::Stack>();
|
||||
}
|
||||
stack_.write().push_smallint(method_id);
|
||||
} catch (vm::VmError& vme) {
|
||||
fatal_error("error deserializing parameter list: "s + vme.get_msg());
|
||||
return;
|
||||
} catch (vm::VmVirtError& vme) {
|
||||
fatal_error("virtualization error while deserializing parameter list: "s + vme.get_msg());
|
||||
return;
|
||||
}
|
||||
perform_getAccountState(blkid, workchain, addr, mode | 0x10000);
|
||||
}
|
||||
|
||||
void LiteQuery::perform_getOneTransaction(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr, LogicalTime lt) {
|
||||
LOG(INFO) << "started a getOneTransaction(" << blkid.to_str() << ", " << workchain << ", " << addr.to_hex() << ","
|
||||
<< lt << ") liteserver query";
|
||||
|
@ -934,10 +978,15 @@ void LiteQuery::finish_getAccountState(td::BufferSlice shard_proof) {
|
|||
acc_root = acc_csr->prefetch_ref();
|
||||
}
|
||||
auto proof = vm::std_boc_serialize_multi({std::move(proof1), pb.extract_proof()});
|
||||
pb.clear();
|
||||
if (proof.is_error()) {
|
||||
fatal_error(proof.move_as_error());
|
||||
return;
|
||||
}
|
||||
if (mode_ & 0x10000) {
|
||||
finish_runSmcMethod(std::move(shard_proof), proof.move_as_ok(), std::move(acc_root));
|
||||
return;
|
||||
}
|
||||
td::BufferSlice data;
|
||||
if (acc_root.not_null()) {
|
||||
auto res = vm::std_boc_serialize(std::move(acc_root));
|
||||
|
@ -954,6 +1003,13 @@ void LiteQuery::finish_getAccountState(td::BufferSlice shard_proof) {
|
|||
finish_query(std::move(b));
|
||||
}
|
||||
|
||||
void LiteQuery::finish_runSmcMethod(td::BufferSlice shard_proof, td::BufferSlice state_proof, Ref<vm::Cell> acc_root) {
|
||||
LOG(INFO) << "completing runSmcMethod() query";
|
||||
// ... TODO ...
|
||||
fatal_error("runSmcMethod not implemented");
|
||||
return;
|
||||
}
|
||||
|
||||
void LiteQuery::continue_getOneTransaction() {
|
||||
LOG(INFO) << "completing getOneTransaction() query";
|
||||
CHECK(block_.not_null());
|
||||
|
|
|
@ -37,6 +37,7 @@ class LiteQuery : public td::actor::Actor {
|
|||
td::Timestamp timeout_;
|
||||
td::Promise<td::BufferSlice> promise_;
|
||||
int pending_{0};
|
||||
int mode_{0};
|
||||
WorkchainId acc_workchain_;
|
||||
StdSmcAddress acc_addr_;
|
||||
LogicalTime trans_lt_;
|
||||
|
@ -55,6 +56,7 @@ class LiteQuery : public td::actor::Actor {
|
|||
std::vector<Ref<td::CntObject>> aux_objs_;
|
||||
std::vector<ton::BlockIdExt> blk_ids_;
|
||||
std::unique_ptr<block::BlockProofChain> chain_;
|
||||
Ref<vm::Stack> stack_;
|
||||
|
||||
public:
|
||||
enum {
|
||||
|
@ -91,10 +93,13 @@ class LiteQuery : public td::actor::Actor {
|
|||
void continue_getState(BlockIdExt blkid, Ref<ShardState> state);
|
||||
void continue_getZeroState(BlockIdExt blkid, td::BufferSlice state);
|
||||
void perform_sendMessage(td::BufferSlice ext_msg);
|
||||
void perform_getAccountState(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr);
|
||||
void perform_getAccountState(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr, int mode);
|
||||
void continue_getAccountState_0(Ref<MasterchainState> mc_state, BlockIdExt blkid);
|
||||
void continue_getAccountState();
|
||||
void finish_getAccountState(td::BufferSlice shard_proof);
|
||||
void perform_runSmcMethod(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr, int mode, int method_id,
|
||||
td::BufferSlice params);
|
||||
void finish_runSmcMethod(td::BufferSlice shard_proof, td::BufferSlice state_proof, Ref<vm::Cell> acc_root);
|
||||
void perform_getOneTransaction(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr, LogicalTime lt);
|
||||
void continue_getOneTransaction();
|
||||
void perform_getTransactions(WorkchainId workchain, StdSmcAddress addr, LogicalTime lt, Bits256 hash, unsigned count);
|
||||
|
|
Loading…
Reference in a new issue