/* 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-2020 Telegram Systems LLP */ #pragma once #include "td/utils/Status.h" namespace vm { enum class Excno : int { none = 0, alt = 1, stk_und = 2, stk_ov = 3, int_ov = 4, range_chk = 5, inv_opcode = 6, type_chk = 7, cell_ov = 8, cell_und = 9, dict_err = 10, unknown = 11, fatal = 12, out_of_gas = 13, virt_err = 14, total }; const char* get_exception_msg(Excno exc_no); class VmError { Excno exc_no; bool msg_alloc = false; const char* msg; long long arg; public: VmError(Excno _excno, const char* _msg) : exc_no(_excno), msg(_msg), arg(0) { } VmError(Excno _excno) : exc_no(_excno), msg(0), arg(0) { } VmError(Excno _excno, const char* _msg, long long _arg) : exc_no(_excno), msg(_msg), arg(_arg) { } VmError(Excno _excno, std::string _msg, long long _arg = 0) : exc_no(_excno), msg_alloc(true), arg(_arg) { msg_alloc = true; char* p = (char*)malloc(_msg.size() + 1); memcpy(p, _msg.data(), _msg.size()); p[_msg.size()] = 0; msg = p; } ~VmError() { if (msg_alloc) { free(const_cast(msg)); } } int get_errno() const { return static_cast(exc_no); } const char* get_msg() const { return msg ? msg : get_exception_msg(exc_no); } long long get_arg() const { return arg; } td::Status as_status() const { return td::Status::Error(td::Slice{get_msg()}); } template td::Status as_status(T pfx) const { return td::Status::Error(PSLICE() << pfx << get_msg()); } }; struct VmNoGas { VmNoGas() = default; int get_errno() const { return static_cast(Excno::out_of_gas); } const char* get_msg() const { return "out of gas"; } operator VmError() const { return VmError{Excno::out_of_gas, "out of gas"}; } td::Status as_status() const { return td::Status::Error(td::Slice{get_msg()}); } template td::Status as_status(T pfx) const { return td::Status::Error(PSLICE() << pfx << get_msg()); } }; struct VmVirtError { int virtualization{0}; VmVirtError() = default; VmVirtError(int virtualization) : virtualization(virtualization) { } int get_errno() const { return static_cast(Excno::virt_err); } const char* get_msg() const { return "prunned branch"; } operator VmError() const { return VmError{Excno::virt_err, "prunned branch", virtualization}; } td::Status as_status() const { return td::Status::Error(td::Slice{get_msg()}); } template td::Status as_status(T pfx) const { return td::Status::Error(PSLICE() << pfx << get_msg()); } }; struct VmFatal {}; template auto try_f(F&& f) noexcept -> decltype(f()) { try { return f(); } catch (vm::VmError& error) { return error.as_status("Got a vm exception: "); } catch (vm::VmVirtError& error) { return error.as_status("Got a vm virtualization exception: "); } catch (vm::VmNoGas& error) { return error.as_status("Got a vm no gas exception: "); } } #define TRY_VM(f) ::vm::try_f([&] { return f; }) } // namespace vm