generator.cc 24.1 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856
/*
 * Copyright 2011,2015 Sven Verdoolaege. 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 SVEN VERDOOLAEGE ''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 SVEN VERDOOLAEGE 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
 * Sven Verdoolaege.
 */

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>

#include <clang/AST/Attr.h>
#include <clang/Basic/SourceManager.h>

#include "isl_config.h"
#include "extract_interface.h"
#include "generator.h"

const char *isl_class::get_prefix = "get_";
const char *isl_class::set_callback_prefix = "set_";

/* Should "method" be considered to be a static method?
 * That is, is the first argument something other than
 * an instance of the class?
 */
bool isl_class::is_static(FunctionDecl *method) const
{
	ParmVarDecl *param;
	QualType type;

	if (method->getNumParams() < 1)
		return true;

	param = method->getParamDecl(0);
	type = param->getOriginalType();
	if (!generator::is_isl_type(type))
		return true;
	return generator::extract_type(type) != name;
}

/* Should "method" be considered to be a static method?
 * That is, is the first argument something other than
 * an instance of the class?
 */
bool generator::is_static(const isl_class &clazz, FunctionDecl *method)
{
	return clazz.is_static(method);
}

/* Does "fd" modify an object of "clazz"?
 * That is, is it an object method that takes the object and
 * returns (gives) an object of the same type?
 */
bool generator::is_mutator(const isl_class &clazz, FunctionDecl *fd)
{
	ParmVarDecl *param;
	QualType type, return_type;

	if (fd->getNumParams() < 1)
		return false;
	if (is_static(clazz, fd))
		return false;

	if (!gives(fd))
		return false;
	param = fd->getParamDecl(0);
	if (!takes(param))
		return false;
	type = param->getOriginalType();
	return_type = fd->getReturnType();
	return return_type == type;
}

/* Find the FunctionDecl with name "name",
 * returning NULL if there is no such FunctionDecl.
 * If "required" is set, then error out if no FunctionDecl can be found.
 */
FunctionDecl *generator::find_by_name(const string &name, bool required)
{
	map<string, FunctionDecl *>::iterator i;

	i = functions_by_name.find(name);
	if (i != functions_by_name.end())
		return i->second;
	if (required)
		die("No " + name + " function found");
	return NULL;
}

/* List of conversion functions that are used to automatically convert
 * the second argument of the conversion function to its function result.
 */
const std::set<std::string> generator::automatic_conversion_functions = {
	"isl_id_read_from_str",
	"isl_val_int_from_si",
};

/* Extract information about the automatic conversion function "fd",
 * storing the results in this->conversions.
 *
 * A function used for automatic conversion has exactly two arguments,
 * an isl_ctx and a non-isl object, and it returns an isl object.
 * Store a mapping from the isl object return type
 * to the non-isl object source type.
 */
void generator::extract_automatic_conversion(FunctionDecl *fd)
{
	QualType return_type = fd->getReturnType();
	const Type *type = return_type.getTypePtr();

	if (fd->getNumParams() != 2)
		die("Expecting two arguments");
	if (!is_isl_ctx(fd->getParamDecl(0)->getOriginalType()))
		die("Expecting isl_ctx first argument");
	if (!is_isl_type(return_type))
		die("Expecting isl object return type");
	conversions[type] = fd->getParamDecl(1);
}

/* Extract information about all automatic conversion functions
 * for the given class, storing the results in this->conversions.
 *
 * In particular, look through all exported constructors for the class and
 * check if any of them is explicitly marked as a conversion function.
 */
void generator::extract_class_automatic_conversions(const isl_class &clazz)
{
	const function_set &constructors = clazz.constructors;
	function_set::iterator fi;

	for (fi = constructors.begin(); fi != constructors.end(); ++fi) {
		FunctionDecl *fd = *fi;
		string name = fd->getName().str();
		if (automatic_conversion_functions.count(name) != 0)
			extract_automatic_conversion(fd);
	}
}

/* Extract information about all automatic conversion functions,
 * storing the results in this->conversions.
 */
