48a93aadb9
Fixes #1708
560 lines
18 KiB
C++
560 lines
18 KiB
C++
//
|
|
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
|
|
//
|
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
//
|
|
#pragma once
|
|
|
|
#include "td/tl/tl_writer.h"
|
|
|
|
#include <cassert>
|
|
#include <cstdint>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace td {
|
|
namespace tl {
|
|
|
|
class TlWriterDotNet final : public TL_writer {
|
|
public:
|
|
bool is_header_;
|
|
std::string prefix_;
|
|
TlWriterDotNet(const std::string &name, bool is_header, const std::string &prefix = "")
|
|
: TL_writer(name), is_header_(is_header), prefix_(prefix) {
|
|
}
|
|
int get_max_arity(void) const final {
|
|
return 0;
|
|
}
|
|
|
|
bool is_built_in_simple_type(const std::string &name) const final {
|
|
return name == "Bool" || name == "Int32" || name == "Int53" || name == "Int64" || name == "Double" ||
|
|
name == "String" || name == "Bytes";
|
|
}
|
|
bool is_built_in_complex_type(const std::string &name) const final {
|
|
return name == "Vector";
|
|
}
|
|
bool is_type_bare(const tl_type *t) const final {
|
|
return t->simple_constructors <= 1 || (is_built_in_simple_type(t->name) && t->name != "Bool") ||
|
|
is_built_in_complex_type(t->name);
|
|
}
|
|
|
|
std::vector<std::string> get_parsers(void) const final {
|
|
return {"FromUnmanaged"};
|
|
}
|
|
int get_parser_type(const tl_combinator *t, const std::string &name) const final {
|
|
return 0;
|
|
}
|
|
Mode get_parser_mode(int type) const final {
|
|
return All; // Server;
|
|
}
|
|
std::vector<std::string> get_storers(void) const final {
|
|
return {"ToUnmanaged", "ToString"};
|
|
}
|
|
std::vector<std::string> get_additional_functions(void) const final {
|
|
return {"ToUnmanaged", "FromUnmanaged"};
|
|
}
|
|
int get_storer_type(const tl_combinator *t, const std::string &name) const final {
|
|
return name == "ToString";
|
|
}
|
|
Mode get_storer_mode(int type) const final {
|
|
return type <= 1 ? All : Server;
|
|
}
|
|
|
|
std::string gen_base_tl_class_name(void) const final {
|
|
return "BaseObject";
|
|
}
|
|
std::string gen_base_type_class_name(int arity) const final {
|
|
assert(arity == 0);
|
|
return "Object";
|
|
}
|
|
std::string gen_base_function_class_name(void) const final {
|
|
return "Function";
|
|
}
|
|
|
|
static std::string to_camelCase(const std::string &name) {
|
|
return to_cCamelCase(name, false);
|
|
}
|
|
static std::string to_CamelCase(const std::string &name) {
|
|
return to_cCamelCase(name, true);
|
|
}
|
|
static std::string to_cCamelCase(const std::string &name, bool flag) {
|
|
bool next_to_upper = flag;
|
|
std::string result;
|
|
for (std::size_t i = 0; i < name.size(); i++) {
|
|
if (!is_alnum(name[i])) {
|
|
next_to_upper = true;
|
|
continue;
|
|
}
|
|
if (next_to_upper) {
|
|
result += to_upper(name[i]);
|
|
next_to_upper = false;
|
|
} else {
|
|
result += name[i];
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::string gen_native_field_name(std::string name) const {
|
|
for (std::size_t i = 0; i < name.size(); i++) {
|
|
if (!is_alnum(name[i])) {
|
|
name[i] = '_';
|
|
}
|
|
}
|
|
assert(name.size() > 0);
|
|
assert(name[name.size() - 1] != '_');
|
|
return name + "_";
|
|
}
|
|
|
|
std::string gen_native_class_name(std::string name) const {
|
|
if (name == "Object") {
|
|
assert(0);
|
|
}
|
|
if (name == "#") {
|
|
return "int32_t";
|
|
}
|
|
for (std::size_t i = 0; i < name.size(); i++) {
|
|
if (!is_alnum(name[i])) {
|
|
name[i] = '_';
|
|
}
|
|
}
|
|
return name;
|
|
}
|
|
|
|
std::string gen_class_name(std::string name) const final {
|
|
if (name == "Object" || name == "#") {
|
|
assert(0);
|
|
}
|
|
return to_CamelCase(name);
|
|
}
|
|
std::string gen_field_name(std::string name) const final {
|
|
assert(name.size() > 0);
|
|
assert(is_alnum(name.back()));
|
|
return to_CamelCase(name);
|
|
}
|
|
|
|
std::string gen_type_name(const tl_tree_type *tree_type) const final {
|
|
const tl_type *t = tree_type->type;
|
|
const std::string &name = t->name;
|
|
|
|
if (name == "#") {
|
|
assert(0);
|
|
}
|
|
if (name == "Bool") {
|
|
return "bool";
|
|
}
|
|
if (name == "Int32") {
|
|
return "int32";
|
|
}
|
|
if (name == "Int53" || name == "Int64") {
|
|
return "int64";
|
|
}
|
|
if (name == "Double") {
|
|
return "float64";
|
|
}
|
|
if (name == "String") {
|
|
return "String^";
|
|
}
|
|
if (name == "Bytes") {
|
|
return "Array<BYTE>^";
|
|
}
|
|
|
|
if (name == "Vector") {
|
|
assert(t->arity == 1);
|
|
assert(tree_type->children.size() == 1);
|
|
assert(tree_type->children[0]->get_type() == NODE_TYPE_TYPE);
|
|
const tl_tree_type *child = static_cast<const tl_tree_type *>(tree_type->children[0]);
|
|
|
|
return "Array<" + gen_type_name(child) + ">^";
|
|
}
|
|
|
|
assert(!is_built_in_simple_type(name) && !is_built_in_complex_type(name));
|
|
|
|
for (std::size_t i = 0; i < tree_type->children.size(); i++) {
|
|
assert(tree_type->children[i]->get_type() == NODE_TYPE_NAT_CONST);
|
|
}
|
|
|
|
return gen_main_class_name(t) + "^";
|
|
}
|
|
std::string gen_output_begin(void) const final {
|
|
return prefix_ +
|
|
"#include \"td/tl/tl_dotnet_object.h\"\n\n"
|
|
"namespace Telegram {\n"
|
|
"namespace Td {\n"
|
|
"namespace Api {\n";
|
|
}
|
|
std::string gen_output_end() const final {
|
|
return "}\n"
|
|
"}\n"
|
|
"}\n";
|
|
}
|
|
|
|
std::string gen_forward_class_declaration(const std::string &class_name, bool is_proxy) const final {
|
|
if (!is_header_) {
|
|
return "";
|
|
}
|
|
std::stringstream ss;
|
|
ss << (is_proxy ? "interface" : "ref") << " class " << class_name << ";\n";
|
|
return ss.str();
|
|
}
|
|
|
|
std::string gen_class_begin(const std::string &class_name, const std::string &base_class_name,
|
|
bool is_proxy, const tl::tl_tree *result) const final {
|
|
if (!is_header_) {
|
|
return "";
|
|
}
|
|
|
|
std::stringstream ss;
|
|
ss << "\npublic " << (is_proxy ? "interface" : "ref") << " class " << class_name << (is_proxy ? "" : " sealed")
|
|
<< (class_name == gen_base_tl_class_name() ? "" : " : " + base_class_name) << " {\n"
|
|
<< " public:\n";
|
|
return ss.str();
|
|
}
|
|
std::string gen_class_end(void) const final {
|
|
return "";
|
|
}
|
|
|
|
std::string gen_field_definition(const std::string &class_name, const std::string &type_name,
|
|
const std::string &field_name) const final {
|
|
if (!is_header_) {
|
|
return "";
|
|
}
|
|
auto fixed_field_name = field_name;
|
|
if (field_name == class_name) {
|
|
fixed_field_name += "Value";
|
|
}
|
|
if (type_name.substr(0, field_name.size()) == field_name) {
|
|
auto fixed_type_name = "::Telegram::Td::Api::" + type_name;
|
|
std::stringstream ss;
|
|
ss << "private:\n";
|
|
ss << " " << fixed_type_name << " " << fixed_field_name << "PrivateField;\n";
|
|
ss << "public:\n";
|
|
ss << " property " << fixed_type_name << " " << fixed_field_name << " {\n";
|
|
ss << " " << fixed_type_name << " get() {\n";
|
|
ss << " return " << fixed_field_name << "PrivateField;\n";
|
|
ss << " }\n";
|
|
ss << " void set(" << fixed_type_name << " newValue) {\n";
|
|
ss << " " << fixed_field_name << "PrivateField = newValue;\n";
|
|
ss << " }\n";
|
|
ss << " }\n";
|
|
return ss.str();
|
|
}
|
|
return " property " + type_name + " " + fixed_field_name + ";\n";
|
|
}
|
|
|
|
std::string gen_store_function_begin(const std::string &storer_name, const std::string &class_name, int arity,
|
|
std::vector<var_description> &vars, int storer_type) const final {
|
|
if (storer_type < 0) {
|
|
return "";
|
|
}
|
|
std::stringstream ss;
|
|
ss << "\n";
|
|
if (storer_type) {
|
|
ss << (is_header_ ? " virtual " : "") << "String^ " << (is_header_ ? "" : gen_class_name(class_name) + "::")
|
|
<< "ToString()" << (is_header_ ? " override;" : " {\n return ::Telegram::Td::Api::ToString(this);\n}")
|
|
<< "\n";
|
|
} else {
|
|
ss << (is_header_ ? " virtual " : "") << "NativeObject^ "
|
|
<< (is_header_ ? "" : gen_class_name(class_name) + "::") << "ToUnmanaged()";
|
|
if (is_header_) {
|
|
ss << ";\n";
|
|
} else {
|
|
ss << " {\n return REF_NEW NativeObject(::Telegram::Td::Api::ToUnmanaged(this).release());\n}\n";
|
|
}
|
|
}
|
|
return ss.str();
|
|
}
|
|
std::string gen_store_function_end(const std::vector<var_description> &vars, int storer_type) const final {
|
|
return "";
|
|
}
|
|
|
|
std::string gen_constructor_begin(int field_count, const std::string &class_name, bool is_default) const final {
|
|
std::stringstream ss;
|
|
ss << "\n";
|
|
ss << (is_header_ ? " " : gen_class_name(class_name) + "::") << gen_class_name(class_name) << "(";
|
|
return ss.str();
|
|
}
|
|
std::string gen_constructor_parameter(int field_num, const std::string &class_name, const arg &a,
|
|
bool is_default) const final {
|
|
if (is_default) {
|
|
return "";
|
|
}
|
|
std::stringstream ss;
|
|
ss << (field_num == 0 ? "" : ", ");
|
|
auto field_type = gen_field_type(a);
|
|
auto pos = 0;
|
|
while (field_type.substr(pos, 6) == "Array<") {
|
|
pos += 6;
|
|
}
|
|
if (field_type.substr(pos, 4) != "BYTE" && field_type.substr(pos, 6) != "String" &&
|
|
to_upper(field_type[pos]) == field_type[pos]) {
|
|
field_type = field_type.substr(0, pos) + "::Telegram::Td::Api::" + field_type.substr(pos);
|
|
}
|
|
ss << field_type << " " << to_camelCase(a.name);
|
|
return ss.str();
|
|
}
|
|
std::string gen_constructor_field_init(int field_num, const std::string &class_name, const arg &a,
|
|
bool is_default) const final {
|
|
if (is_default || is_header_) {
|
|
return "";
|
|
}
|
|
|
|
std::stringstream ss;
|
|
if (field_num == 0) {
|
|
ss << ") {\n";
|
|
}
|
|
auto field_name = gen_field_name(a.name);
|
|
if (field_name == class_name) {
|
|
field_name += "Value";
|
|
}
|
|
ss << " " << field_name << " = " << to_camelCase(a.name) << ";\n";
|
|
|
|
return ss.str();
|
|
}
|
|
std::string gen_constructor_end(const tl_combinator *t, int field_count, bool is_default) const final {
|
|
if (is_header_) {
|
|
return ");\n";
|
|
}
|
|
std::stringstream ss;
|
|
if (field_count == 0) {
|
|
ss << ") {\n";
|
|
}
|
|
ss << "}\n";
|
|
return ss.str();
|
|
}
|
|
std::string gen_additional_function(const std::string &function_name, const tl_combinator *t,
|
|
bool is_function) const final {
|
|
std::stringstream ss;
|
|
if (is_header_ && function_name == "ToUnmanaged") {
|
|
ss << "};\n";
|
|
}
|
|
ss << "\n";
|
|
if (function_name == "ToUnmanaged") {
|
|
gen_to_unmanaged(ss, t);
|
|
} else {
|
|
gen_from_unmanaged(ss, t);
|
|
}
|
|
return ss.str();
|
|
}
|
|
void gen_to_unmanaged(std::stringstream &ss, const tl_combinator *t) const {
|
|
auto native_class_name = gen_native_class_name(t->name);
|
|
auto class_name = gen_class_name(t->name);
|
|
ss << "td::td_api::object_ptr<td::td_api::" << native_class_name << "> ToUnmanaged(" << class_name << "^ from)";
|
|
if (is_header_) {
|
|
ss << ";\n";
|
|
return;
|
|
}
|
|
ss << " {\n"
|
|
<< " if (!from) {\n"
|
|
<< " return nullptr;\n"
|
|
<< " }\n"
|
|
<< " return td::td_api::make_object<td::td_api::" << native_class_name << ">(";
|
|
bool is_first = true;
|
|
for (auto &it : t->args) {
|
|
if (is_first) {
|
|
is_first = false;
|
|
} else {
|
|
ss << ", ";
|
|
}
|
|
auto field_name = gen_field_name(it.name);
|
|
if (field_name == class_name) {
|
|
field_name += "Value";
|
|
}
|
|
ss << "ToUnmanaged(from->" << field_name << ")";
|
|
}
|
|
ss << ");\n}\n";
|
|
}
|
|
|
|
void gen_from_unmanaged(std::stringstream &ss, const tl_combinator *t) const {
|
|
auto native_class_name = gen_native_class_name(t->name);
|
|
auto class_name = gen_class_name(t->name);
|
|
ss << class_name << "^ FromUnmanaged(td::td_api::" << native_class_name << " &from)";
|
|
if (is_header_) {
|
|
ss << ";\n";
|
|
return;
|
|
}
|
|
ss << " {\n"
|
|
<< " return REF_NEW " << class_name << "(";
|
|
bool is_first = true;
|
|
for (auto &it : t->args) {
|
|
if (is_first) {
|
|
is_first = false;
|
|
} else {
|
|
ss << ", ";
|
|
}
|
|
bool need_bytes = gen_field_type(it) == "Array<BYTE>^" || gen_field_type(it) == "Array<Array<BYTE>^>^";
|
|
ss << (need_bytes ? "Bytes" : "") << "FromUnmanaged(from." << gen_native_field_name(it.name) << ")";
|
|
}
|
|
ss << ");\n}\n";
|
|
}
|
|
|
|
std::string gen_array_type_name(const tl_tree_array *arr, const std::string &field_name) const final {
|
|
assert(0);
|
|
return std::string();
|
|
}
|
|
std::string gen_var_type_name(void) const final {
|
|
assert(0);
|
|
return std::string();
|
|
}
|
|
|
|
std::string gen_int_const(const tl_tree *tree_c, const std::vector<var_description> &vars) const final {
|
|
assert(0);
|
|
return std::string();
|
|
}
|
|
|
|
std::string gen_var_name(const var_description &desc) const final {
|
|
assert(0);
|
|
return "";
|
|
}
|
|
std::string gen_parameter_name(int index) const final {
|
|
assert(0);
|
|
return "";
|
|
}
|
|
|
|
std::string gen_class_alias(const std::string &class_name, const std::string &alias_name) const final {
|
|
return "";
|
|
}
|
|
|
|
std::string gen_vars(const tl_combinator *t, const tl_tree_type *result_type,
|
|
std::vector<var_description> &vars) const final {
|
|
assert(vars.empty());
|
|
return "";
|
|
}
|
|
std::string gen_function_vars(const tl_combinator *t, std::vector<var_description> &vars) const final {
|
|
assert(vars.empty());
|
|
return "";
|
|
}
|
|
std::string gen_uni(const tl_tree_type *result_type, std::vector<var_description> &vars,
|
|
bool check_negative) const final {
|
|
assert(result_type->children.empty());
|
|
return "";
|
|
}
|
|
std::string gen_constructor_id_store(std::int32_t id, int storer_type) const final {
|
|
return "";
|
|
}
|
|
std::string gen_field_fetch(int field_num, const arg &a, std::vector<var_description> &vars, bool flat,
|
|
int parser_type) const final {
|
|
return "";
|
|
// std::stringstream ss;
|
|
// ss << gen_field_name(a.name) << " = from_unmanaged(from->" <<
|
|
// gen_native_field_name(a.name) << ");\n"; return ss.str();
|
|
}
|
|
std::string gen_field_store(const arg &a, std::vector<var_description> &vars, bool flat,
|
|
int storer_type) const final {
|
|
return "";
|
|
// std::stringstream ss;
|
|
// ss << "to_unmanaged(" << gen_field_name(a.name) << ")";
|
|
// return ss.str();
|
|
}
|
|
std::string gen_type_fetch(const std::string &field_name, const tl_tree_type *tree_type,
|
|
const std::vector<var_description> &vars, int parser_type) const final {
|
|
assert(vars.empty());
|
|
return "";
|
|
}
|
|
|
|
std::string gen_type_store(const std::string &field_name, const tl_tree_type *tree_type,
|
|
const std::vector<var_description> &vars, int storer_type) const final {
|
|
return "";
|
|
}
|
|
std::string gen_var_type_fetch(const arg &a) const final {
|
|
assert(0);
|
|
return "";
|
|
}
|
|
|
|
std::string gen_get_id(const std::string &class_name, std::int32_t id, bool is_proxy) const final {
|
|
return "";
|
|
}
|
|
|
|
std::string gen_function_result_type(const tl_tree *result) const final {
|
|
return "";
|
|
}
|
|
|
|
std::string gen_fetch_function_begin(const std::string &parser_name, const std::string &class_name,
|
|
const std::string &parent_class_name, int arity, int field_count,
|
|
std::vector<var_description> &vars, int parser_type) const final {
|
|
return "";
|
|
}
|
|
std::string gen_fetch_function_end(bool has_parent, int field_count, const std::vector<var_description> &vars,
|
|
int parser_type) const final {
|
|
return "";
|
|
}
|
|
|
|
std::string gen_fetch_function_result_begin(const std::string &parser_name, const std::string &class_name,
|
|
const tl_tree *result) const final {
|
|
return "";
|
|
}
|
|
std::string gen_fetch_function_result_end(void) const final {
|
|
return "";
|
|
}
|
|
std::string gen_fetch_function_result_any_begin(const std::string &parser_name, const std::string &class_name,
|
|
bool is_proxy) const final {
|
|
return "";
|
|
}
|
|
std::string gen_fetch_function_result_any_end(bool is_proxy) const final {
|
|
return "";
|
|
}
|
|
|
|
std::string gen_fetch_switch_begin(void) const final {
|
|
return "";
|
|
}
|
|
std::string gen_fetch_switch_case(const tl_combinator *t, int arity) const final {
|
|
return "";
|
|
}
|
|
std::string gen_fetch_switch_end(void) const final {
|
|
return "";
|
|
}
|
|
|
|
std::string gen_additional_proxy_function_begin(const std::string &function_name, const tl_type *type,
|
|
const std::string &name, int arity, bool is_function) const final {
|
|
std::stringstream ss;
|
|
if (is_header_ && function_name == "ToUnmanaged") {
|
|
ss << "};\n";
|
|
}
|
|
if (type == nullptr) {
|
|
return ss.str();
|
|
}
|
|
auto native_class_name = gen_native_class_name(type->name);
|
|
auto class_name = gen_class_name(type->name);
|
|
if (function_name == "ToUnmanaged") {
|
|
ss << "td::td_api::object_ptr<td::td_api::" << native_class_name << "> ToUnmanaged(" << class_name << "^ from)";
|
|
if (is_header_) {
|
|
ss << ";\n";
|
|
return ss.str();
|
|
}
|
|
ss << " {\n"
|
|
<< " if (!from) {\n"
|
|
<< " return nullptr;\n"
|
|
<< " }\n"
|
|
<< " return td::td_api::move_object_as<td::td_api::" << native_class_name
|
|
<< ">(from->ToUnmanaged()->get_object_ptr());\n}\n";
|
|
} else {
|
|
ss << class_name << "^ FromUnmanaged(td::td_api::" << native_class_name << " &from)";
|
|
if (is_header_) {
|
|
ss << ";\n";
|
|
return ss.str();
|
|
}
|
|
ss << " {\n";
|
|
ss << " return DoFromUnmanaged<" << class_name << "^>(from);\n";
|
|
ss << "}\n";
|
|
}
|
|
return ss.str();
|
|
}
|
|
std::string gen_additional_proxy_function_case(const std::string &function_name, const tl_type *type,
|
|
const std::string &class_name, int arity) const final {
|
|
return "";
|
|
}
|
|
std::string gen_additional_proxy_function_case(const std::string &function_name, const tl_type *type,
|
|
const tl_combinator *t, int arity, bool is_function) const final {
|
|
return "";
|
|
}
|
|
std::string gen_additional_proxy_function_end(const std::string &function_name, const tl_type *type,
|
|
bool is_function) const final {
|
|
return "";
|
|
}
|
|
};
|
|
|
|
} // namespace tl
|
|
} // namespace td
|