mirror of
https://review.haiku-os.org/buildtools
synced 2025-01-18 20:38:39 +01:00
1202 lines
36 KiB
C++
1202 lines
36 KiB
C++
/*
|
|
* Copyright 2016, 2017 Tobias Grosser. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following
|
|
* disclaimer in the documentation and/or other materials provided
|
|
* with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY TOBIAS GROSSER ''AS IS'' AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL TOBIAS GROSSER OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* The views and conclusions contained in the software and documentation
|
|
* are those of the authors and should not be interpreted as
|
|
* representing official policies, either expressed or implied, of
|
|
* Tobias Grosser.
|
|
*/
|
|
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "cpp.h"
|
|
#include "isl_config.h"
|
|
|
|
/* Determine the isl types from which the given class can be implicitly
|
|
* constructed using a unary constructor.
|
|
*
|
|
* Look through all constructors for implicit conversion constructors that take
|
|
* an isl type and add those types, along with the corresponding
|
|
* constructor argument.
|
|
*/
|
|
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);
|
|
}
|
|
}
|
|
|
|
/* Determine the isl types from which any (proper) class can be constructed
|
|
* using a unary constructor.
|
|
*/
|
|
void cpp_generator::set_construction_types()
|
|
{
|
|
for (auto &kvp : classes) {
|
|
auto &clazz = kvp.second;
|
|
set_class_construction_types(clazz);
|
|
}
|
|
}
|
|
|
|
/* Construct a generator for C++ bindings.
|
|
*
|
|
* The classes and methods are extracted by the constructor
|
|
* of the generator superclass.
|
|
*
|
|
* Additionally extract information about types
|
|
* that can be converted to a class and copy all methods
|
|
* from superclasses that can be converted to a given class
|
|
* to that class.
|
|
*/
|
|
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();
|
|
}
|
|
|
|
/* Copy the method called "name" described by "fd" from "super" to "clazz"
|
|
* with the distance to the original ancestor given by "depth".
|
|
*
|
|
* In particular, keep track of "fd" as well as the superclass
|
|
* from which it was copied and the distance to the original ancestor.
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/* Do "fd1" and "fd2" have the same signature (ignoring the first argument
|
|
* which represents the object class on which the corresponding method
|
|
* gets called).
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/* Return the distance between "clazz" and the ancestor
|
|
* from which "fd" got copied.
|
|
* If no distance was recorded, then the method has not been copied
|
|
* but appears in "clazz" itself and so the distance is zero.
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/* Is the method derived from "fd", with method name "name" and
|
|
* with distance to the original ancestor "depth",
|
|
* overridden by a method already in "clazz"?
|
|
*
|
|
* A method is considered to have been overridden if there
|
|
* is a method with the same name in "clazz" that has the same signature and
|
|
* that comes from an ancestor closer to "clazz",
|
|
* where an ancestor is closer if the distance in the class hierarchy
|
|
* is smaller or the distance is the same and the ancestor appears
|
|
* closer in the declaration of the type (in which case it gets added first).
|
|
*
|
|
* If a method with the same signature has already been added,
|
|
* but it does not override the method derived from "fd",
|
|
* then this method is removed since it is overridden by "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;
|
|
}
|
|
|
|
/* Add the methods "methods" with method name "name" from "super" to "clazz"
|
|
* provided they have not been overridden by a method already in "clazz".
|
|
*
|
|
* Methods that are static in their original class are not copied.
|
|
*/
|
|
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);
|
|
}
|
|
}
|
|
|
|
/* Add all methods from "super" to "clazz" that have not been overridden
|
|
* by a method already in "clazz".
|
|
*
|
|
* Look through all groups of methods with the same name.
|
|
*/
|
|
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);
|
|
}
|
|
}
|
|
|
|
/* Copy methods from the superclasses of "clazz"
|
|
* if an object of this class can be implicitly converted to an object
|
|
* from the superclass, keeping track
|
|
* of the classes that have already been handled in "done".
|
|
*
|
|
* Make sure the superclasses have copied methods from their superclasses first
|
|
* since those methods could be copied further down to this class.
|
|
*
|
|
* Consider the superclass that appears closest to the subclass first.
|
|
*/
|
|
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);
|
|
}
|
|
}
|
|
|
|
/* For each (proper) class, copy methods from its superclasses,
|
|
* if an object from the class can be converted to an object
|
|
* from the superclass.
|
|
*
|
|
* Type based subclasses are not considered for now since
|
|
* they do not have any explicit superclasses.
|
|
*
|
|
* Iterate through all (proper) classes and copy methods
|
|
* from their superclasses,
|
|
* unless they have already been determined by a recursive call.
|
|
*/
|
|
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);
|
|
}
|
|
}
|
|
|
|
/* Print declarations or implementations of constructors.
|
|
*
|
|
* For each isl function that is marked as __isl_constructor,
|
|
* add a corresponding C++ constructor.
|
|
*
|
|
* Example of declarations:
|
|
*
|
|
* inline /\* implicit *\/ union_set(basic_set bset);
|
|
* inline /\* implicit *\/ union_set(set set);
|
|
* inline explicit val(ctx ctx, long i);
|
|
* inline explicit val(ctx ctx, const std::string &str);
|
|
*/
|
|
void cpp_generator::class_printer::print_constructors()
|
|
{
|
|
for (const auto &cons : clazz.constructors)
|
|
print_method(Method(clazz, cons));
|
|
}
|
|
|
|
/* Print declarations or definitions for methods in the class.
|
|
*/
|
|
void cpp_generator::class_printer::print_methods()
|
|
{
|
|
for (const auto &kvp : clazz.methods)
|
|
print_method_group(kvp.second, kvp.first);
|
|
}
|
|
|
|
/* Print declarations or implementations for the methods derived from "fd",
|
|
* which sets an enum.
|
|
*
|
|
* A method is generated for each value in the enum, setting
|
|
* the enum to that value.
|
|
*/
|
|
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);
|
|
}
|
|
}
|
|
|
|
/* Print declarations or implementations for methods derived from functions
|
|
* that set an enum.
|
|
*/
|
|
void cpp_generator::class_printer::print_set_enums()
|
|
{
|
|
for (const auto &kvp : clazz.set_enums)
|
|
print_set_enums(kvp.first);
|
|
}
|
|
|
|
/* Update "convert" to reflect the next combination of automatic conversions
|
|
* for the arguments of "fd",
|
|
* returning false if there are no more combinations.
|
|
*
|
|
* In particular, find the last argument for which an automatic
|
|
* conversion function is available mapping to the type of this argument and
|
|
* that is not already marked for conversion.
|
|
* Mark this argument, if any, for conversion and clear the markings
|
|
* of all subsequent arguments.
|
|
* Repeated calls to this method therefore run through
|
|
* all possible combinations.
|
|
*
|
|
* Note that the first function argument is never considered
|
|
* for automatic conversion since this is the argument
|
|
* from which the isl_ctx used in the conversion is extracted.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/* Print a declaration or definition for a method called "name"
|
|
* derived from "fd".
|
|
*
|
|
* If the method was copied from a superclass, then print a definition
|
|
* that calls the corresponding method in the superclass.
|
|
* Otherwise, for methods that are identified as "get" methods, also
|
|
* print a declaration or definition for the method
|
|
* using a name that includes the "get_" prefix.
|
|
*
|
|
* If the generated method is an object method, then check
|
|
* whether any of its arguments can be automatically converted
|
|
* from something else, and, if so, generate a method
|
|
* for each combination of converted arguments.
|
|
* Do so by constructing a ConversionMethod that changes the converted arguments
|
|
* to those of the sources of the conversions.
|
|
*
|
|
* Note that a method may be both copied from a superclass and
|
|
* have arguments that can be automatically converted.
|
|
* In this case, the conversion methods for the arguments
|
|
* call the corresponding method in this class, which
|
|
* in turn will call the method in the superclass.
|
|
*/
|
|
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);
|
|
}));
|
|
}
|
|
}
|
|
|
|
/* Given a function declaration representing a method,
|
|
* does this method have a single argument (beyond the object
|
|
* on which the method is called) that corresponds to
|
|
* an isl object?
|
|
*/
|
|
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());
|
|
}
|
|
|
|
/* Does the set "methods" contain exactly one function declaration
|
|
* that corresponds to a method of "clazz" itself (i.e., that
|
|
* was not copied from an ancestor)?
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/* Given a function declaration "fd" for a method called "name"
|
|
* with a single argument representing an isl object,
|
|
* generate declarations or definitions for methods with the same name,
|
|
* but with as argument an isl object of a class that can be implicitly
|
|
* converted to that of the original argument.
|
|
* In particular, generate methods for converting this argument.
|
|
*/
|
|
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;
|
|
}));
|
|
}
|
|
}
|
|
|
|
/* Print declarations or definitions for methods called "name"
|
|
* derived from "methods".
|
|
*
|
|
* If want_descendent_overloads signals that variants should be added that take
|
|
* as arguments those types that can be converted to the original argument type
|
|
* through a unary constructor and if only one of the methods in the group
|
|
* was originally defined in "clazz", then effectively add those variants.
|
|
* Only do this for methods with a single (isl object) argument.
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/* Print the use of the argument at position "pos" to "os".
|
|
*
|
|
* Member methods pass the isl object corresponding to "this"
|
|
* as first argument (at position 0).
|
|
* Any other arguments are passed along from the method arguments.
|
|
*
|
|
* If the argument value is loaded from a this pointer, the original
|
|
* value must be preserved and must consequently be copied. Values that are
|
|
* loaded from method parameters do not need to be preserved, as such values
|
|
* will already be copies of the actual parameters. It is consequently possible
|
|
* to directly take the pointer from these values, which saves
|
|
* an unnecessary copy.
|
|
*
|
|
* In case the parameter is a callback function, two parameters get printed,
|
|
* a wrapper for the callback function and a pointer to the actual
|
|
* callback function. The wrapper is expected to be available
|
|
* in a previously declared variable <name>_lambda, while
|
|
* the actual callback function is expected to be stored
|
|
* in a structure called <name>_data.
|
|
* The caller of this function must ensure that these variables exist.
|
|
*/
|
|
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()";
|
|
}
|
|
}
|
|
|
|
/* Does the isl function from which this method is derived
|
|
* modify an object of a subclass based on a type function?
|
|
*/
|
|
bool Method::is_subclass_mutator() const
|
|
{
|
|
return clazz.is_type_subclass() && generator::is_mutator(clazz, fd);
|
|
}
|
|
|
|
/* Return the C++ return type of the method "method".
|
|
*
|
|
* If the corresponding function modifies an object of a subclass, then return
|
|
* the type of this subclass.
|
|
* Otherwise, return the C++ counterpart of the actual return type.
|
|
*/
|
|
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());
|
|
}
|
|
|
|
/* Return the formal parameter at position "pos" of "fd".
|
|
* However, if this parameter should be converted, as indicated
|
|
* by "convert", then return the second formal parameter
|
|
* of the conversion function instead.
|
|
*/
|
|
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()];
|
|
}
|
|
|
|
/* Print the header for "method", without newline or semicolon,
|
|
* using "type_printer" to print argument and return types.
|
|
*
|
|
* Print the header of a declaration if this->declarations is set,
|
|
* otherwise print the header of a method definition.
|
|
*
|
|
* This function prints headers for member methods, static methods, and
|
|
* constructors, either for their declaration or definition.
|
|
*
|
|
* Member functions are declared as "const", as they do not change the current
|
|
* object, but instead create a new object. They always retrieve the first
|
|
* parameter of the original isl function from the this-pointer of the object,
|
|
* such that only starting at the second parameter the parameters of the
|
|
* original function become part of the method's interface.
|
|
*
|
|
* A function
|
|
*
|
|
* __isl_give isl_set *isl_set_intersect(__isl_take isl_set *s1,
|
|
* __isl_take isl_set *s2);
|
|
*
|
|
* is translated into:
|
|
*
|
|
* inline set intersect(set set2) const
|
|
*
|
|
* For static functions and constructors all parameters of the original isl
|
|
* function are exposed.
|
|
*
|
|
* Parameters of which no copy is required, are passed
|
|
* as const reference, which allows the compiler to optimize the parameter
|
|
* transfer.
|
|
*
|
|
* Constructors are marked as explicit using the C++ keyword 'explicit' or as
|
|
* implicit using a comment in place of the explicit keyword. By annotating
|
|
* implicit constructors with a comment, users of the interface are made
|
|
* aware of the potential danger that implicit construction is possible
|
|
* for these constructors, whereas without a comment not every user would
|
|
* know that implicit construction is allowed in absence of an explicit keyword.
|
|
*
|
|
* Note that in case "method" is a ConversionMethod, the argument returned
|
|
* by Method::get_param may be different from the original argument.
|
|
* The name of the argument is, however, derived from the original
|
|
* function argument.
|
|
*/
|
|
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) {
|
|
std::string name = method.fd->getParamDecl(i)->getName().str();
|
|
ParmVarDecl *param = method.get_param(i);
|
|
QualType type = param->getOriginalType();
|
|
string cpptype = type_printer.param(i, type);
|
|
|
|
if (!method.param_needs_copy(i))
|
|
os << "const " << cpptype << " &" << name;
|
|
else
|
|
os << cpptype << " " << name;
|
|
});
|
|
|
|
if (method.kind == Method::Kind::member_method)
|
|
os << " const";
|
|
}
|
|
|
|
/* Generate the list of argument types for a callback function of
|
|
* type "type", appearing in argument position "arg".
|
|
* If "cpp" is set, then generate the C++ type list, otherwise
|
|
* the C type list.
|
|
*
|
|
* For a callback of type
|
|
*
|
|
* isl_stat (*)(__isl_take isl_map *map, void *user)
|
|
*
|
|
* the following C++ argument list is generated:
|
|
*
|
|
* map
|
|
*
|
|
* The arguments of the callback are considered to appear
|
|
* after the position of the callback itself.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/* Generate the full cpp type of a callback function of type "type",
|
|
* appearing in argument position "arg".
|
|
*
|
|
* For a callback of type
|
|
*
|
|
* isl_stat (*)(__isl_take isl_map *map, void *user)
|
|
*
|
|
* the following type is generated:
|
|
*
|
|
* std::function<stat(map)>
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/* An array listing functions that must be renamed and the function name they
|
|
* should be renamed to. We currently rename functions in case their name would
|
|
* match a reserved C++ keyword, which is not allowed in C++.
|
|
*/
|
|
static const char *rename_map[][2] = {
|
|
{ "union", "unite" },
|
|
};
|
|
|
|
/* Rename method "name" in case the method name in the C++ bindings should not
|
|
* match the name in the C bindings. We do this for example to avoid
|
|
* C++ keywords.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/* Translate isl class "clazz" to its corresponding C++ type.
|
|
* Use the name of the type based subclass, if any.
|
|
*/
|
|
string cpp_generator::type2cpp(const isl_class &clazz)
|
|
{
|
|
return type2cpp(clazz.subclass_name);
|
|
}
|
|
|
|
/* Translate type string "type_str" to its C++ name counterpart.
|
|
*/
|
|
string cpp_generator::type2cpp(string type_str)
|
|
{
|
|
return type_str.substr(4);
|
|
}
|
|
|
|
/* Return the C++ counterpart to the isl_bool type.
|
|
*
|
|
* By default, this is simply "bool" since
|
|
* the exceptional case is handled through exceptions.
|
|
*/
|
|
std::string cpp_type_printer::isl_bool() const
|
|
{
|
|
return "bool";
|
|
}
|
|
|
|
/* Return the C++ counterpart to the isl_stat type.
|
|
*
|
|
* By default, this is simply "void" since
|
|
* the exceptional case is handled through exceptions.
|
|
*/
|
|
string cpp_type_printer::isl_stat() const
|
|
{
|
|
return "void";
|
|
}
|
|
|
|
/* Return the C++ counterpart to the isl_size type.
|
|
*
|
|
* By default, this is simply "unsigned" since
|
|
* the exceptional case is handled through exceptions.
|
|
*/
|
|
string cpp_type_printer::isl_size() const
|
|
{
|
|
return "unsigned";
|
|
}
|
|
|
|
/* Return the namespace of the generated C++ bindings.
|
|
*
|
|
* By default, this is "isl::".
|
|
*/
|
|
std::string cpp_type_printer::isl_namespace() const
|
|
{
|
|
return "isl::";
|
|
}
|
|
|
|
/* Return the class type given the C++ name.
|
|
*
|
|
* By default, directly use the C++ name.
|
|
*/
|
|
std::string cpp_type_printer::class_type(const std::string &cpp_name) const
|
|
{
|
|
return cpp_name;
|
|
}
|
|
|
|
/* Return the qualified form of the given C++ isl type name appearing
|
|
* in argument position "arg" (-1 for return type).
|
|
*
|
|
* By default, the argument position is ignored.
|
|
*/
|
|
std::string cpp_type_printer::qualified(int arg, const std::string &cpp_type)
|
|
const
|
|
{
|
|
return isl_namespace() + cpp_type;
|
|
}
|
|
|
|
/* Return the C++ counterpart to the given isl type appearing
|
|
* in argument position "arg" (-1 for return 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));
|
|
}
|
|
|
|
/* Translate parameter or return type "type" to its C++ name counterpart.
|
|
* "arg" is the position of the argument, or -1 in case of the return type.
|
|
* If any callback is involved, then the return type and arguments types
|
|
* of the callback are considered to start at the position of the callback.
|
|
*/
|
|
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");
|
|
}
|
|
|
|
/* Check if "subclass_type" is a subclass of "class_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;
|
|
}
|
|
|
|
/* Check if "cons" is an implicit conversion constructor of class "clazz".
|
|
*
|
|
* An implicit conversion constructor is generated in case "cons" has a single
|
|
* parameter, where the parameter type is a subclass of the class that is
|
|
* currently being generated.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/* Get kind of "method" in "clazz".
|
|
*
|
|
* Given the declaration of a static or member method, returns its kind.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/* Return the callback argument of "fd", if there is any.
|
|
* Return NULL otherwise.
|
|
*/
|
|
static ParmVarDecl *find_callback_arg(FunctionDecl *fd)
|
|
{
|
|
int num_params = fd->getNumParams();
|
|
|
|
for (int i = 0; i < num_params; ++i) {
|
|
ParmVarDecl *param = fd->getParamDecl(i);
|
|
if (generator::is_callback(param->getType()))
|
|
return param;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Construct a C++ method object from the class to which is belongs,
|
|
* the isl function from which it is derived and the method name.
|
|
*
|
|
* Perform any renaming of the method that may be required and
|
|
* determine the type of the method.
|
|
*/
|
|
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)),
|
|
callback(find_callback_arg(fd))
|
|
{
|
|
}
|
|
|
|
/* Construct a C++ method object from the class to which is belongs and
|
|
* the isl function from which it is derived.
|
|
*
|
|
* Obtain the default method name and continue
|
|
* with the generic constructor.
|
|
*/
|
|
Method::Method(const isl_class &clazz, FunctionDecl *fd) :
|
|
Method(clazz, fd, clazz.method_name(fd))
|
|
{
|
|
}
|
|
|
|
/* Return the number of parameters of the corresponding C function.
|
|
*
|
|
* If the method has a callback argument, we reduce the number of parameters
|
|
* that are exposed by one to hide the user pointer from the interface. On
|
|
* the C++ side no user pointer is needed, as arguments can be forwarded
|
|
* as part of the std::function argument which specifies the callback function.
|
|
*
|
|
* The user pointer is also removed from the number of parameters
|
|
* of the C function because the pair of callback and user pointer
|
|
* is considered as a single argument that is printed as a whole
|
|
* by Method::print_param_use.
|
|
*/
|
|
int Method::c_num_params() const
|
|
{
|
|
return fd->getNumParams() - (callback != NULL);
|
|
}
|
|
|
|
/* Return the number of parameters of the method
|
|
* (including the implicit "this").
|
|
*
|
|
* By default, it is the same as the number of parameters
|
|
* of the corresponding C function.
|
|
*/
|
|
int Method::num_params() const
|
|
{
|
|
return c_num_params();
|
|
}
|
|
|
|
/* Print the arguments from "start" (inclusive) to "end" (exclusive)
|
|
* as arguments to a method of C function call, using "print_arg"
|
|
* to print each individual argument.
|
|
*/
|
|
void Method::print_arg_list(std::ostream &os, int start, int end,
|
|
const std::function<void(int i)> &print_arg)
|
|
{
|
|
os << "(";
|
|
for (int i = start; i < end; ++i) {
|
|
if (i != start)
|
|
os << ", ";
|
|
print_arg(i);
|
|
}
|
|
os << ")";
|
|
}
|
|
|
|
/* Print the arguments to the method call, using "print_arg"
|
|
* to print each individual argument.
|
|
*/
|
|
void Method::print_cpp_arg_list(std::ostream &os,
|
|
const std::function<void(int i)> &print_arg) const
|
|
{
|
|
int first_param = kind == member_method ? 1 : 0;
|
|
print_arg_list(os, first_param, num_params(), print_arg);
|
|
}
|
|
|
|
/* Should the parameter at position "pos" be a copy (rather than
|
|
* a const reference)?
|
|
*
|
|
* Strictly speaking, a copy is only needed on isl types that are
|
|
* not marked __isl_keep, since those will be release()'d
|
|
* by code printed by Method::print_param_use.
|
|
*
|
|
* However, there may be other arguments such as integer types
|
|
* that are more naturally passed as a copy.
|
|
* The default is therefore to require a copy, except for
|
|
* arguments marked __isl_keep, string arguments or callback arguments.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/* Return the method argument at position "pos".
|
|
*/
|
|
clang::ParmVarDecl *Method::get_param(int pos) const
|
|
{
|
|
return fd->getParamDecl(pos);
|
|
}
|
|
|
|
/* Construct a method that performs one or more conversions
|
|
* from the original Method (without conversions),
|
|
* the name of the type to which "this" should be converted and
|
|
* a function for determining the arguments of the constructed method.
|
|
*/
|
|
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)
|
|
{
|
|
}
|
|
|
|
/* Construct a method that only performs a conversion on "this"
|
|
* from the original Method (without conversions) and
|
|
* the name of the type to which "this" should be converted.
|
|
*
|
|
* Call the generic constructor with
|
|
* a function for determining the arguments of the constructed method
|
|
* that performs no conversion.
|
|
*/
|
|
ConversionMethod::ConversionMethod(const Method &method,
|
|
const std::string &this_type) :
|
|
ConversionMethod(method, this_type, [this] (int pos) {
|
|
return Method::get_param(pos);
|
|
})
|
|
{
|
|
}
|
|
|
|
/* Construct a method that performs one or more argument conversions
|
|
* from the original Method (without conversions) and
|
|
* a function for determining the arguments of the constructed method.
|
|
*
|
|
* Call the generic constructor with method.clazz.name as "this" type,
|
|
* indicating that "this" should not be converted.
|
|
*/
|
|
ConversionMethod::ConversionMethod(const Method &method,
|
|
const std::function<clang::ParmVarDecl *(int pos)> &get_param) :
|
|
ConversionMethod(method, method.clazz.name, get_param)
|
|
{
|
|
}
|
|
|
|
/* Should the parameter at position "pos" be a copy (rather than
|
|
* a const reference)?
|
|
*
|
|
* Parameters of isl type do not need to be a copy.
|
|
* For other types, use the same defaults as Method.
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/* Return the method argument at position "pos".
|
|
*
|
|
* Call get_param_fn to determine this argument.
|
|
*/
|
|
clang::ParmVarDecl *ConversionMethod::get_param(int pos) const
|
|
{
|
|
return get_param_fn(pos);
|
|
}
|
|
|
|
/* Print a call to the method (without the arguments),
|
|
* with "ns" the namespace of the generated C++ bindings.
|
|
*
|
|
* If "this_type" is different from the name of the class of the method,
|
|
* then "this" needs to be converted to that type before
|
|
* the call is performed.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/* Construct an object representing a C++ method for setting an enum
|
|
* from the class to which is belongs,
|
|
* the isl function from which it is derived and the method and enum names.
|
|
*/
|
|
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)
|
|
{
|
|
}
|
|
|
|
/* Print the use of the argument at position "pos" to "os".
|
|
*
|
|
* If the position is beyond the number of method arguments,
|
|
* then it corresponds to the enum value corresponding to this EnumMethod.
|
|
* Otherwise, delegate to Method::print_param_use.
|
|
*/
|
|
void EnumMethod::print_param_use(ostream &os, int pos) const
|
|
{
|
|
if (pos == num_params())
|
|
os << enum_name;
|
|
else
|
|
Method::print_param_use(os, pos);
|
|
}
|
|
|
|
/* Return the number of parameters of the method
|
|
* (including the implicit "this").
|
|
*
|
|
* The last argument of the C function does not appear in the method call,
|
|
* because it is replaced by a break-up into several methods.
|
|
*/
|
|
int EnumMethod::num_params() const
|
|
{
|
|
return Method::num_params() - 1;
|
|
}
|
|
|
|
/* Initialize a class method printer from the stream onto which the methods
|
|
* are printed, the class method description and the C++ interface generator.
|
|
*/
|
|
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)
|
|
{
|
|
}
|