void generator::extract_automatic_conversions()
{
	map<string, isl_class>::iterator ci;

	for (ci = classes.begin(); ci != classes.end(); ++ci)
		extract_class_automatic_conversions(ci->second);
}

/* Add a subclass derived from "decl" called "sub_name" to the set of classes,
 * keeping track of the _to_str, _copy and _free functions, if any, separately.
 * "sub_name" is either the name of the class itself or
 * the name of a type based subclass.
 * If the class is a proper subclass, then "super_name" is the name
 * of its immediate superclass.
 */
void generator::add_subclass(RecordDecl *decl, const string &super_name,
	const string &sub_name)
{
	string name = decl->getName().str();

	classes[sub_name].name = name;
	classes[sub_name].superclass_name = super_name;
	classes[sub_name].subclass_name = sub_name;
	classes[sub_name].type = decl;
	classes[sub_name].fn_to_str = find_by_name(name + "_to_str", false);
	classes[sub_name].fn_copy = find_by_name(name + "_copy", true);
	classes[sub_name].fn_free = find_by_name(name + "_free", true);
}

/* Add a class derived from "decl" to the set of classes,
 * keeping track of the _to_str, _copy and _free functions, if any, separately.
 */
void generator::add_class(RecordDecl *decl)
{
	return add_subclass(decl, "", decl->getName().str());
}

/* Given a function "fn_type" that returns the subclass type
 * of a C object, create subclasses for each of the (non-negative)
 * return values.
 *
 * The function "fn_type" is also stored in the superclass,
 * along with all pairs of type values and subclass names.
 */
void generator::add_type_subclasses(FunctionDecl *fn_type)
{
	QualType return_type = fn_type->getReturnType();
	const EnumType *enum_type = return_type->getAs<EnumType>();
	EnumDecl *decl = enum_type->getDecl();
	isl_class *c = method2class(fn_type);
	DeclContext::decl_iterator i;

	c->fn_type = fn_type;
	for (i = decl->decls_begin(); i != decl->decls_end(); ++i) {
		EnumConstantDecl *ecd = dyn_cast<EnumConstantDecl>(*i);
		int val = (int) ecd->getInitVal().getSExtValue();
		string name = ecd->getNameAsString();

		if (val < 0)
			continue;
		c->type_subclasses[val] = name;
		add_subclass(c->type, c->subclass_name, name);
	}
}

/* Add information about the enum values in "decl", set by "fd",
 * to c->set_enums. "prefix" is the prefix of the generated method names.
 * In particular, it has the name of the enum type removed.
 *
 * In particular, for each non-negative enum value, keep track of
 * the value, the name and the corresponding method name.
 */
static void add_set_enum(isl_class *c, const string &prefix, EnumDecl *decl,
	FunctionDecl *fd)
{
	DeclContext::decl_iterator i;

	for (i = decl->decls_begin(); i != decl->decls_end(); ++i) {
		EnumConstantDecl *ecd = dyn_cast<EnumConstantDecl>(*i);
		int val = (int) ecd->getInitVal().getSExtValue();
		string name = ecd->getNameAsString();
		string method_name;

		if (val < 0)
			continue;
		method_name = prefix + name.substr(4);
		c->set_enums[fd].push_back(set_enum(val, name, method_name));
	}
}

/* Check if "fd" sets an enum value and, if so, add information
 * about the enum values to c->set_enums.
 *
 * A function is considered to set an enum value if:
 * - the function returns an object of the same type
 * - the last argument is of type enum
 * - the name of the function ends with the name of the enum
 */
static bool handled_sets_enum(isl_class *c, FunctionDecl *fd)
{
	unsigned n;
	ParmVarDecl *param;
	const EnumType *enum_type;
	EnumDecl *decl;
	string enum_name;
	string fd_name;
	string prefix;
	size_t pos;

	if (!generator::is_mutator(*c, fd))
		return false;
	n = fd->getNumParams();
	if (n < 2)
		return false;
	param = fd->getParamDecl(n - 1);
	enum_type = param->getType()->getAs<EnumType>();
	if (!enum_type)
		return false;
	decl = enum_type->getDecl();
	enum_name = decl->getName().str();
	enum_name = enum_name.substr(4);
	fd_name = c->method_name(fd);
	pos = fd_name.find(enum_name);
	if (pos == std::string::npos)
		return false;
	prefix = fd_name.substr(0, pos);

	add_set_enum(c, prefix, decl, fd);

	return true;
}

