/* This file is part of TON Blockchain Library. TON Blockchain Library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. TON Blockchain Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . Copyright 2017-2019 Telegram Systems LLP */ #pragma once #include #include #include #include #include #include #include #include "common/refcnt.hpp" #include "common/bigint.hpp" #include "common/refint.h" #include "common/bitstring.h" #include "vm/cells.h" #include "vm/cellslice.h" #include "vm/excno.hpp" namespace td { extern template class td::Cnt; extern template class td::Ref>; } // namespace td namespace vm { using td::Cnt; using td::Ref; using td::RefAny; const char* get_exception_msg(Excno exc_no); std::string str_to_hex(std::string data, std::string prefix = ""); class StackEntry; class Stack; class Continuation; class Box; class Atom; using Tuple = td::Cnt>; struct from_object_t {}; constexpr from_object_t from_object{}; class StackEntry { public: enum Type { t_null, t_int, t_cell, t_builder, t_slice, t_vmcont, t_tuple, t_stack, t_string, t_bytes, t_bitstring, t_box, t_atom, t_object }; private: RefAny ref; Type tp; public: StackEntry() : ref(), tp(t_null) { } ~StackEntry() { } StackEntry(Ref cell_ref) : ref(std::move(cell_ref)), tp(t_cell) { } StackEntry(Ref cb_ref) : ref(std::move(cb_ref)), tp(t_builder) { } StackEntry(Ref cs_ref) : ref(std::move(cs_ref)), tp(t_slice) { } StackEntry(td::RefInt256 int_ref) : ref(std::move(int_ref)), tp(t_int) { } StackEntry(std::string str, bool bytes = false) : ref(), tp(bytes ? t_bytes : t_string) { ref = Ref>{true, std::move(str)}; } StackEntry(Ref stack_ref); StackEntry(Ref cont_ref); StackEntry(Ref box_ref); StackEntry(Ref tuple_ref); StackEntry(const std::vector& tuple_components); StackEntry(std::vector&& tuple_components); StackEntry(Ref atom_ref); StackEntry(const StackEntry& se) : ref(se.ref), tp(se.tp) { } StackEntry(StackEntry&& se) noexcept : ref(std::move(se.ref)), tp(se.tp) { se.tp = t_null; } template StackEntry(from_object_t, Ref obj_ref) : ref(std::move(obj_ref)), tp(t_object) { } StackEntry& operator=(const StackEntry& se) { ref = se.ref; tp = se.tp; return *this; } StackEntry& operator=(StackEntry&& se) { ref = std::move(se.ref); tp = se.tp; se.tp = t_null; return *this; } StackEntry& clear() { ref.clear(); tp = t_null; return *this; } bool empty() const { return tp == t_null; } bool is_tuple() const { return tp == t_tuple; } bool is_atom() const { return tp == t_atom; } bool is(int wanted) const { return tp == wanted; } void swap(StackEntry& se) { ref.swap(se.ref); std::swap(tp, se.tp); } bool operator==(const StackEntry& other) const { return tp == other.tp && ref == other.ref; } bool operator!=(const StackEntry& other) const { return !(tp == other.tp && ref == other.ref); } Type type() const { return tp; } private: template Ref dynamic_as() const & { return tp == tag ? static_cast>(ref) : td::Ref{}; } template Ref dynamic_as() && { return tp == tag ? static_cast>(std::move(ref)) : td::Ref{}; } template Ref dynamic_move_as() & { return tp == tag ? static_cast>(std::move(ref)) : td::Ref{}; } template Ref as() const & { return tp == tag ? Ref{td::static_cast_ref(), ref} : td::Ref{}; } template Ref as() && { return tp == tag ? Ref{td::static_cast_ref(), std::move(ref)} : td::Ref{}; } template Ref move_as() & { return tp == tag ? Ref{td::static_cast_ref(), std::move(ref)} : td::Ref{}; } public: template static StackEntry maybe(Ref ref) { if (ref.is_null()) { return {}; } else { return ref; } } td::RefInt256 as_int() const & { return as(); } td::RefInt256 as_int() && { return move_as(); } Ref as_cell() const & { return as(); } Ref as_cell() && { return move_as(); } Ref as_builder() const & { return as(); } Ref as_builder() && { return move_as(); } Ref as_slice() const & { return as(); } Ref as_slice() && { return move_as(); } Ref as_cont() const &; Ref as_cont() &&; Ref> as_string_ref() const { return as, t_string>(); } Ref> as_bytes_ref() const { return as, t_bytes>(); } std::string as_string() const { //assert(!as_string_ref().is_null()); return tp == t_string ? *as_string_ref() : ""; } std::string as_bytes() const { return tp == t_bytes ? *as_bytes_ref() : ""; } Ref as_box() const &; Ref as_box() &&; Ref as_tuple() const &; Ref as_tuple() &&; Ref as_tuple_range(unsigned max_len = 255, unsigned min_len = 0) const &; Ref as_tuple_range(unsigned max_len = 255, unsigned min_len = 0) &&; Ref as_atom() const &; Ref as_atom() &&; template Ref as_object() const & { return dynamic_as(); } template Ref as_object() && { return dynamic_move_as(); } void dump(std::ostream& os) const; void print_list(std::ostream& os) const; void print_list_tail(std::ostream& os) const; std::string to_string() const; }; inline void swap(StackEntry& se1, StackEntry& se2) { se1.swap(se2); } template Ref make_tuple_ref(Args&&... args) { return td::make_cnt_ref>(std::vector{std::forward(args)...}); } const StackEntry& tuple_index(const Tuple& tup, unsigned idx); StackEntry tuple_extend_index(const Ref& tup, unsigned idx); unsigned tuple_extend_set_index(Ref& tup, unsigned idx, StackEntry&& value, bool force = false); class Stack : public td::CntObject { std::vector stack; public: Stack() { } ~Stack() override = default; Stack(const std::vector& _stack) : stack(_stack) { } Stack(std::vector&& _stack) : stack(std::move(_stack)) { } Stack(const Stack& old_stack, unsigned copy_elem, unsigned skip_top); Stack(Stack&& old_stack, unsigned copy_elem, unsigned skip_top); td::CntObject* make_copy() const override { std::cerr << "copy stack at " << (const void*)this << " (" << depth() << " entries)\n"; return new Stack{stack}; } void push_from_stack(const Stack& old_stack, unsigned copy_elem, unsigned skip_top = 0); void push_from_stack(Stack&& old_stack, unsigned copy_elem, unsigned skip_top = 0); void move_from_stack(Stack& old_stack, unsigned copy_elem); Ref split_top(unsigned top_cnt, unsigned drop_cnt = 0); StackEntry& push() { stack.emplace_back(); return stack.back(); } template StackEntry& push(Args&&... args) { stack.emplace_back(args...); return stack.back(); } StackEntry& push(const StackEntry& se) { stack.push_back(se); return stack.back(); } StackEntry& push(StackEntry&& se) { stack.emplace_back(std::move(se)); return stack.back(); } void pop(StackEntry& se) { stack.back().swap(se); stack.pop_back(); } StackEntry pop() { StackEntry res = std::move(stack.back()); stack.pop_back(); return res; } StackEntry pop_chk() { check_underflow(1); return pop(); } void pop_many(int count) { stack.resize(stack.size() - count); } void drop_bottom(int count) { std::move(stack.cbegin() + count, stack.cend(), stack.begin()); pop_many(count); } StackEntry& operator[](int idx) { // NB: we sometimes use idx=-1 return stack[stack.size() - idx - 1]; } const StackEntry& operator[](int idx) const { return stack[stack.size() - idx - 1]; } StackEntry& at(int idx) { return stack.at(stack.size() - idx - 1); } const StackEntry& at(int idx) const { return stack.at(stack.size() - idx - 1); } StackEntry fetch(int idx) const { return stack[stack.size() - idx - 1]; } StackEntry& tos() { return stack.back(); } const StackEntry& tos() const { return stack.back(); } bool is_empty() const { return stack.empty(); } int depth() const { return (int)stack.size(); } std::vector::iterator top() { return stack.end(); } std::vector::const_iterator top() const { return stack.cend(); } std::vector::iterator from_top(int offs) { return stack.end() - offs; } std::vector::const_iterator from_top(int offs) const { return stack.cend() - offs; } bool at_least(int req) const { return depth() >= req; } template bool at_least(int req, Args... args) const { return at_least(req) && at_least(args...); } bool more_than(int req) const { return depth() > req; } template bool more_than(int req, Args... args) const { return more_than(req) && more_than(args...); } void clear() { stack.clear(); } Stack& set_contents(const Stack& other_stack) { stack = other_stack.stack; return *this; } Stack& set_contents(Stack&& other_stack) { stack = std::move(other_stack.stack); return *this; } Stack& set_contents(Ref ref) { if (ref.is_null()) { clear(); } else if (ref->is_unique()) { set_contents(std::move(ref.unique_write())); } else { set_contents(*ref); } return *this; } template const Stack& check_underflow(Args... args) const { if (!at_least(args...)) { throw VmError{Excno::stk_und}; } return *this; } template Stack& check_underflow(Args... args) { if (!at_least(args...)) { throw VmError{Excno::stk_und}; } return *this; } template const Stack& check_underflow_p(Args... args) const { if (!more_than(args...)) { throw VmError{Excno::stk_und}; } return *this; } template Stack& check_underflow_p(Args... args) { if (!more_than(args...)) { throw VmError{Excno::stk_und}; } return *this; } Stack& reserve(int cnt) { stack.reserve(cnt); return *this; } void pop_null(); td::RefInt256 pop_int(); td::RefInt256 pop_int_finite(); bool pop_bool(); long long pop_long(); long long pop_long_range(long long max, long long min = 0); int pop_smallint_range(int max, int min = 0); Ref pop_cell(); Ref pop_maybe_cell(); Ref pop_builder(); Ref pop_cellslice(); Ref pop_cont(); Ref pop_box(); Ref pop_tuple(); Ref pop_tuple_range(unsigned max_len = 255, unsigned min_len = 0); Ref pop_maybe_tuple(); Ref pop_maybe_tuple_range(unsigned max_len = 255); Ref pop_atom(); std::string pop_string(); std::string pop_bytes(); void push_null(); void push_int(td::RefInt256 val); void push_int_quiet(td::RefInt256 val, bool quiet = true); void push_smallint(long long val); void push_bool(bool val); void push_string(std::string str); void push_string(td::Slice slice); void push_bytes(std::string str); void push_bytes(td::Slice slice); void push_cell(Ref cell); void push_maybe_cell(Ref cell); void push_maybe_cellslice(Ref cs); void push_builder(Ref cb); void push_cellslice(Ref cs); void push_cont(Ref cont); void push_box(Ref box); void push_tuple(Ref tuple); void push_tuple(const std::vector& components); void push_tuple(std::vector&& components); void push_maybe_tuple(Ref tuple); void push_atom(Ref atom); template void push_object(Ref obj) { push({vm::from_object, std::move(obj)}); } template void push_make_object(Args&&... args) { push_object(td::make_ref(std::forward(args)...)); } template void push_maybe(Ref val) { if (val.is_null()) { push({}); } else { push(std::move(val)); } } void dump(std::ostream& os, bool cr = true) const; }; } // namespace vm namespace td { extern template class td::Cnt>; extern template class td::Ref>>; } // namespace td