#include <iostream>
#include <string>
#include <vector>
#include "cpp.h"
#include "isl_config.h"
void cpp_generator::set_class_construction_types(isl_class &clazz)
{
for (const auto &cons : clazz.constructors) {
ParmVarDecl *param;
QualType type;
std::string arg_type;
if (!is_implicit_conversion(Method(clazz, cons)))
continue;
param = cons->getParamDecl(0);
type = param->getOriginalType();
arg_type = extract_type(type);
clazz.construction_types.emplace(arg_type, param);
}
}
void cpp_generator::set_construction_types()
{
for (auto &kvp : classes) {
auto &clazz = kvp.second;
set_class_construction_types(clazz);
}
}
cpp_generator::cpp_generator(SourceManager &SM,
set<RecordDecl *> &exported_types,
set<FunctionDecl *> exported_functions, set<FunctionDecl *> functions) :
generator(SM, exported_types, exported_functions, functions)
{
set_construction_types();
copy_super_methods();
}
static void copy_method(isl_class &clazz, const isl_class &super,
const std::string &name, FunctionDecl *fd, int depth)
{
clazz.methods[name].insert(fd);
clazz.copied_from.emplace(fd, super);
clazz.copy_depth.emplace(fd, depth);
}
static bool same_signature(FunctionDecl *fd1, FunctionDecl *fd2)
{
int n1 = fd1->getNumParams();
int n2 = fd2->getNumParams();
if (n1 != n2)
return false;
for (int i = 1; i < n1; ++i) {
ParmVarDecl *p1 = fd1->getParamDecl(i);
ParmVarDecl *p2 = fd2->getParamDecl(i);
if (p1->getOriginalType() != p2->getOriginalType())
return false;
}
return true;
}
static int copy_depth(const isl_class &clazz, FunctionDecl *fd)
{
if (clazz.copy_depth.count(fd) == 0)
return 0;
return clazz.copy_depth.at(fd);
}
static bool is_overridden(FunctionDecl *fd, isl_class &clazz,
const std::string &name, int depth)
{
if (clazz.methods.count(name) == 0)
return false;
for (const auto &m : clazz.methods.at(name)) {
if (!same_signature(fd, m))
continue;
if (copy_depth(clazz, m) <= depth)
return true;
clazz.methods[name].erase(m);
return false;
}
return false;
}
void cpp_generator::copy_methods(isl_class &clazz, const std::string &name,
const isl_class &super, const function_set &methods)
{
for (auto fd : methods) {
int depth;
if (method2class(fd)->is_static(fd))
continue;
depth = copy_depth(super, fd) + 1;
if (is_overridden(fd, clazz, name, depth))
continue;
copy_method(clazz, super, name, fd, depth);
}
}
void cpp_generator::copy_super_methods(isl_class &clazz, const isl_class &super)
{
for (const auto &kvp : super.methods) {
const auto &name = kvp.first;
const auto &methods = kvp.second;
copy_methods(clazz, name, super, methods);
}
}
void cpp_generator::copy_super_methods(isl_class &clazz, set<string> &done)
{
auto supers = find_superclasses(clazz.type);
for (const auto &super : supers)
if (done.count(super) == 0)
copy_super_methods(classes[super], done);
done.insert(clazz.name);
for (const auto &super_name : supers) {
const auto &super = classes[super_name];
if (super.construction_types.count(clazz.name) == 0)
continue;
copy_super_methods(clazz, super);
}
}
void cpp_generator::copy_super_methods()
{
set<string> done;
for (auto &kvp : classes) {
auto &clazz = kvp.second;
if (clazz.is_type_subclass())
continue;
if (done.count(clazz.name) != 0)
continue;
copy_super_methods(clazz, done);
}
}
void cpp_generator::class_printer::print_constructors()
{
for (const auto &cons : clazz.constructors)
print_method(Method(clazz, cons));
}
void cpp_generator::class_printer::print_methods()
{
for (const auto &kvp : clazz.methods)
print_method_group(kvp.second, kvp.first);
}
void cpp_generator::class_printer::print_set_enums(FunctionDecl *fd)
{
for (const auto &set : clazz.set_enums.at(fd)) {
EnumMethod method(clazz, fd, set.method_name, set.name);
print_method(method);
}
}
void cpp_generator::class_printer::print_set_enums()
{
for (const auto &kvp : clazz.set_enums)
print_set_enums(kvp.first);
}
bool cpp_generator::class_printer::next_variant(FunctionDecl *fd,
std::vector<bool> &convert)
{
size_t n = convert.size();
for (int i = n - 1; i >= 1; --i) {
ParmVarDecl *param = fd->getParamDecl(i);
const Type *type = param->getOriginalType().getTypePtr();
if (generator.conversions.count(type) == 0)
continue;
if (convert[i])
continue;
convert[i] = true;
for (size_t j = i + 1; j < n; ++j)
convert[j] = false;
return true;
}
return false;
}
void cpp_generator::class_printer::print_method_variants(FunctionDecl *fd,
const std::string &name)
{
Method method(clazz, fd, name);
std::vector<bool> convert(method.num_params());
if (method.clazz.copied_from.count(method.fd) == 0) {
print_method(method);
if (clazz.is_get_method(fd))
print_get_method(fd);
} else {
auto super = method.clazz.copied_from.at(method.fd);
print_method(ConversionMethod(method, super.name));
}
if (method.kind != Method::Kind::member_method)
return;
while (next_variant(fd, convert)) {
print_method(ConversionMethod(method, [&] (int pos) {
return get_param(fd, pos, convert);
}));
}
}
static bool has_single_isl_argument(FunctionDecl *fd)
{
ParmVarDecl *param;
if (fd->getNumParams() != 2)
return false;
param = fd->getParamDecl(1);
return generator::is_isl_type(param->getOriginalType());
}
static FunctionDecl *single_local(const isl_class &clazz,
const function_set &methods)
{
int count = 0;
FunctionDecl *local;
for (const auto &fn : methods) {
if (!clazz.first_arg_matches_class(fn))
continue;
++count;
local = fn;
}
return count == 1 ? local : NULL;
}
void cpp_generator::class_printer::print_descendent_overloads(
FunctionDecl *fd, const std::string &name)
{
Method method(clazz, fd, name);
ParmVarDecl *param = fd->getParamDecl(1);
QualType type = param->getOriginalType();
std::string arg = type->getPointeeType().getAsString();
for (const auto &kvp : generator.classes[arg].construction_types) {
const auto sub = kvp.second;
print_method(ConversionMethod(method, [&] (int pos) {
return sub;
}));
}
}
void cpp_generator::class_printer::print_method_group(
const function_set &methods, const std::string &name)
{
FunctionDecl *local;
for (const auto &fd : methods)
print_method_variants(fd, name);
if (!want_descendent_overloads(methods))
return;
local = single_local(clazz, methods);
if (!local)
return;
if (!has_single_isl_argument(local))
return;
print_descendent_overloads(local, name);
}
void Method::print_param_use(ostream &os, int pos) const
{
ParmVarDecl *param = fd->getParamDecl(pos);
bool load_from_this_ptr = pos == 0 && kind == member_method;
string name = param->getName().str();
QualType type = param->getOriginalType();
if (type->isIntegerType()) {
os << name;
return;
}
if (generator::is_string(type)) {
os << name << ".c_str()";
return;
}
if (generator::is_callback(type)) {
os << name << "_lambda, ";
os << "&" << name << "_data";
return;
}
if (!load_from_this_ptr)
os << name << ".";
if (generator::keeps(param)) {
os << "get()";
} else {
if (load_from_this_ptr)
os << "copy()";
else
os << "release()";
}
}
bool Method::is_subclass_mutator() const
{
return clazz.is_type_subclass() && generator::is_mutator(clazz, fd);
}
std::string cpp_type_printer::return_type(const Method &method) const
{
if (method.is_subclass_mutator())
return cpp_generator::type2cpp(method.clazz);
else
return param(-1, method.fd->getReturnType());
}
ParmVarDecl *cpp_generator::class_printer::get_param(FunctionDecl *fd,
int pos, const std::vector<bool> &convert)
{
ParmVarDecl *param = fd->getParamDecl(pos);
if (!convert[pos])
return param;
return generator.conversions[param->getOriginalType().getTypePtr()];
}
void cpp_generator::class_printer::print_method_header(
const Method &method, const cpp_type_printer &type_printer)
{
string rettype_str = type_printer.return_type(method);
if (declarations) {
os << " ";
if (method.kind == Method::Kind::static_method)
os << "static ";
os << "inline ";
if (method.kind == Method::Kind::constructor) {
if (generator.is_implicit_conversion(method))
os << "/* implicit */ ";
else
os << "explicit ";
}
}
if (method.kind != Method::Kind::constructor)
os << rettype_str << " ";
if (!declarations)
os << type_printer.class_type(cppstring) << "::";
if (method.kind != Method::Kind::constructor)
os << method.name;
else
os << cppstring;
method.print_cpp_arg_list(os, [&] (int i, int arg) {
std::string name = method.fd->getParamDecl(i)->getName().str();
ParmVarDecl *param = method.get_param(i);
QualType type = param->getOriginalType();
string cpptype = type_printer.param(arg, type);
if (!method.param_needs_copy(i))
os << "const " << cpptype << " &" << name;
else
os << cpptype << " " << name;
});
if (method.kind == Method::Kind::member_method)
os << " const";
}
std::string cpp_type_printer::generate_callback_args(int arg, QualType type,
bool cpp) const
{
std::string type_str;
const FunctionProtoType *callback;
int num_params;
callback = generator::extract_prototype(type);
num_params = callback->getNumArgs();
if (cpp)
num_params--;
for (long i = 0; i < num_params; i++) {
QualType type = callback->getArgType(i);
if (cpp)
type_str += param(arg + 1 + i, type);
else
type_str += type.getAsString();
if (!cpp)
type_str += "arg_" + ::to_string(i);
if (i != num_params - 1)
type_str += ", ";
}
return type_str;
}
std::string cpp_type_printer::generate_callback_type(int arg, QualType type)
const
{
std::string type_str;
const FunctionProtoType *callback = generator::extract_prototype(type);
QualType return_type = callback->getReturnType();
string rettype_str = param(arg, return_type);
type_str = "std::function<";
type_str += rettype_str;
type_str += "(";
type_str += generate_callback_args(arg, type, true);
type_str += ")>";
return type_str;
}
static const char *rename_map[][2] = {
{ "union", "unite" },
};
static std::string rename_method(std::string name)
{
for (size_t i = 0; i < sizeof(rename_map) / sizeof(rename_map[0]); i++)
if (name.compare(rename_map[i][0]) == 0)
return rename_map[i][1];
return name;
}
string cpp_generator::type2cpp(const isl_class &clazz)
{
return type2cpp(clazz.subclass_name);
}
string cpp_generator::type2cpp(string type_str)
{
return type_str.substr(4);
}
std::string cpp_type_printer::isl_bool() const
{
return "bool";
}
string cpp_type_printer::isl_stat() const
{
return "void";
}
string cpp_type_printer::isl_size() const
{
return "unsigned";
}
std::string cpp_type_printer::isl_namespace() const
{
return "isl::";
}
std::string cpp_type_printer::class_type(const std::string &cpp_name) const
{
return cpp_name;
}
std::string cpp_type_printer::qualified(int arg, const std::string &cpp_type)
const
{
return isl_namespace() + cpp_type;
}
std::string cpp_type_printer::isl_type(int arg, QualType type) const
{
auto name = type->getPointeeType().getAsString();
return qualified(arg, cpp_generator::type2cpp(name));
}
std::string cpp_type_printer::param(int arg, QualType type) const
{
if (cpp_generator::is_isl_type(type))
return isl_type(arg, type);
if (cpp_generator::is_isl_bool(type))
return isl_bool();
if (cpp_generator::is_isl_stat(type))
return isl_stat();
if (cpp_generator::is_isl_size(type))
return isl_size();
if (type->isIntegerType())
return type.getAsString();
if (cpp_generator::is_string(type))
return "std::string";
if (cpp_generator::is_callback(type))
return generate_callback_type(arg, type);
generator::die("Cannot convert type to C++ type");
}
bool cpp_generator::is_subclass(QualType subclass_type,
const isl_class &class_type)
{
std::string type_str = subclass_type->getPointeeType().getAsString();
std::vector<std::string> superclasses;
std::vector<const isl_class *> parents;
std::vector<std::string>::iterator ci;
superclasses = generator::find_superclasses(classes[type_str].type);
for (ci = superclasses.begin(); ci < superclasses.end(); ci++)
parents.push_back(&classes[*ci]);
while (!parents.empty()) {
const isl_class *candidate = parents.back();
parents.pop_back();
if (&class_type == candidate)
return true;
superclasses = generator::find_superclasses(candidate->type);
for (ci = superclasses.begin(); ci < superclasses.end(); ci++)
parents.push_back(&classes[*ci]);
}
return false;
}
bool cpp_generator::is_implicit_conversion(const Method &cons)
{
const auto &clazz = cons.clazz;
ParmVarDecl *param = cons.fd->getParamDecl(0);
QualType type = param->getOriginalType();
int num_params = cons.fd->getNumParams();
if (num_params != 1)
return false;
if (is_isl_type(type) && !is_isl_ctx(type) && is_subclass(type, clazz))
return true;
return false;
}
Method::list_combiner Method::print_combiner(std::ostream &os)
{
return {
[&] () { os << "("; },
[&] () { os << ", "; },
[&] () { os << ")"; }
};
}
Method::list_combiner Method::empty_combiner()
{
return { [&] () { }, [&] () { }, [&] () { } };
}
static Method::Kind get_kind(const isl_class &clazz, FunctionDecl *method)
{
if (generator::is_constructor(method))
return Method::Kind::constructor;
else if (generator::is_static(clazz, method))
return Method::Kind::static_method;
else
return Method::Kind::member_method;
}
static std::vector<ParmVarDecl *> find_callback_args(FunctionDecl *fd)
{
std::vector<ParmVarDecl *> callbacks;
int num_params = fd->getNumParams();
for (int i = 0; i < num_params; ++i) {
ParmVarDecl *param = fd->getParamDecl(i);
if (generator::is_callback(param->getType()))
callbacks.emplace_back(param);
}
return callbacks;
}
Method::Method(const isl_class &clazz, FunctionDecl *fd,
const std::string &name) :
clazz(clazz), fd(fd), name(rename_method(name)),
kind(get_kind(clazz, fd)),
callbacks(find_callback_args(fd))
{
}
Method::Method(const isl_class &clazz, FunctionDecl *fd) :
Method(clazz, fd, clazz.method_name(fd))
{
}
int Method::c_num_params() const
{
return fd->getNumParams();
}
int Method::num_params() const
{
return c_num_params();
}
void Method::on_arg_list(int start, int end,
const Method::list_combiner &combiner,
const std::function<bool(int i)> &on_arg_skip_next)
{
combiner.before();
for (int i = start; i < end; ++i) {
if (i != start)
combiner.between();
if (on_arg_skip_next(i))
++i;
}
combiner.after();
}
void Method::print_arg_list(std::ostream &os, int start, int end,
const std::function<bool(int i)> &print_arg_skip_next)
{
on_arg_list(start, end, print_combiner(os), [&] (int i) {
return print_arg_skip_next(i);
});
}
void Method::on_fd_arg_list(int start, int end,
const Method::list_combiner &combiner,
const std::function<void(int i, int arg)> &on_arg) const
{
int arg = start;
on_arg_list(start, end, combiner, [this, &on_arg, &arg] (int i) {
auto type = fd->getParamDecl(i)->getType();
on_arg(i, arg++);
if (!generator::is_callback(type))
return false;
arg += generator::prototype_n_args(type) - 1;
return true;
});
}
void Method::print_fd_arg_list(std::ostream &os, int start, int end,
const std::function<void(int i, int arg)> &print_arg) const
{
on_fd_arg_list(start, end, print_combiner(os), print_arg);
}
void Method::on_cpp_arg_list(const Method::list_combiner &combiner,
const std::function<void(int i, int arg)> &on_arg) const
{
int first_param = kind == member_method ? 1 : 0;
on_fd_arg_list(first_param, num_params(), combiner, on_arg);
}
void Method::on_cpp_arg_list(
const std::function<void(int i, int arg)> &on_arg) const
{
on_cpp_arg_list(empty_combiner(), on_arg);
}
void Method::print_cpp_arg_list(std::ostream &os,
const std::function<void(int i, int arg)> &print_arg) const
{
on_cpp_arg_list(print_combiner(os), print_arg);
}
bool Method::param_needs_copy(int pos) const
{
ParmVarDecl *param = get_param(pos);
QualType type = param->getOriginalType();
if (generator::keeps(param))
return false;
if (generator::is_string(type) || generator::is_callback(type))
return false;
return true;
}
clang::ParmVarDecl *Method::get_param(int pos) const
{
return fd->getParamDecl(pos);
}
ConversionMethod::ConversionMethod(const Method &method,
const std::string &this_type,
const std::function<clang::ParmVarDecl *(int pos)> &get_param) :
NoCopyMethod(method), this_type(this_type),
get_param_fn(get_param)
{
}
ConversionMethod::ConversionMethod(const Method &method,
const std::string &this_type) :
ConversionMethod(method, this_type, [this] (int pos) {
return Method::get_param(pos);
})
{
}
ConversionMethod::ConversionMethod(const Method &method,
const std::function<clang::ParmVarDecl *(int pos)> &get_param) :
ConversionMethod(method, method.clazz.name, get_param)
{
}
bool NoCopyMethod::param_needs_copy(int pos) const
{
ParmVarDecl *param = get_param(pos);
QualType type = param->getOriginalType();
if (generator::is_isl_type(type))
return false;
return Method::param_needs_copy(pos);
}
clang::ParmVarDecl *ConversionMethod::get_param(int pos) const
{
return get_param_fn(pos);
}
void ConversionMethod::print_call(std::ostream &os, const std::string &ns) const
{
if (clazz.name == this_type) {
os << "this->";
} else {
auto cpp_type = ns + cpp_generator::type2cpp(this_type);
os << cpp_type << "(*this).";
}
os << name;
}
EnumMethod::EnumMethod(const isl_class &clazz, FunctionDecl *fd,
const std::string &method_name, const std::string &enum_name) :
Method(clazz, fd, method_name), enum_name(enum_name)
{
}
void EnumMethod::print_param_use(ostream &os, int pos) const
{
if (pos == num_params())
os << enum_name;
else
Method::print_param_use(os, pos);
}
int EnumMethod::num_params() const
{
return Method::num_params() - 1;
}
cpp_generator::class_printer::class_printer(std::ostream &os,
const isl_class &clazz, cpp_generator &generator,
bool declarations) :
os(os), clazz(clazz), cppstring(type2cpp(clazz)), generator(generator),
declarations(declarations)
{
}