/* Return the callback argument of a function setting
 * a persistent callback.
 * This callback is in the second argument (position 1).
 */
ParmVarDecl *generator::persistent_callback_arg(FunctionDecl *fd)
{
	return fd->getParamDecl(1);
}

/* Does the given function set a persistent callback?
 * The following heuristics are used to determine this property:
 * - the function returns an object of the same type
 * - its name starts with "set_"
 * - it has exactly three arguments
 * - the second (position 1) of which is a callback
 */
static bool sets_persistent_callback(isl_class *c, FunctionDecl *fd)
{
	ParmVarDecl *param;

	if (!generator::is_mutator(*c, fd))
		return false;
	if (fd->getNumParams() != 3)
		return false;
	param = generator::persistent_callback_arg(fd);
	if (!generator::is_callback(param->getType()))
		return false;
	return prefixcmp(c->method_name(fd).c_str(),
			 c->set_callback_prefix) == 0;
}

/* Does this function take any enum arguments?
 */
static bool takes_enums(FunctionDecl *fd)
{
	unsigned n;

	n = fd->getNumParams();
	for (unsigned i = 0; i < n; ++i) {
		ParmVarDecl *param = fd->getParamDecl(i);
		if (param->getType()->getAs<EnumType>())
			return true;
	}
	return false;
}

/* Sorting function that places declaration of functions
 * with a shorter name first.
 */
static bool less_name(const FunctionDecl *a, const FunctionDecl *b)
{
	return a->getName().size() < b->getName().size();
}

/* Collect all functions that belong to a certain type, separating
 * constructors from methods that set an enum value,
 * methods that set a persistent callback and
 * from regular methods, while keeping track of the _to_str,
 * _copy and _free functions, if any, separately.
 * Methods that accept any enum arguments that are not specifically handled
 * are not supported.
 * If there are any overloaded
 * functions, then they are grouped based on their name after removing the
 * argument type suffix.
 * Check for functions that describe subclasses before considering
 * any other functions in order to be able to detect those other
 * functions as belonging to the subclasses.
 * Sort the names of the functions based on their lengths
 * to ensure that nested subclasses are handled later.
 */
generator::generator(SourceManager &SM, set<RecordDecl *> &exported_types,
	set<FunctionDecl *> exported_functions, set<FunctionDecl *> functions) :
	SM(SM)
{
	set<FunctionDecl *>::iterator in;
	set<RecordDecl *>::iterator it;
	vector<FunctionDecl *> type_subclasses;
	vector<FunctionDecl *>::iterator iv;

	for (in = functions.begin(); in != functions.end(); ++in) {
		FunctionDecl *decl = *in;
		functions_by_name[decl->getName().str()] = decl;
	}

	for (it = exported_types.begin(); it != exported_types.end(); ++it)
		add_class(*it);

	for (in = exported_functions.begin(); in != exported_functions.end();
	     ++in) {
		if (is_subclass(*in))
			type_subclasses.push_back(*in);
	}
	std::sort(type_subclasses.begin(), type_subclasses.end(), &less_name);
	for (iv = type_subclasses.begin(); iv != type_subclasses.end(); ++iv) {
		add_type_subclasses(*iv);
	}

	for (in = exported_functions.begin(); in != exported_functions.end();
	     ++in) {
		FunctionDecl *method = *in;
		isl_class *c;

		if (is_subclass(method))
			continue;

		c = method2class(method);
		if (!c)
			continue;
		if (is_constructor(method)) {
			c->constructors.insert(method);
		} else if (handled_sets_enum(c, method)) {
		} else if (sets_persistent_callback(c, method)) {
			c->persistent_callbacks.insert(method);
		} else if (takes_enums(method)) {
			std::string name = method->getName().str();
			die(name + " has unhandled enum argument");
		} else {
			string fullname = c->name_without_type_suffixes(method);
			c->methods[fullname].insert(method);
		}
	}

	extract_automatic_conversions();
}

