#include <eosio/abi.hpp>
#include "abieos.hpp"
using namespace eosio;
namespace {
template <int i>
bool ends_with(const std::string& s, const char (&suffix)[i]) {
return s.size() >= i - 1 && !strcmp(s.c_str() + s.size() - (i - 1), suffix);
}
template <typename T>
struct abi_serializer_impl : abi_serializer {
void json_to_bin(::abieos::jvalue_to_bin_state& state, bool allow_extensions, const abi_type* type,
bool start) const override {
return ::abieos::json_to_bin((T*)nullptr, state, allow_extensions, type, start);
}
void json_to_bin(::abieos::json_to_bin_state& state, bool allow_extensions, const abi_type* type,
bool start) const override {
return ::abieos::json_to_bin((T*)nullptr, state, allow_extensions, type, start);
}
void bin_to_json(::abieos::bin_to_json_state& state, bool allow_extensions, const abi_type* type,
bool start) const override {
return ::abieos::bin_to_json((T*)nullptr, state, allow_extensions, type, start);
}
};
template <typename T>
constexpr auto abi_serializer_for = abi_serializer_impl<T>{};
abi_type::alias resolve(std::map<std::string, abi_type>& abi_types, const abi_type::alias_def* type, int depth);
template<typename... T, typename... A>
bool holds_any_alternative(const std::variant<A...>& v) {
return (... || std::holds_alternative<T>(v));
}
template <typename F>
constexpr void for_each_abi_type(F f) {
static_assert(sizeof(float) == 4);
static_assert(sizeof(double) == 8);
using namespace ::abieos;
f((bool*)nullptr);
f((int8_t*)nullptr);
f((uint8_t*)nullptr);
f((int16_t*)nullptr);
f((uint16_t*)nullptr);
f((int32_t*)nullptr);
f((uint32_t*)nullptr);
f((int64_t*)nullptr);
f((uint64_t*)nullptr);
f((int128*)nullptr);
f((uint128*)nullptr);
f((varuint32*)nullptr);
f((varint32*)nullptr);
f((float*)nullptr);
f((double*)nullptr);
f((float128*)nullptr);
f((time_point*)nullptr);
f((time_point_sec*)nullptr);
f((block_timestamp*)nullptr);
f((name*)nullptr);
f((bytes*)nullptr);
f((std::string*)nullptr);
f((checksum160*)nullptr);
f((checksum256*)nullptr);
f((checksum512*)nullptr);
f((public_key*)nullptr);
f((private_key*)nullptr);
f((signature*)nullptr);
f((symbol*)nullptr);
f((symbol_code*)nullptr);
f((asset*)nullptr);
}
abi_type* get_type(std::map<std::string, abi_type>& abi_types,
const std::string& name, int depth) {
eosio::check(depth < 32,
eosio::convert_abi_error(abi_error::recursion_limit_reached));
auto it = abi_types.find(name);
if (it == abi_types.end()) {
if (ends_with(name, "?")) {
auto base = get_type(abi_types, name.substr(0, name.size() - 1), depth + 1);
eosio::check(!holds_any_alternative<abi_type::optional, abi_type::array, abi_type::extension>(base->_data),
eosio::convert_abi_error(abi_error::invalid_nesting));
auto [iter, success] = abi_types.try_emplace(name, name, abi_type::optional{base}, &abi_serializer_for< ::abieos::pseudo_optional>);
return &iter->second;
} else if (ends_with(name, "[]")) {
auto element = get_type(abi_types, name.substr(0, name.size() - 2), depth + 1);
eosio::check(!holds_any_alternative<abi_type::optional, abi_type::array, abi_type::extension>(element->_data),
eosio::convert_abi_error(abi_error::invalid_nesting));
auto [iter, success] = abi_types.try_emplace(name, name, abi_type::array{element}, &abi_serializer_for< ::abieos::pseudo_array>);
return &iter->second;
} else if (ends_with(name, "$")) {
auto base = get_type(abi_types, name.substr(0, name.size() - 1), depth + 1);
eosio::check(!std::holds_alternative<abi_type::extension>(base->_data),
eosio::convert_abi_error(abi_error::invalid_nesting));
auto [iter, success] = abi_types.try_emplace(name, name, abi_type::extension{base}, &abi_serializer_for< ::abieos::pseudo_extension>);
return &iter->second;
} else
eosio::check(false, eosio::convert_abi_error(abi_error::unknown_type));
}
if (auto* alias = std::get_if<abi_type::alias>(&it->second._data)) {
return alias->type;
} else if(auto* alias = std::get_if<const abi_type::alias_def*>(&it->second._data)) {
auto base = resolve(abi_types, *alias, depth);
it->second._data = base;
return base.type;
}
return &it->second;
}
abi_type::struct_ resolve(std::map<std::string, abi_type>& abi_types, const struct_def* type, int depth) {
eosio::check(depth < 32,
eosio::convert_abi_error(abi_error::recursion_limit_reached));
abi_type::struct_ result;
if (!type->base.empty()) {
auto base = get_type(abi_types, type->base, depth + 1);
if(auto* base_def = std::get_if<const struct_def*>(&base->_data)) {
auto b = resolve(abi_types, *base_def, depth + 1);
base->_data = std::move(b);
}
if(auto* b = std::get_if<abi_type::struct_>(&base->_data)) {
result.fields = b->fields;
} else {
eosio::check(false, eosio::convert_abi_error(abi_error::base_not_a_struct));
}
}
for (auto& field : type->fields) {
auto t = get_type(abi_types, field.type, depth + 1);
result.fields.push_back(abi_field{field.name, t});
}
return result;
}
abi_type::variant resolve(std::map<std::string, abi_type>& abi_types, const variant_def* type, int depth) {
eosio::check(depth < 32,
eosio::convert_abi_error(abi_error::recursion_limit_reached));
abi_type::variant result;
for (const std::string& field : type->types) {
auto t = get_type(abi_types, field, depth + 1);
result.push_back({field, t});
}
return result;
}
abi_type::alias resolve(std::map<std::string, abi_type>& abi_types, const abi_type::alias_def* type, int depth) {
auto t = get_type(abi_types, *type, depth + 1);
eosio::check(!std::holds_alternative<abi_type::extension>(t->_data),
eosio::convert_abi_error(abi_error::extension_typedef));
return abi_type::alias{t};
}
struct fill_t {
std::map<std::string, abi_type>& abi_types;
abi_type& type;
int depth;
template<typename T>
auto operator()(T& t) -> std::void_t<decltype(resolve(abi_types, t, depth))> {
auto x = resolve(abi_types, t, depth);
type._data = std::move(x);
}
template<typename T>
auto operator()(const T& t) {
}
};
void fill(std::map<std::string, abi_type>& abi_types, abi_type& type, int depth) {
return std::visit(fill_t{abi_types, type, depth}, type._data);
}
}
const abi_type* eosio::abi::get_type(const std::string& name) {
return ::get_type(abi_types, name, 0);
}
void eosio::convert(const abi_def& abi, eosio::abi& c) {
for (auto& a : abi.actions)
c.action_types[a.name] = a.type;
for (auto& t : abi.tables)
c.table_types[t.name] = t.type;
for (auto& r : abi.action_results.value)
c.action_result_types[r.name] = r.result_type;
for_each_abi_type([&](auto* p) {
const char* name = get_type_name(p);
c.abi_types.try_emplace(name, name, abi_type::builtin{}, &abi_serializer_for<std::decay_t<decltype(*p)>>);
});
{
c.abi_types.try_emplace("extended_asset", "extended_asset",
abi_type::struct_{nullptr, {{"quantity", &c.abi_types.find("asset")->second},
{"contract", &c.abi_types.find("name")->second}}},
&abi_serializer_for<::abieos::pseudo_object>);
}
for (auto& t : abi.types) {
eosio::check(!t.new_type_name.empty(),
eosio::convert_abi_error(abi_error::missing_name));
auto [_, inserted] = c.abi_types.try_emplace(t.new_type_name, t.new_type_name, &t.type, nullptr);
eosio::check(inserted,
eosio::convert_abi_error(abi_error::redefined_type));
}
for (auto& s : abi.structs) {
eosio::check(!s.name.empty(),
eosio::convert_abi_error(abi_error::missing_name));
auto [it, inserted] = c.abi_types.try_emplace(s.name, s.name, &s, &abi_serializer_for<::abieos::pseudo_object>);
eosio::check(inserted,
eosio::convert_abi_error(abi_error::redefined_type));
}
for (auto& v : abi.variants.value) {
eosio::check(!v.name.empty(),
eosio::convert_abi_error(abi_error::missing_name));
auto [it, inserted] = c.abi_types.try_emplace(v.name, v.name, &v, &abi_serializer_for<::abieos::pseudo_variant>);
eosio::check(inserted,
eosio::convert_abi_error(abi_error::redefined_type));
}
for (auto& [_, t] : c.abi_types) {
fill(c.abi_types, t, 0);
}
for (const auto& [key, val] : abi.kv_tables.value) {
std::vector<char> bytes;
eosio::vector_stream strm(bytes);
to_json(val, strm);
c.kv_tables.try_emplace(key, bytes.begin(), bytes.end());
}
}
void to_abi_def(abi_def& def, const std::string& name, const abi_type::builtin&) {}
void to_abi_def(abi_def& def, const std::string& name, const abi_type::optional&) {}
void to_abi_def(abi_def& def, const std::string& name, const abi_type::array&) {}
void to_abi_def(abi_def& def, const std::string& name, const abi_type::extension&) {}
template<typename T>
void to_abi_def(abi_def& def, const std::string& name, const T*) {
eosio::check(false, eosio::convert_abi_error(eosio::abi_error::bad_abi));
}
void to_abi_def(abi_def& def, const std::string& name, const abi_type::alias& alias) {
def.types.push_back({name, alias.type->name});
}
void to_abi_def(abi_def& def, const std::string& name, const abi_type::struct_& struct_) {
if(name == "extended_asset") return;
std::size_t field_offset = 0;
std::string base;
std::vector<field_def> fields;
if(struct_.base) {
field_offset = struct_.base->as_struct()->fields.size();
base = struct_.base->name;
}
for(std::size_t i = field_offset; i < struct_.fields.size(); ++i) {
const abi_field& field = struct_.fields[i];
fields.push_back({field.name, field.type->name});
}
def.structs.push_back({name, std::move(base), std::move(fields)});
}
void to_abi_def(abi_def& def, const std::string& name, const abi_type::variant& variant) {
std::vector<std::string> types;
for(const auto& [name, type] : variant) {
types.push_back(type->name);
}
def.variants.value.push_back({name, std::move(types)});
}
void eosio::convert(const eosio::abi& abi, eosio::abi_def& def) {
def.version = "eosio::abi/1.0";
for(auto& [name, type] : abi.abi_types) {
std::visit([&name = type.name, &def](const auto& t){ return to_abi_def(def, name, t); }, type._data);
}
}
const abi_serializer* const eosio::object_abi_serializer = &abi_serializer_for< ::abieos::pseudo_object>;
const abi_serializer* const eosio::variant_abi_serializer = &abi_serializer_for< ::abieos::pseudo_variant>;
const abi_serializer* const eosio::array_abi_serializer = &abi_serializer_for< ::abieos::pseudo_array>;
const abi_serializer* const eosio::extension_abi_serializer = &abi_serializer_for< ::abieos::pseudo_extension>;
const abi_serializer* const eosio::optional_abi_serializer = &abi_serializer_for< ::abieos::pseudo_optional>;
std::vector<char> eosio::abi_type::json_to_bin_reorderable(std::string_view json, std::function<void()> f) const {
abieos::jvalue tmp;
abieos::json_to_jvalue(tmp, json, f);
std::vector<char> result;
abieos::json_to_bin(result, this, tmp, f);
return result;
}
std::vector<char> eosio::abi_type::json_to_bin(std::string_view json, std::function<void()> f) const {
std::vector<char> result;
abieos::json_to_bin(result, this, json, f);
return result;
}
std::string eosio::abi_type::bin_to_json(input_stream& bin, std::function<void()> f) const {
std::string result;
abieos::bin_to_json(bin, this, result, f);
return result;
}