1 /*
2  * Copyright 2016, 2017 Tobias Grosser. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  *    1. Redistributions of source code must retain the above copyright
9  *       notice, this list of conditions and the following disclaimer.
10  *
11  *    2. Redistributions in binary form must reproduce the above
12  *       copyright notice, this list of conditions and the following
13  *       disclaimer in the documentation and/or other materials provided
14  *       with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY TOBIAS GROSSER ''AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL TOBIAS GROSSER OR
20  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
23  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * The views and conclusions contained in the software and documentation
29  * are those of the authors and should not be interpreted as
30  * representing official policies, either expressed or implied, of
31  * Tobias Grosser.
32  */
33 
34 #include <iostream>
35 #include <string>
36 #include <vector>
37 
38 #include "cpp.h"
39 #include "isl_config.h"
40 
41 /* Determine the isl types from which the given class can be implicitly
42  * constructed using a unary constructor.
43  *
44  * Look through all constructors for implicit conversion constructors that take
45  * an isl type and add those types, along with the corresponding
46  * constructor argument.
47  */
set_class_construction_types(isl_class & clazz)48 void cpp_generator::set_class_construction_types(isl_class &clazz)
49 {
50 	for (const auto &cons : clazz.constructors) {
51 		ParmVarDecl *param;
52 		QualType type;
53 		std::string arg_type;
54 
55 		if (!is_implicit_conversion(Method(clazz, cons)))
56 			continue;
57 
58 		param = cons->getParamDecl(0);
59 		type = param->getOriginalType();
60 		arg_type = extract_type(type);
61 		clazz.construction_types.emplace(arg_type, param);
62 	}
63 }
64 
65 /* Determine the isl types from which any (proper) class can be constructed
66  * using a unary constructor.
67  */
set_construction_types()68 void cpp_generator::set_construction_types()
69 {
70 	for (auto &kvp : classes) {
71 		auto &clazz = kvp.second;
72 		set_class_construction_types(clazz);
73 	}
74 }
75 
76 /* Construct a generator for C++ bindings.
77  *
78  * The classes and methods are extracted by the constructor
79  * of the generator superclass.
80  *
81  * Additionally extract information about types
82  * that can be converted to a class and copy all methods
83  * from superclasses that can be converted to a given class
84  * to that class.
85  */
cpp_generator(SourceManager & SM,set<RecordDecl * > & exported_types,set<FunctionDecl * > exported_functions,set<FunctionDecl * > functions)86 cpp_generator::cpp_generator(SourceManager &SM,
87 	set<RecordDecl *> &exported_types,
88 	set<FunctionDecl *> exported_functions, set<FunctionDecl *> functions) :
89 		generator(SM, exported_types, exported_functions, functions)
90 {
91 	set_construction_types();
92 	copy_super_methods();
93 }
94 
95 /* Copy the method called "name" described by "fd" from "super" to "clazz"
96  * with the distance to the original ancestor given by "depth".
97  *
98  * In particular, keep track of "fd" as well as the superclass
99  * from which it was copied and the distance to the original ancestor.
100  */
copy_method(isl_class & clazz,const isl_class & super,const std::string & name,FunctionDecl * fd,int depth)101 static void copy_method(isl_class &clazz, const isl_class &super,
102 	const std::string &name, FunctionDecl *fd, int depth)
103 {
104 	clazz.methods[name].insert(fd);
105 	clazz.copied_from.emplace(fd, super);
106 	clazz.copy_depth.emplace(fd, depth);
107 }
108 
109 /* Do "fd1" and "fd2" have the same signature (ignoring the first argument
110  * which represents the object class on which the corresponding method
111  * gets called).
112  */
same_signature(FunctionDecl * fd1,FunctionDecl * fd2)113 static bool same_signature(FunctionDecl *fd1, FunctionDecl *fd2)
114 {
115 	int n1 = fd1->getNumParams();
116 	int n2 = fd2->getNumParams();
117 
118 	if (n1 != n2)
119 		return false;
120 
121 	for (int i = 1; i < n1; ++i) {
122 		ParmVarDecl *p1 = fd1->getParamDecl(i);
123 		ParmVarDecl *p2 = fd2->getParamDecl(i);
124 
125 		if (p1->getOriginalType() != p2->getOriginalType())
126 			return false;
127 	}
128 
129 	return true;
130 }
131 
132 /* Return the distance between "clazz" and the ancestor
133  * from which "fd" got copied.
134  * If no distance was recorded, then the method has not been copied
135  * but appears in "clazz" itself and so the distance is zero.
136  */
copy_depth(const isl_class & clazz,FunctionDecl * fd)137 static int copy_depth(const isl_class &clazz, FunctionDecl *fd)
138 {
139 	if (clazz.copy_depth.count(fd) == 0)
140 		return 0;
141 	return clazz.copy_depth.at(fd);
142 }
143 
144 /* Is the method derived from "fd", with method name "name" and
145  * with distance to the original ancestor "depth",
146  * overridden by a method already in "clazz"?
147  *
148  * A method is considered to have been overridden if there
149  * is a method with the same name in "clazz" that has the same signature and
150  * that comes from an ancestor closer to "clazz",
151  * where an ancestor is closer if the distance in the class hierarchy
152  * is smaller or the distance is the same and the ancestor appears
153  * closer in the declaration of the type (in which case it gets added first).
154  *
155  * If a method with the same signature has already been added,
156  * but it does not override the method derived from "fd",
157  * then this method is removed since it is overridden by "fd".
158  */
is_overridden(FunctionDecl * fd,isl_class & clazz,const std::string & name,int depth)159 static bool is_overridden(FunctionDecl *fd, isl_class &clazz,
160 	const std::string &name, int depth)
161 {
162 	if (clazz.methods.count(name) == 0)
163 		return false;
164 
165 	for (const auto &m : clazz.methods.at(name)) {
166 		if (!same_signature(fd, m))
167 			continue;
168 		if (copy_depth(clazz, m) <= depth)
169 			return true;
170 		clazz.methods[name].erase(m);
171 		return false;
172 	}
173 	return false;
174 }
175 
176 /* Add the methods "methods" with method name "name" from "super" to "clazz"
177  * provided they have not been overridden by a method already in "clazz".
178  *
179  * Methods that are static in their original class are not copied.
180  */
copy_methods(isl_class & clazz,const std::string & name,const isl_class & super,const function_set & methods)181 void cpp_generator::copy_methods(isl_class &clazz, const std::string &name,
182 	const isl_class &super, const function_set &methods)
183 {
184 	for (auto fd : methods) {
185 		int depth;
186 
187 		if (method2class(fd)->is_static(fd))
188 			continue;
189 		depth = copy_depth(super, fd) + 1;
190 		if (is_overridden(fd, clazz, name, depth))
191 			continue;
192 		copy_method(clazz, super, name, fd, depth);
193 	}
194 }
195 
196 /* Add all methods from "super" to "clazz" that have not been overridden
197  * by a method already in "clazz".
198  *
199  * Look through all groups of methods with the same name.
200  */
copy_super_methods(isl_class & clazz,const isl_class & super)201 void cpp_generator::copy_super_methods(isl_class &clazz, const isl_class &super)
202 {
203 	for (const auto &kvp : super.methods) {
204 		const auto &name = kvp.first;
205 		const auto &methods = kvp.second;
206 
207 		copy_methods(clazz, name, super, methods);
208 	}
209 }
210 
211 /* Copy methods from the superclasses of "clazz"
212  * if an object of this class can be implicitly converted to an object
213  * from the superclass, keeping track
214  * of the classes that have already been handled in "done".
215  *
216  * Make sure the superclasses have copied methods from their superclasses first
217  * since those methods could be copied further down to this class.
218  *
219  * Consider the superclass that appears closest to the subclass first.
220  */
copy_super_methods(isl_class & clazz,set<string> & done)221 void cpp_generator::copy_super_methods(isl_class &clazz, set<string> &done)
222 {
223 	auto supers = find_superclasses(clazz.type);
224 
225 	for (const auto &super : supers)
226 		if (done.count(super) == 0)
227 			copy_super_methods(classes[super], done);
228 	done.insert(clazz.name);
229 
230 	for (const auto &super_name : supers) {
231 		const auto &super = classes[super_name];
232 
233 		if (super.construction_types.count(clazz.name) == 0)
234 			continue;
235 		copy_super_methods(clazz, super);
236 	}
237 }
238 
239 /* For each (proper) class, copy methods from its superclasses,
240  * if an object from the class can be converted to an object
241  * from the superclass.
242  *
243  * Type based subclasses are not considered for now since
244  * they do not have any explicit superclasses.
245  *
246  * Iterate through all (proper) classes and copy methods
247  * from their superclasses,
248  * unless they have already been determined by a recursive call.
249  */
copy_super_methods()250 void cpp_generator::copy_super_methods()
251 {
252 	set<string> done;
253 
254 	for (auto &kvp : classes) {
255 		auto &clazz = kvp.second;
256 
257 		if (clazz.is_type_subclass())
258 			continue;
259 		if (done.count(clazz.name) != 0)
260 			continue;
261 		copy_super_methods(clazz, done);
262 	}
263 }
264 
265 /* Print declarations or implementations of constructors.
266  *
267  * For each isl function that is marked as __isl_constructor,
268  * add a corresponding C++ constructor.
269  *
270  * Example of declarations:
271  *
272  * 	inline /\* implicit *\/ union_set(basic_set bset);
273  * 	inline /\* implicit *\/ union_set(set set);
274  * 	inline explicit val(ctx ctx, long i);
275  * 	inline explicit val(ctx ctx, const std::string &str);
276  */
print_constructors()277 void cpp_generator::class_printer::print_constructors()
278 {
279 	for (const auto &cons : clazz.constructors)
280 		print_method(Method(clazz, cons));
281 }
282 
283 /* Print declarations or definitions for methods in the class.
284  */
print_methods()285 void cpp_generator::class_printer::print_methods()
286 {
287 	for (const auto &kvp : clazz.methods)
288 		print_method_group(kvp.second, kvp.first);
289 }
290 
291 /* Print declarations or implementations for the methods derived from "fd",
292  * which sets an enum.
293  *
294  * A method is generated for each value in the enum, setting
295  * the enum to that value.
296  */
print_set_enums(FunctionDecl * fd)297 void cpp_generator::class_printer::print_set_enums(FunctionDecl *fd)
298 {
299 	for (const auto &set : clazz.set_enums.at(fd)) {
300 		EnumMethod method(clazz, fd, set.method_name, set.name);
301 
302 		print_method(method);
303 	}
304 }
305 
306 /* Print declarations or implementations for methods derived from functions
307  * that set an enum.
308  */
print_set_enums()309 void cpp_generator::class_printer::print_set_enums()
310 {
311 	for (const auto &kvp : clazz.set_enums)
312 		print_set_enums(kvp.first);
313 }
314 
315 /* Update "convert" to reflect the next combination of automatic conversions
316  * for the arguments of "fd",
317  * returning false if there are no more combinations.
318  *
319  * In particular, find the last argument for which an automatic
320  * conversion function is available mapping to the type of this argument and
321  * that is not already marked for conversion.
322  * Mark this argument, if any, for conversion and clear the markings
323  * of all subsequent arguments.
324  * Repeated calls to this method therefore run through
325  * all possible combinations.
326  *
327  * Note that the first function argument is never considered
328  * for automatic conversion since this is the argument
329  * from which the isl_ctx used in the conversion is extracted.
330  */
next_variant(FunctionDecl * fd,std::vector<bool> & convert)331 bool cpp_generator::class_printer::next_variant(FunctionDecl *fd,
332 	std::vector<bool> &convert)
333 {
334 	size_t n = convert.size();
335 
336 	for (int i = n - 1; i >= 1; --i) {
337 		ParmVarDecl *param = fd->getParamDecl(i);
338 		const Type *type = param->getOriginalType().getTypePtr();
339 
340 		if (generator.conversions.count(type) == 0)
341 			continue;
342 		if (convert[i])
343 			continue;
344 		convert[i] = true;
345 		for (size_t j = i + 1; j < n; ++j)
346 			convert[j] = false;
347 		return true;
348 	}
349 
350 	return false;
351 }
352 
353 /* Print a declaration or definition for a method called "name"
354  * derived from "fd".
355  *
356  * If the method was copied from a superclass, then print a definition
357  * that calls the corresponding method in the superclass.
358  * Otherwise, for methods that are identified as "get" methods, also
359  * print a declaration or definition for the method
360  * using a name that includes the "get_" prefix.
361  *
362  * If the generated method is an object method, then check
363  * whether any of its arguments can be automatically converted
364  * from something else, and, if so, generate a method
365  * for each combination of converted arguments.
366  * Do so by constructing a ConversionMethod that changes the converted arguments
367  * to those of the sources of the conversions.
368  *
369  * Note that a method may be both copied from a superclass and
370  * have arguments that can be automatically converted.
371  * In this case, the conversion methods for the arguments
372  * call the corresponding method in this class, which
373  * in turn will call the method in the superclass.
374  */
print_method_variants(FunctionDecl * fd,const std::string & name)375 void cpp_generator::class_printer::print_method_variants(FunctionDecl *fd,
376 	const std::string &name)
377 {
378 	Method method(clazz, fd, name);
379 	std::vector<bool> convert(method.num_params());
380 
381 	if (method.clazz.copied_from.count(method.fd) == 0) {
382 		print_method(method);
383 		if (clazz.is_get_method(fd))
384 			print_get_method(fd);
385 	} else {
386 		auto super = method.clazz.copied_from.at(method.fd);
387 		print_method(ConversionMethod(method, super.name));
388 	}
389 	if (method.kind != Method::Kind::member_method)
390 		return;
391 	while (next_variant(fd, convert)) {
392 		print_method(ConversionMethod(method, [&] (int pos) {
393 			return get_param(fd, pos, convert);
394 		}));
395 	}
396 }
397 
398 /* Given a function declaration representing a method,
399  * does this method have a single argument (beyond the object
400  * on which the method is called) that corresponds to
401  * an isl object?
402  */
has_single_isl_argument(FunctionDecl * fd)403 static bool has_single_isl_argument(FunctionDecl *fd)
404 {
405 	ParmVarDecl *param;
406 
407 	if (fd->getNumParams() != 2)
408 		return false;
409 
410 	param = fd->getParamDecl(1);
411 	return generator::is_isl_type(param->getOriginalType());
412 }
413 
414 /* Does the set "methods" contain exactly one function declaration
415  * that corresponds to a method of "clazz" itself (i.e., that
416  * was not copied from an ancestor)?
417  */
single_local(const isl_class & clazz,const function_set & methods)418 static FunctionDecl *single_local(const isl_class &clazz,
419 	const function_set &methods)
420 {
421 	int count = 0;
422 	FunctionDecl *local;
423 
424 	for (const auto &fn : methods) {
425 		if (!clazz.first_arg_matches_class(fn))
426 			continue;
427 		++count;
428 		local = fn;
429 	}
430 
431 	return count == 1 ? local : NULL;
432 }
433 
434 /* Given a function declaration "fd" for a method called "name"
435  * with a single argument representing an isl object,
436  * generate declarations or definitions for methods with the same name,
437  * but with as argument an isl object of a class that can be implicitly
438  * converted to that of the original argument.
439  * In particular, generate methods for converting this argument.
440  */
print_descendent_overloads(FunctionDecl * fd,const std::string & name)441 void cpp_generator::class_printer::print_descendent_overloads(
442 	FunctionDecl *fd, const std::string &name)
443 {
444 	Method method(clazz, fd, name);
445 	ParmVarDecl *param = fd->getParamDecl(1);
446 	QualType type = param->getOriginalType();
447 	std::string arg = type->getPointeeType().getAsString();
448 
449 	for (const auto &kvp : generator.classes[arg].construction_types) {
450 		const auto sub = kvp.second;
451 		print_method(ConversionMethod(method, [&] (int pos) {
452 			return sub;
453 		}));
454 	}
455 }
456 
457 /* Print declarations or definitions for methods called "name"
458  * derived from "methods".
459  *
460  * If want_descendent_overloads signals that variants should be added that take
461  * as arguments those types that can be converted to the original argument type
462  * through a unary constructor and if only one of the methods in the group
463  * was originally defined in "clazz", then effectively add those variants.
464  * Only do this for methods with a single (isl object) argument.
465  */
print_method_group(const function_set & methods,const std::string & name)466 void cpp_generator::class_printer::print_method_group(
467 	const function_set &methods, const std::string &name)
468 {
469 	FunctionDecl *local;
470 
471 	for (const auto &fd : methods)
472 		print_method_variants(fd, name);
473 	if (!want_descendent_overloads(methods))
474 		return;
475 	local = single_local(clazz, methods);
476 	if (!local)
477 		return;
478 	if (!has_single_isl_argument(local))
479 		return;
480 	print_descendent_overloads(local, name);
481 }
482 
483 /* Print the use of the argument at position "pos" to "os".
484  *
485  * Member methods pass the isl object corresponding to "this"
486  * as first argument (at position 0).
487  * Any other arguments are passed along from the method arguments.
488  *
489  * If the argument value is loaded from a this pointer, the original
490  * value must be preserved and must consequently be copied.  Values that are
491  * loaded from method parameters do not need to be preserved, as such values
492  * will already be copies of the actual parameters.  It is consequently possible
493  * to directly take the pointer from these values, which saves
494  * an unnecessary copy.
495  *
496  * In case the parameter is a callback function, two parameters get printed,
497  * a wrapper for the callback function and a pointer to the actual
498  * callback function.  The wrapper is expected to be available
499  * in a previously declared variable <name>_lambda, while
500  * the actual callback function is expected to be stored
501  * in a structure called <name>_data.
502  * The caller of this function must ensure that these variables exist.
503  */
print_param_use(ostream & os,int pos) const504 void Method::print_param_use(ostream &os, int pos) const
505 {
506 	ParmVarDecl *param = fd->getParamDecl(pos);
507 	bool load_from_this_ptr = pos == 0 && kind == member_method;
508 	string name = param->getName().str();
509 	QualType type = param->getOriginalType();
510 
511 	if (type->isIntegerType()) {
512 		os << name;
513 		return;
514 	}
515 
516 	if (generator::is_string(type)) {
517 		os << name << ".c_str()";
518 		return;
519 	}
520 
521 	if (generator::is_callback(type)) {
522 		os << name << "_lambda, ";
523 		os << "&" << name << "_data";
524 		return;
525 	}
526 
527 	if (!load_from_this_ptr)
528 		os << name << ".";
529 
530 	if (generator::keeps(param)) {
531 		os << "get()";
532 	} else {
533 		if (load_from_this_ptr)
534 			os << "copy()";
535 		else
536 			os << "release()";
537 	}
538 }
539 
540 /* Does the isl function from which this method is derived
541  * modify an object of a subclass based on a type function?
542  */
is_subclass_mutator() const543 bool Method::is_subclass_mutator() const
544 {
545 	return clazz.is_type_subclass() && generator::is_mutator(clazz, fd);
546 }
547 
548 /* Return the C++ return type of the method "method".
549  *
550  * If the corresponding function modifies an object of a subclass, then return
551  * the type of this subclass.
552  * Otherwise, return the C++ counterpart of the actual return type.
553  */
return_type(const Method & method) const554 std::string cpp_type_printer::return_type(const Method &method) const
555 {
556 	if (method.is_subclass_mutator())
557 		return cpp_generator::type2cpp(method.clazz);
558 	else
559 		return param(-1, method.fd->getReturnType());
560 }
561 
562 /* Return the formal parameter at position "pos" of "fd".
563  * However, if this parameter should be converted, as indicated
564  * by "convert", then return the second formal parameter
565  * of the conversion function instead.
566  */
get_param(FunctionDecl * fd,int pos,const std::vector<bool> & convert)567 ParmVarDecl *cpp_generator::class_printer::get_param(FunctionDecl *fd,
568 	int pos, const std::vector<bool> &convert)
569 {
570 	ParmVarDecl *param = fd->getParamDecl(pos);
571 
572 	if (!convert[pos])
573 		return param;
574 	return generator.conversions[param->getOriginalType().getTypePtr()];
575 }
576 
577 /* Print the header for "method", without newline or semicolon,
578  * using "type_printer" to print argument and return types.
579  *
580  * Print the header of a declaration if this->declarations is set,
581  * otherwise print the header of a method definition.
582  *
583  * This function prints headers for member methods, static methods, and
584  * constructors, either for their declaration or definition.
585  *
586  * Member functions are declared as "const", as they do not change the current
587  * object, but instead create a new object. They always retrieve the first
588  * parameter of the original isl function from the this-pointer of the object,
589  * such that only starting at the second parameter the parameters of the
590  * original function become part of the method's interface.
591  *
592  * A function
593  *
594  * 	__isl_give isl_set *isl_set_intersect(__isl_take isl_set *s1,
595  * 		__isl_take isl_set *s2);
596  *
597  * is translated into:
598  *
599  * 	inline set intersect(set set2) const
600  *
601  * For static functions and constructors all parameters of the original isl
602  * function are exposed.
603  *
604  * Parameters of which no copy is required, are passed
605  * as const reference, which allows the compiler to optimize the parameter
606  * transfer.
607  *
608  * Constructors are marked as explicit using the C++ keyword 'explicit' or as
609  * implicit using a comment in place of the explicit keyword. By annotating
610  * implicit constructors with a comment, users of the interface are made
611  * aware of the potential danger that implicit construction is possible
612  * for these constructors, whereas without a comment not every user would
613  * know that implicit construction is allowed in absence of an explicit keyword.
614  *
615  * Note that in case "method" is a ConversionMethod, the argument returned
616  * by Method::get_param may be different from the original argument.
617  * The name of the argument is, however, derived from the original
618  * function argument.
619  */
print_method_header(const Method & method,const cpp_type_printer & type_printer)620 void cpp_generator::class_printer::print_method_header(
621 	const Method &method, const cpp_type_printer &type_printer)
622 {
623 	string rettype_str = type_printer.return_type(method);
624 
625 	if (declarations) {
626 		os << "  ";
627 
628 		if (method.kind == Method::Kind::static_method)
629 			os << "static ";
630 
631 		os << "inline ";
632 
633 		if (method.kind == Method::Kind::constructor) {
634 			if (generator.is_implicit_conversion(method))
635 				os << "/* implicit */ ";
636 			else
637 				os << "explicit ";
638 		}
639 	}
640 
641 	if (method.kind != Method::Kind::constructor)
642 		os << rettype_str << " ";
643 
644 	if (!declarations)
645 		os << type_printer.class_type(cppstring) << "::";
646 
647 	if (method.kind != Method::Kind::constructor)
648 		os << method.name;
649 	else
650 		os << cppstring;
651 
652 	method.print_cpp_arg_list(os, [&] (int i) {
653 		std::string name = method.fd->getParamDecl(i)->getName().str();
654 		ParmVarDecl *param = method.get_param(i);
655 		QualType type = param->getOriginalType();
656 		string cpptype = type_printer.param(i, type);
657 
658 		if (!method.param_needs_copy(i))
659 			os << "const " << cpptype << " &" << name;
660 		else
661 			os << cpptype << " " << name;
662 	});
663 
664 	if (method.kind == Method::Kind::member_method)
665 		os << " const";
666 }
667 
668 /* Generate the list of argument types for a callback function of
669  * type "type", appearing in argument position "arg".
670  * If "cpp" is set, then generate the C++ type list, otherwise
671  * the C type list.
672  *
673  * For a callback of type
674  *
675  *      isl_stat (*)(__isl_take isl_map *map, void *user)
676  *
677  * the following C++ argument list is generated:
678  *
679  *      map
680  *
681  * The arguments of the callback are considered to appear
682  * after the position of the callback itself.
683  */
generate_callback_args(int arg,QualType type,bool cpp) const684 std::string cpp_type_printer::generate_callback_args(int arg, QualType type,
685 	bool cpp) const
686 {
687 	std::string type_str;
688 	const FunctionProtoType *callback;
689 	int num_params;
690 
691 	callback = generator::extract_prototype(type);
692 	num_params = callback->getNumArgs();
693 	if (cpp)
694 		num_params--;
695 
696 	for (long i = 0; i < num_params; i++) {
697 		QualType type = callback->getArgType(i);
698 
699 		if (cpp)
700 			type_str += param(arg + 1 + i, type);
701 		else
702 			type_str += type.getAsString();
703 
704 		if (!cpp)
705 			type_str += "arg_" + ::to_string(i);
706 
707 		if (i != num_params - 1)
708 			type_str += ", ";
709 	}
710 
711 	return type_str;
712 }
713 
714 /* Generate the full cpp type of a callback function of type "type",
715  * appearing in argument position "arg".
716  *
717  * For a callback of type
718  *
719  *      isl_stat (*)(__isl_take isl_map *map, void *user)
720  *
721  * the following type is generated:
722  *
723  *      std::function<stat(map)>
724  */
generate_callback_type(int arg,QualType type) const725 std::string cpp_type_printer::generate_callback_type(int arg, QualType type)
726 	const
727 {
728 	std::string type_str;
729 	const FunctionProtoType *callback = generator::extract_prototype(type);
730 	QualType return_type = callback->getReturnType();
731 	string rettype_str = param(arg, return_type);
732 
733 	type_str = "std::function<";
734 	type_str += rettype_str;
735 	type_str += "(";
736 	type_str += generate_callback_args(arg, type, true);
737 	type_str += ")>";
738 
739 	return type_str;
740 }
741 
742 /* An array listing functions that must be renamed and the function name they
743  * should be renamed to. We currently rename functions in case their name would
744  * match a reserved C++ keyword, which is not allowed in C++.
745  */
746 static const char *rename_map[][2] = {
747 	{ "union", "unite" },
748 };
749 
750 /* Rename method "name" in case the method name in the C++ bindings should not
751  * match the name in the C bindings. We do this for example to avoid
752  * C++ keywords.
753  */
rename_method(std::string name)754 static std::string rename_method(std::string name)
755 {
756 	for (size_t i = 0; i < sizeof(rename_map) / sizeof(rename_map[0]); i++)
757 		if (name.compare(rename_map[i][0]) == 0)
758 			return rename_map[i][1];
759 
760 	return name;
761 }
762 
763 /* Translate isl class "clazz" to its corresponding C++ type.
764  * Use the name of the type based subclass, if any.
765  */
type2cpp(const isl_class & clazz)766 string cpp_generator::type2cpp(const isl_class &clazz)
767 {
768 	return type2cpp(clazz.subclass_name);
769 }
770 
771 /* Translate type string "type_str" to its C++ name counterpart.
772 */
type2cpp(string type_str)773 string cpp_generator::type2cpp(string type_str)
774 {
775 	return type_str.substr(4);
776 }
777 
778 /* Return the C++ counterpart to the isl_bool type.
779  *
780  * By default, this is simply "bool" since
781  * the exceptional case is handled through exceptions.
782  */
isl_bool() const783 std::string cpp_type_printer::isl_bool() const
784 {
785 	return "bool";
786 }
787 
788 /* Return the C++ counterpart to the isl_stat type.
789  *
790  * By default, this is simply "void" since
791  * the exceptional case is handled through exceptions.
792  */
isl_stat() const793 string cpp_type_printer::isl_stat() const
794 {
795 	return "void";
796 }
797 
798 /* Return the C++ counterpart to the isl_size type.
799  *
800  * By default, this is simply "unsigned" since
801  * the exceptional case is handled through exceptions.
802  */
isl_size() const803 string cpp_type_printer::isl_size() const
804 {
805 	return "unsigned";
806 }
807 
808 /* Return the namespace of the generated C++ bindings.
809  *
810  * By default, this is "isl::".
811  */
isl_namespace() const812 std::string cpp_type_printer::isl_namespace() const
813 {
814 	return "isl::";
815 }
816 
817 /* Return the class type given the C++ name.
818  *
819  * By default, directly use the C++ name.
820  */
class_type(const std::string & cpp_name) const821 std::string cpp_type_printer::class_type(const std::string &cpp_name) const
822 {
823 	return cpp_name;
824 }
825 
826 /* Return the qualified form of the given C++ isl type name appearing
827  * in argument position "arg" (-1 for return type).
828  *
829  * By default, the argument position is ignored.
830  */
qualified(int arg,const std::string & cpp_type) const831 std::string cpp_type_printer::qualified(int arg, const std::string &cpp_type)
832 	const
833 {
834 	return isl_namespace() + cpp_type;
835 }
836 
837 /* Return the C++ counterpart to the given isl type appearing
838  * in argument position "arg" (-1 for return type).
839  */
isl_type(int arg,QualType type) const840 std::string cpp_type_printer::isl_type(int arg, QualType type) const
841 {
842 	auto name = type->getPointeeType().getAsString();
843 	return qualified(arg, cpp_generator::type2cpp(name));
844 }
845 
846 /* Translate parameter or return type "type" to its C++ name counterpart.
847  * "arg" is the position of the argument, or -1 in case of the return type.
848  * If any callback is involved, then the return type and arguments types
849  * of the callback are considered to start at the position of the callback.
850  */
param(int arg,QualType type) const851 std::string cpp_type_printer::param(int arg, QualType type) const
852 {
853 	if (cpp_generator::is_isl_type(type))
854 		return isl_type(arg, type);
855 
856 	if (cpp_generator::is_isl_bool(type))
857 		return isl_bool();
858 
859 	if (cpp_generator::is_isl_stat(type))
860 		return isl_stat();
861 
862 	if (cpp_generator::is_isl_size(type))
863 		return isl_size();
864 
865 	if (type->isIntegerType())
866 		return type.getAsString();
867 
868 	if (cpp_generator::is_string(type))
869 		return "std::string";
870 
871 	if (cpp_generator::is_callback(type))
872 		return generate_callback_type(arg, type);
873 
874 	generator::die("Cannot convert type to C++ type");
875 }
876 
877 /* Check if "subclass_type" is a subclass of "class_type".
878  */
is_subclass(QualType subclass_type,const isl_class & class_type)879 bool cpp_generator::is_subclass(QualType subclass_type,
880 	const isl_class &class_type)
881 {
882 	std::string type_str = subclass_type->getPointeeType().getAsString();
883 	std::vector<std::string> superclasses;
884 	std::vector<const isl_class *> parents;
885 	std::vector<std::string>::iterator ci;
886 
887 	superclasses = generator::find_superclasses(classes[type_str].type);
888 
889 	for (ci = superclasses.begin(); ci < superclasses.end(); ci++)
890 		parents.push_back(&classes[*ci]);
891 
892 	while (!parents.empty()) {
893 		const isl_class *candidate = parents.back();
894 
895 		parents.pop_back();
896 
897 		if (&class_type == candidate)
898 			return true;
899 
900 		superclasses = generator::find_superclasses(candidate->type);
901 
902 		for (ci = superclasses.begin(); ci < superclasses.end(); ci++)
903 			parents.push_back(&classes[*ci]);
904 	}
905 
906 	return false;
907 }
908 
909 /* Check if "cons" is an implicit conversion constructor of class "clazz".
910  *
911  * An implicit conversion constructor is generated in case "cons" has a single
912  * parameter, where the parameter type is a subclass of the class that is
913  * currently being generated.
914  */
is_implicit_conversion(const Method & cons)915 bool cpp_generator::is_implicit_conversion(const Method &cons)
916 {
917 	const auto &clazz = cons.clazz;
918 	ParmVarDecl *param = cons.fd->getParamDecl(0);
919 	QualType type = param->getOriginalType();
920 
921 	int num_params = cons.fd->getNumParams();
922 	if (num_params != 1)
923 		return false;
924 
925 	if (is_isl_type(type) && !is_isl_ctx(type) && is_subclass(type, clazz))
926 		return true;
927 
928 	return false;
929 }
930 
931 /* Get kind of "method" in "clazz".
932  *
933  * Given the declaration of a static or member method, returns its kind.
934  */
get_kind(const isl_class & clazz,FunctionDecl * method)935 static Method::Kind get_kind(const isl_class &clazz, FunctionDecl *method)
936 {
937 	if (generator::is_constructor(method))
938 		return Method::Kind::constructor;
939 	else if (generator::is_static(clazz, method))
940 		return Method::Kind::static_method;
941 	else
942 		return Method::Kind::member_method;
943 }
944 
945 /* Return the callback argument of "fd", if there is any.
946  * Return NULL otherwise.
947  */
find_callback_arg(FunctionDecl * fd)948 static ParmVarDecl *find_callback_arg(FunctionDecl *fd)
949 {
950 	int num_params = fd->getNumParams();
951 
952 	for (int i = 0; i < num_params; ++i) {
953 		ParmVarDecl *param = fd->getParamDecl(i);
954 		if (generator::is_callback(param->getType()))
955 			return param;
956 	}
957 
958 	return NULL;
959 }
960 
961 /* Construct a C++ method object from the class to which is belongs,
962  * the isl function from which it is derived and the method name.
963  *
964  * Perform any renaming of the method that may be required and
965  * determine the type of the method.
966  */
Method(const isl_class & clazz,FunctionDecl * fd,const std::string & name)967 Method::Method(const isl_class &clazz, FunctionDecl *fd,
968 	const std::string &name) :
969 		clazz(clazz), fd(fd), name(rename_method(name)),
970 		kind(get_kind(clazz, fd)),
971 		callback(find_callback_arg(fd))
972 {
973 }
974 
975 /* Construct a C++ method object from the class to which is belongs and
976  * the isl function from which it is derived.
977  *
978  * Obtain the default method name and continue
979  * with the generic constructor.
980  */
Method(const isl_class & clazz,FunctionDecl * fd)981 Method::Method(const isl_class &clazz, FunctionDecl *fd) :
982 	Method(clazz, fd, clazz.method_name(fd))
983 {
984 }
985 
986 /* Return the number of parameters of the corresponding C function.
987  *
988  * If the method has a callback argument, we reduce the number of parameters
989  * that are exposed by one to hide the user pointer from the interface. On
990  * the C++ side no user pointer is needed, as arguments can be forwarded
991  * as part of the std::function argument which specifies the callback function.
992  *
993  * The user pointer is also removed from the number of parameters
994  * of the C function because the pair of callback and user pointer
995  * is considered as a single argument that is printed as a whole
996  * by Method::print_param_use.
997  */
c_num_params() const998 int Method::c_num_params() const
999 {
1000 	return fd->getNumParams() - (callback != NULL);
1001 }
1002 
1003 /* Return the number of parameters of the method
1004  * (including the implicit "this").
1005  *
1006  * By default, it is the same as the number of parameters
1007  * of the corresponding C function.
1008  */
num_params() const1009 int Method::num_params() const
1010 {
1011 	return c_num_params();
1012 }
1013 
1014 /* Print the arguments from "start" (inclusive) to "end" (exclusive)
1015  * as arguments to a method of C function call, using "print_arg"
1016  * to print each individual argument.
1017  */
print_arg_list(std::ostream & os,int start,int end,const std::function<void (int i)> & print_arg)1018 void Method::print_arg_list(std::ostream &os, int start, int end,
1019 	const std::function<void(int i)> &print_arg)
1020 {
1021 	os << "(";
1022 	for (int i = start; i < end; ++i) {
1023 		if (i != start)
1024 			os << ", ";
1025 		print_arg(i);
1026 	}
1027 	os << ")";
1028 }
1029 
1030 /* Print the arguments to the method call, using "print_arg"
1031  * to print each individual argument.
1032  */
print_cpp_arg_list(std::ostream & os,const std::function<void (int i)> & print_arg) const1033 void Method::print_cpp_arg_list(std::ostream &os,
1034 	const std::function<void(int i)> &print_arg) const
1035 {
1036 	int first_param = kind == member_method ? 1 : 0;
1037 	print_arg_list(os, first_param, num_params(), print_arg);
1038 }
1039 
1040 /* Should the parameter at position "pos" be a copy (rather than
1041  * a const reference)?
1042  *
1043  * Strictly speaking, a copy is only needed on isl types that are
1044  * not marked __isl_keep, since those will be release()'d
1045  * by code printed by Method::print_param_use.
1046  *
1047  * However, there may be other arguments such as integer types
1048  * that are more naturally passed as a copy.
1049  * The default is therefore to require a copy, except for
1050  * arguments marked __isl_keep, string arguments or callback arguments.
1051  */
param_needs_copy(int pos) const1052 bool Method::param_needs_copy(int pos) const
1053 {
1054 	ParmVarDecl *param = get_param(pos);
1055 	QualType type = param->getOriginalType();
1056 
1057 	if (generator::keeps(param))
1058 		return false;
1059 	if (generator::is_string(type) || generator::is_callback(type))
1060 		return false;
1061 	return true;
1062 }
1063 
1064 /* Return the method argument at position "pos".
1065  */
get_param(int pos) const1066 clang::ParmVarDecl *Method::get_param(int pos) const
1067 {
1068 	return fd->getParamDecl(pos);
1069 }
1070 
1071 /* Construct a method that performs one or more conversions
1072  * from the original Method (without conversions),
1073  * the name of the type to which "this" should be converted and
1074  * a function for determining the arguments of the constructed method.
1075  */
ConversionMethod(const Method & method,const std::string & this_type,const std::function<clang::ParmVarDecl * (int pos)> & get_param)1076 ConversionMethod::ConversionMethod(const Method &method,
1077 	const std::string &this_type,
1078 	const std::function<clang::ParmVarDecl *(int pos)> &get_param) :
1079 		NoCopyMethod(method), this_type(this_type),
1080 		get_param_fn(get_param)
1081 {
1082 }
1083 
1084 /* Construct a method that only performs a conversion on "this"
1085  * from the original Method (without conversions) and
1086  * the name of the type to which "this" should be converted.
1087  *
1088  * Call the generic constructor with
1089  * a function for determining the arguments of the constructed method
1090  * that performs no conversion.
1091  */
ConversionMethod(const Method & method,const std::string & this_type)1092 ConversionMethod::ConversionMethod(const Method &method,
1093 	const std::string &this_type) :
1094 		ConversionMethod(method, this_type, [this] (int pos) {
1095 			return Method::get_param(pos);
1096 		})
1097 {
1098 }
1099 
1100 /* Construct a method that performs one or more argument conversions
1101  * from the original Method (without conversions) and
1102  * a function for determining the arguments of the constructed method.
1103  *
1104  * Call the generic constructor with method.clazz.name as "this" type,
1105  * indicating that "this" should not be converted.
1106  */
ConversionMethod(const Method & method,const std::function<clang::ParmVarDecl * (int pos)> & get_param)1107 ConversionMethod::ConversionMethod(const Method &method,
1108 	const std::function<clang::ParmVarDecl *(int pos)> &get_param) :
1109 		ConversionMethod(method, method.clazz.name, get_param)
1110 {
1111 }
1112 
1113 /* Should the parameter at position "pos" be a copy (rather than
1114  * a const reference)?
1115  *
1116  * Parameters of isl type do not need to be a copy.
1117  * For other types, use the same defaults as Method.
1118  */
param_needs_copy(int pos) const1119 bool NoCopyMethod::param_needs_copy(int pos) const
1120 {
1121 	ParmVarDecl *param = get_param(pos);
1122 	QualType type = param->getOriginalType();
1123 
1124 	if (generator::is_isl_type(type))
1125 		return false;
1126 
1127 	return Method::param_needs_copy(pos);
1128 }
1129 
1130 /* Return the method argument at position "pos".
1131  *
1132  * Call get_param_fn to determine this argument.
1133  */
get_param(int pos) const1134 clang::ParmVarDecl *ConversionMethod::get_param(int pos) const
1135 {
1136 	return get_param_fn(pos);
1137 }
1138 
1139 /* Print a call to the method (without the arguments),
1140  * with "ns" the namespace of the generated C++ bindings.
1141  *
1142  * If "this_type" is different from the name of the class of the method,
1143  * then "this" needs to be converted to that type before
1144  * the call is performed.
1145  */
print_call(std::ostream & os,const std::string & ns) const1146 void ConversionMethod::print_call(std::ostream &os, const std::string &ns) const
1147 {
1148 	if (clazz.name == this_type) {
1149 		os << "this->";
1150 	} else {
1151 		auto cpp_type = ns + cpp_generator::type2cpp(this_type);
1152 		os << cpp_type << "(*this).";
1153 	}
1154 	os << name;
1155 }
1156 
1157 /* Construct an object representing a C++ method for setting an enum
1158  * from the class to which is belongs,
1159  * the isl function from which it is derived and the method and enum names.
1160  */
EnumMethod(const isl_class & clazz,FunctionDecl * fd,const std::string & method_name,const std::string & enum_name)1161 EnumMethod::EnumMethod(const isl_class &clazz, FunctionDecl *fd,
1162 	const std::string &method_name, const std::string &enum_name) :
1163 		Method(clazz, fd, method_name), enum_name(enum_name)
1164 {
1165 }
1166 
1167 /* Print the use of the argument at position "pos" to "os".
1168  *
1169  * If the position is beyond the number of method arguments,
1170  * then it corresponds to the enum value corresponding to this EnumMethod.
1171  * Otherwise, delegate to Method::print_param_use.
1172  */
print_param_use(ostream & os,int pos) const1173 void EnumMethod::print_param_use(ostream &os, int pos) const
1174 {
1175 	if (pos == num_params())
1176 		os << enum_name;
1177 	else
1178 		Method::print_param_use(os, pos);
1179 }
1180 
1181 /* Return the number of parameters of the method
1182  * (including the implicit "this").
1183  *
1184  * The last argument of the C function does not appear in the method call,
1185  * because it is replaced by a break-up into several methods.
1186  */
num_params() const1187 int EnumMethod::num_params() const
1188 {
1189 	return Method::num_params() - 1;
1190 }
1191 
1192 /* Initialize a class method printer from the stream onto which the methods
1193  * are printed, the class method description and the C++ interface generator.
1194  */
class_printer(std::ostream & os,const isl_class & clazz,cpp_generator & generator,bool declarations)1195 cpp_generator::class_printer::class_printer(std::ostream &os,
1196 		const isl_class &clazz, cpp_generator &generator,
1197 		bool declarations) :
1198 	os(os), clazz(clazz), cppstring(type2cpp(clazz)), generator(generator),
1199 	declarations(declarations)
1200 {
1201 }
1202