/* Print error message "msg" and abort.
 */
void generator::die(const char *msg)
{
	fprintf(stderr, "%s\n", msg);
	abort();
}

/* Print error message "msg" and abort.
 */
void generator::die(string msg)
{
	die(msg.c_str());
}

/* Return a sequence of the types of which the given type declaration is
 * marked as being a subtype.
 * The order of the types is the opposite of the order in which they
 * appear in the source.  In particular, the first annotation
 * is the one that is closest to the annotated type and the corresponding
 * type is then also the first that will appear in the sequence of types.
 * This is also the order in which the annotations appear
 * in the AttrVec returned by Decl::getAttrs() in older versions of clang.
 * In newer versions of clang, the order is that in which
 * the attribute appears in the source.
 * Use the position of the "isl_export" attribute to determine
 * whether this is an old (with reversed order) or a new version.
 * The "isl_export" attribute is automatically added
 * after each "isl_subclass" attribute.  If it appears in the list before
 * any "isl_subclass" is encountered, then this must be a reversed list.
 */
std::vector<string> generator::find_superclasses(Decl *decl)
{
	vector<string> super;
	bool reversed = false;

	if (!decl->hasAttrs())
		return super;

	string sub = "isl_subclass";
	size_t len = sub.length();
	AttrVec attrs = decl->getAttrs();
	for (AttrVec::const_iterator i = attrs.begin(); i != attrs.end(); ++i) {
		const AnnotateAttr *ann = dyn_cast<AnnotateAttr>(*i);
		if (!ann)
			continue;
		string s = ann->getAnnotation().str();
		if (s == "isl_export" && super.size() == 0)
			reversed = true;
		if (s.substr(0, len) == sub) {
			s = s.substr(len + 1, s.length() - len  - 2);
			if (reversed)
				super.push_back(s);
			else
				super.insert(super.begin(), s);
		}
	}

	return super;
}

/* Is "decl" marked as describing subclasses?
 */
bool generator::is_subclass(FunctionDecl *decl)
{
	return find_superclasses(decl).size() > 0;
}

/* Is decl marked as being part of an overloaded method?
 */
bool generator::is_overload(Decl *decl)
{
	return has_annotation(decl, "isl_overload");
}

/* Is decl marked as a constructor?
 */
bool generator::is_constructor(Decl *decl)
{
	return has_annotation(decl, "isl_constructor");
}

/* Is decl marked as consuming a reference?
 */
bool generator::takes(Decl *decl)
{
	return has_annotation(decl, "isl_take");
}

/* Is decl marked as preserving a reference?
 */
bool generator::keeps(Decl *decl)
{
	return has_annotation(decl, "isl_keep");
}

/* Is decl marked as returning a reference that is required to be freed.
 */
bool generator::gives(Decl *decl)
{
	return has_annotation(decl, "isl_give");
}

/* Return the class that has a name that best matches the initial part
 * of the name of function "fd" or NULL if no such class could be found.
 */
isl_class *generator::method2class(FunctionDecl *fd)
{
	string best;
	map<string, isl_class>::iterator ci;
	string name = fd->getNameAsString();

	for (ci = classes.begin(); ci != classes.end(); ++ci) {
		size_t len = ci->first.length();
		if (len > best.length() && name.substr(0, len) == ci->first &&
		    name[len] == '_')
			best = ci->first;
	}

	if (classes.find(best) == classes.end()) {
		cerr << "Unable to find class of " << name << endl;
		return NULL;
	}

	return &classes[best];
}

/* Is "type" the type "isl_ctx *"?
 */
bool generator::is_isl_ctx(QualType type)
{
	if (!type->isPointerType())
		return false;
	type = type->getPointeeType();
	if (type.getAsString() != "isl_ctx")
		return false;

	return true;
}

/* Is the first argument of "fd" of type "isl_ctx *"?
 */
bool generator::first_arg_is_isl_ctx(FunctionDecl *fd)
{
	ParmVarDecl *param;

	if (fd->getNumParams() < 1)
		return false;

	param = fd->getParamDecl(0);
	return is_isl_ctx(param->getOriginalType());
}

namespace {

struct ClangAPI {
	/* Return the first location in the range returned by
	 * clang::SourceManager::getImmediateExpansionRange.
	 * Older versions of clang return a pair of SourceLocation objects.
	 * More recent versions return a CharSourceRange.
	 */
	static SourceLocation range_begin(
			const std::pair<SourceLocation,SourceLocation> &p) {
		return p.first;
	}
	static SourceLocation range_begin(const CharSourceRange &range) {
		return range.getBegin();
	}
};

}

/* Does the callback argument "param" take its argument at position "pos"?
 *
 * The memory management annotations of arguments to function pointers
 * are not recorded by clang, so the information cannot be extracted
 * from the type of "param".
 * Instead, go to the location in the source where the callback argument
 * is declared, look for the right argument of the callback itself and
 * then check if it has an "__isl_take" memory management annotation.
 *
 * If the return value of the function has a memory management annotation,
 * then the spelling of "param" will point to the spelling
 * of this memory management annotation.  Since the macro is defined
 * on the command line (in main), this location does not have a file entry.
 * In this case, move up one level in the macro expansion to the location
 * where the memory management annotation is used.
 */
bool generator::callback_takes_argument(ParmVarDecl *param,
	int pos)
{
	SourceLocation loc;
	const char *s, *end, *next;
	bool takes, keeps;

	loc = param->getSourceRange().getBegin();
	if (!SM.getFileEntryForID(SM.getFileID(SM.getSpellingLoc(loc))))
		loc = ClangAPI::range_begin(SM.getImmediateExpansionRange(loc));
	s = SM.getCharacterData(loc);
	if (!s)
		die("No character data");
	s = strchr(s, '(');
	if (!s)
		die("Cannot find function pointer");
	s = strchr(s + 1, '(');
	if (!s)
		die("Cannot find function pointer arguments");
	end = strchr(s + 1, ')');
	if (!end)
		die("Cannot find end of function pointer arguments");
	while (pos-- > 0) {
		s = strchr(s + 1, ',');
		if (!s || s > end)
			die("Cannot find function pointer argument");
	}
	next = strchr(s + 1, ',');
	if (next && next < end)
		end = next;
	s = strchr(s + 1, '_');
	if (!s || s > end)
		die("Cannot find function pointer argument annotation");
	takes = prefixcmp(s, "__isl_take") == 0;
	keeps = prefixcmp(s, "__isl_keep") == 0;
	if (!takes && !keeps)
		die("Cannot find function pointer argument annotation");

	return takes;
}

/* Is "type" that of a pointer to an isl_* structure?
 */
bool generator::is_isl_type(QualType type)
{
	if (type->isPointerType()) {
		string s;

		type = type->getPointeeType();
		if (type->isFunctionType())
			return false;
		s = type.getAsString();
		return s.substr(0, 4) == "isl_";
	}

	return false;
}

/* Is "type" one of the integral types with a negative value
 * indicating an error condition?
 */
bool generator::is_isl_neg_error(QualType type)
{
	return is_isl_bool(type) || is_isl_stat(type) || is_isl_size(type);
}

/* Is "type" the primitive type with the given name?
 */
static bool is_isl_primitive(QualType type, const char *name)
{
	string s;

	if (type->isPointerType())
		return false;

	s = type.getAsString();
	return s == name;
}

/* Is "type" the type isl_bool?
 */
bool generator::is_isl_bool(QualType type)
{
	return is_isl_primitive(type, "isl_bool");
}

/* Is "type" the type isl_stat?
 */
bool generator::is_isl_stat(QualType type)
{
	return is_isl_primitive(type, "isl_stat");
}

/* Is "type" the type isl_size?
 */
bool generator::is_isl_size(QualType type)
{
	return is_isl_primitive(type, "isl_size");
}

/* Is "type" that of a pointer to a function?
 */
bool generator::is_callback(QualType type)
{
	if (!type->isPointerType())
		return false;
	type = type->getPointeeType();
	return type->isFunctionType();
}

/* Is "type" that of "char *" of "const char *"?
 */
bool generator::is_string(QualType type)
{
	if (type->isPointerType()) {
		string s = type->getPointeeType().getAsString();
		return s == "const char" || s == "char";
	}

	return false;
}

/* Is "type" that of "long"?
 */
bool generator::is_long(QualType type)
{
	const BuiltinType *builtin = type->getAs<BuiltinType>();
	return builtin && builtin->getKind() == BuiltinType::Long;
}

/* Is "type" that of "unsigned int"?
 */
static bool is_unsigned_int(QualType type)
{
	const BuiltinType *builtin = type->getAs<BuiltinType>();
	return builtin && builtin->getKind() == BuiltinType::UInt;
}

/* Return the name of the type that "type" points to.
 * The input "type" is assumed to be a pointer type.
 */
string generator::extract_type(QualType type)
{
	if (type->isPointerType())
		return type->getPointeeType().getAsString();
	die("Cannot extract type from non-pointer type");
}

/* Given the type of a function pointer, return the corresponding
 * function prototype.
 */
const FunctionProtoType *generator::extract_prototype(QualType type)
{
	return type->getPointeeType()->getAs<FunctionProtoType>();
}

/* Return the function name suffix for the type of "param".
 *
 * If the type of "param" is an isl object type,
 * then the suffix is the name of the type with the "isl" prefix removed,
 * but keeping the "_".
 * If the type is an unsigned integer, then the type suffix is "_ui".
 */
static std::string type_suffix(ParmVarDecl *param)
{
	QualType type;

	type = param->getOriginalType();
	if (generator::is_isl_type(type))
		return generator::extract_type(type).substr(3);
	else if (is_unsigned_int(type))
		return "_ui";
	generator::die("Unsupported type suffix");
}

/* If "suffix" is a suffix of "s", then return "s" with the suffix removed.
 * Otherwise, simply return "s".
 */
static std::string drop_suffix(const std::string &s, const std::string &suffix)
{
	size_t len, suffix_len;

	len = s.length();
	suffix_len = suffix.length();

	if (len >= suffix_len && s.substr(len - suffix_len) == suffix)
		return s.substr(0, len - suffix_len);
	else
		return s;
}

/* If "method" is overloaded, then return its name with the suffixes
 * corresponding to the types of the final arguments removed.
 * Otherwise, simply return the name of the function.
 * Start from the final argument and keep removing suffixes
 * matching arguments, independently of whether previously considered
 * arguments matched.
 */
string isl_class::name_without_type_suffixes(FunctionDecl *method)
{
	int num_params;
	string name;

	name = method->getName().str();
	if (!generator::is_overload(method))
		return name;

	num_params = method->getNumParams();
	for (int i = num_params - 1; i >= 0; --i) {
		ParmVarDecl *param;
		string type;

		param = method->getParamDecl(i);
		type = type_suffix(param);

		name = drop_suffix(name, type);
	}

	return name;
}

/* Is function "fd" with the given name a "get" method?
 *
 * A "get" method is an instance method
 * with a name that starts with the get method prefix.
 */
bool isl_class::is_get_method_name(FunctionDecl *fd, const string &name) const
{
	return !is_static(fd) && prefixcmp(name.c_str(), get_prefix) == 0;
}

/* Extract the method name corresponding to "fd".
 *
 * If "fd" is a "get" method, then drop the "get" method prefix.
 */
string isl_class::method_name(FunctionDecl *fd) const
{
      string base = base_method_name(fd);

      if (is_get_method_name(fd, base))
	      return base.substr(strlen(get_prefix));
      return base;
}