2 * Copyright 2011,2015 Sven Verdoolaege. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
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.
16 * THIS SOFTWARE IS PROVIDED BY SVEN VERDOOLAEGE ''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 SVEN VERDOOLAEGE 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.
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
39 #include <clang/AST/Attr.h>
40 #include <clang/Basic/SourceManager.h>
42 #include "isl_config.h"
43 #include "extract_interface.h"
44 #include "generator.h"
46 const char *isl_class::get_prefix
= "get_";
47 const char *isl_class::set_callback_prefix
= "set_";
49 /* Should "method" be considered to be a static method?
50 * That is, is the first argument something other than
51 * an instance of the class?
53 bool isl_class::is_static(FunctionDecl
*method
) const
58 if (method
->getNumParams() < 1)
61 param
= method
->getParamDecl(0);
62 type
= param
->getOriginalType();
63 if (!generator::is_isl_type(type
))
65 return generator::extract_type(type
) != name
;
68 /* Should "method" be considered to be a static method?
69 * That is, is the first argument something other than
70 * an instance of the class?
72 bool generator::is_static(const isl_class
&clazz
, FunctionDecl
*method
)
74 return clazz
.is_static(method
);
77 /* Does "fd" modify an object of "clazz"?
78 * That is, is it an object method that takes the object and
79 * returns (gives) an object of the same type?
81 bool generator::is_mutator(const isl_class
&clazz
, FunctionDecl
*fd
)
84 QualType type
, return_type
;
86 if (fd
->getNumParams() < 1)
88 if (is_static(clazz
, fd
))
93 param
= fd
->getParamDecl(0);
96 type
= param
->getOriginalType();
97 return_type
= fd
->getReturnType();
98 return return_type
== type
;
101 /* Find the FunctionDecl with name "name",
102 * returning NULL if there is no such FunctionDecl.
103 * If "required" is set, then error out if no FunctionDecl can be found.
105 FunctionDecl
*generator::find_by_name(const string
&name
, bool required
)
107 map
<string
, FunctionDecl
*>::iterator i
;
109 i
= functions_by_name
.find(name
);
110 if (i
!= functions_by_name
.end())
113 die("No " + name
+ " function found");
117 /* List of conversion functions that are used to automatically convert
118 * the second argument of the conversion function to its function result.
120 const std::set
<std::string
> generator::automatic_conversion_functions
= {
121 "isl_id_read_from_str",
122 "isl_val_int_from_si",
125 /* Extract information about the automatic conversion function "fd",
126 * storing the results in this->conversions.
128 * A function used for automatic conversion has exactly two arguments,
129 * an isl_ctx and a non-isl object, and it returns an isl object.
130 * Store a mapping from the isl object return type
131 * to the non-isl object source type.
133 void generator::extract_automatic_conversion(FunctionDecl
*fd
)
135 QualType return_type
= fd
->getReturnType();
136 const Type
*type
= return_type
.getTypePtr();
138 if (fd
->getNumParams() != 2)
139 die("Expecting two arguments");
140 if (!is_isl_ctx(fd
->getParamDecl(0)->getOriginalType()))
141 die("Expecting isl_ctx first argument");
142 if (!is_isl_type(return_type
))
143 die("Expecting isl object return type");
144 conversions
[type
] = fd
->getParamDecl(1);
147 /* Extract information about all automatic conversion functions
148 * for the given class, storing the results in this->conversions.
150 * In particular, look through all exported constructors for the class and
151 * check if any of them is explicitly marked as a conversion function.
153 void generator::extract_class_automatic_conversions(const isl_class
&clazz
)
155 const function_set
&constructors
= clazz
.constructors
;
156 function_set::iterator fi
;
158 for (fi
= constructors
.begin(); fi
!= constructors
.end(); ++fi
) {
159 FunctionDecl
*fd
= *fi
;
160 string name
= fd
->getName().str();
161 if (automatic_conversion_functions
.count(name
) != 0)
162 extract_automatic_conversion(fd
);
166 /* Extract information about all automatic conversion functions,
167 * storing the results in this->conversions.
169 void generator::extract_automatic_conversions()
171 map
<string
, isl_class
>::iterator ci
;
173 for (ci
= classes
.begin(); ci
!= classes
.end(); ++ci
)
174 extract_class_automatic_conversions(ci
->second
);
177 /* Add a subclass derived from "decl" called "sub_name" to the set of classes,
178 * keeping track of the _to_str, _copy and _free functions, if any, separately.
179 * "sub_name" is either the name of the class itself or
180 * the name of a type based subclass.
181 * If the class is a proper subclass, then "super_name" is the name
182 * of its immediate superclass.
184 void generator::add_subclass(RecordDecl
*decl
, const string
&super_name
,
185 const string
&sub_name
)
187 string name
= decl
->getName().str();
189 classes
[sub_name
].name
= name
;
190 classes
[sub_name
].superclass_name
= super_name
;
191 classes
[sub_name
].subclass_name
= sub_name
;
192 classes
[sub_name
].type
= decl
;
193 classes
[sub_name
].fn_to_str
= find_by_name(name
+ "_to_str", false);
194 classes
[sub_name
].fn_copy
= find_by_name(name
+ "_copy", true);
195 classes
[sub_name
].fn_free
= find_by_name(name
+ "_free", true);
198 /* Add a class derived from "decl" to the set of classes,
199 * keeping track of the _to_str, _copy and _free functions, if any, separately.
201 void generator::add_class(RecordDecl
*decl
)
203 return add_subclass(decl
, "", decl
->getName().str());
206 /* Given a function "fn_type" that returns the subclass type
207 * of a C object, create subclasses for each of the (non-negative)
210 * The function "fn_type" is also stored in the superclass,
211 * along with all pairs of type values and subclass names.
213 void generator::add_type_subclasses(FunctionDecl
*fn_type
)
215 QualType return_type
= fn_type
->getReturnType();
216 const EnumType
*enum_type
= return_type
->getAs
<EnumType
>();
217 EnumDecl
*decl
= enum_type
->getDecl();
218 isl_class
*c
= method2class(fn_type
);
219 DeclContext::decl_iterator i
;
221 c
->fn_type
= fn_type
;
222 for (i
= decl
->decls_begin(); i
!= decl
->decls_end(); ++i
) {
223 EnumConstantDecl
*ecd
= dyn_cast
<EnumConstantDecl
>(*i
);
224 int val
= (int) ecd
->getInitVal().getSExtValue();
225 string name
= ecd
->getNameAsString();
229 c
->type_subclasses
[val
] = name
;
230 add_subclass(c
->type
, c
->subclass_name
, name
);
234 /* Add information about the enum values in "decl", set by "fd",
235 * to c->set_enums. "prefix" is the prefix of the generated method names.
236 * In particular, it has the name of the enum type removed.
238 * In particular, for each non-negative enum value, keep track of
239 * the value, the name and the corresponding method name.
241 static void add_set_enum(isl_class
*c
, const string
&prefix
, EnumDecl
*decl
,
244 DeclContext::decl_iterator i
;
246 for (i
= decl
->decls_begin(); i
!= decl
->decls_end(); ++i
) {
247 EnumConstantDecl
*ecd
= dyn_cast
<EnumConstantDecl
>(*i
);
248 int val
= (int) ecd
->getInitVal().getSExtValue();
249 string name
= ecd
->getNameAsString();
254 method_name
= prefix
+ name
.substr(4);
255 c
->set_enums
[fd
].push_back(set_enum(val
, name
, method_name
));
259 /* Check if "fd" sets an enum value and, if so, add information
260 * about the enum values to c->set_enums.
262 * A function is considered to set an enum value if:
263 * - the function returns an object of the same type
264 * - the last argument is of type enum
265 * - the name of the function ends with the name of the enum
267 static bool handled_sets_enum(isl_class
*c
, FunctionDecl
*fd
)
271 const EnumType
*enum_type
;
278 if (!generator::is_mutator(*c
, fd
))
280 n
= fd
->getNumParams();
283 param
= fd
->getParamDecl(n
- 1);
284 enum_type
= param
->getType()->getAs
<EnumType
>();
287 decl
= enum_type
->getDecl();
288 enum_name
= decl
->getName().str();
289 enum_name
= enum_name
.substr(4);
290 fd_name
= c
->method_name(fd
);
291 pos
= fd_name
.find(enum_name
);
292 if (pos
== std::string::npos
)
294 prefix
= fd_name
.substr(0, pos
);
296 add_set_enum(c
, prefix
, decl
, fd
);
301 /* Return the callback argument of a function setting
302 * a persistent callback.
303 * This callback is in the second argument (position 1).
305 ParmVarDecl
*generator::persistent_callback_arg(FunctionDecl
*fd
)
307 return fd
->getParamDecl(1);
310 /* Does the given function set a persistent callback?
311 * The following heuristics are used to determine this property:
312 * - the function returns an object of the same type
313 * - its name starts with "set_"
314 * - it has exactly three arguments
315 * - the second (position 1) of which is a callback
317 static bool sets_persistent_callback(isl_class
*c
, FunctionDecl
*fd
)
321 if (!generator::is_mutator(*c
, fd
))
323 if (fd
->getNumParams() != 3)
325 param
= generator::persistent_callback_arg(fd
);
326 if (!generator::is_callback(param
->getType()))
328 return prefixcmp(c
->method_name(fd
).c_str(),
329 c
->set_callback_prefix
) == 0;
332 /* Does this function take any enum arguments?
334 static bool takes_enums(FunctionDecl
*fd
)
338 n
= fd
->getNumParams();
339 for (unsigned i
= 0; i
< n
; ++i
) {
340 ParmVarDecl
*param
= fd
->getParamDecl(i
);
341 if (param
->getType()->getAs
<EnumType
>())
347 /* Sorting function that places declaration of functions
348 * with a shorter name first.
350 static bool less_name(const FunctionDecl
*a
, const FunctionDecl
*b
)
352 return a
->getName().size() < b
->getName().size();
355 /* Collect all functions that belong to a certain type, separating
356 * constructors from methods that set an enum value,
357 * methods that set a persistent callback and
358 * from regular methods, while keeping track of the _to_str,
359 * _copy and _free functions, if any, separately.
360 * Methods that accept any enum arguments that are not specifically handled
362 * If there are any overloaded
363 * functions, then they are grouped based on their name after removing the
364 * argument type suffix.
365 * Check for functions that describe subclasses before considering
366 * any other functions in order to be able to detect those other
367 * functions as belonging to the subclasses.
368 * Sort the names of the functions based on their lengths
369 * to ensure that nested subclasses are handled later.
371 * Also extract information about automatic conversion functions.
373 generator::generator(SourceManager
&SM
, set
<RecordDecl
*> &exported_types
,
374 set
<FunctionDecl
*> exported_functions
, set
<FunctionDecl
*> functions
) :
377 set
<FunctionDecl
*>::iterator in
;
378 set
<RecordDecl
*>::iterator it
;
379 vector
<FunctionDecl
*> type_subclasses
;
380 vector
<FunctionDecl
*>::iterator iv
;
382 for (in
= functions
.begin(); in
!= functions
.end(); ++in
) {
383 FunctionDecl
*decl
= *in
;
384 functions_by_name
[decl
->getName().str()] = decl
;
387 for (it
= exported_types
.begin(); it
!= exported_types
.end(); ++it
)
390 for (in
= exported_functions
.begin(); in
!= exported_functions
.end();
392 if (is_subclass(*in
))
393 type_subclasses
.push_back(*in
);
395 std::sort(type_subclasses
.begin(), type_subclasses
.end(), &less_name
);
396 for (iv
= type_subclasses
.begin(); iv
!= type_subclasses
.end(); ++iv
) {
397 add_type_subclasses(*iv
);
400 for (in
= exported_functions
.begin(); in
!= exported_functions
.end();
402 FunctionDecl
*method
= *in
;
405 if (is_subclass(method
))
408 c
= method2class(method
);
411 if (is_constructor(method
)) {
412 c
->constructors
.insert(method
);
413 } else if (handled_sets_enum(c
, method
)) {
414 } else if (sets_persistent_callback(c
, method
)) {
415 c
->persistent_callbacks
.insert(method
);
416 } else if (takes_enums(method
)) {
417 std::string name
= method
->getName().str();
418 die(name
+ " has unhandled enum argument");
420 string name
= c
->method_name(method
);
421 c
->methods
[name
].insert(method
);
425 extract_automatic_conversions();
428 /* Print error message "msg" and abort.
430 void generator::die(const char *msg
)
432 fprintf(stderr
, "%s\n", msg
);
436 /* Print error message "msg" and abort.
438 void generator::die(string msg
)
443 /* Return a sequence of the types of which the given type declaration is
444 * marked as being a subtype.
445 * The order of the types is the opposite of the order in which they
446 * appear in the source. In particular, the first annotation
447 * is the one that is closest to the annotated type and the corresponding
448 * type is then also the first that will appear in the sequence of types.
449 * This is also the order in which the annotations appear
450 * in the AttrVec returned by Decl::getAttrs() in older versions of clang.
451 * In newer versions of clang, the order is that in which
452 * the attribute appears in the source.
453 * Use the position of the "isl_export" attribute to determine
454 * whether this is an old (with reversed order) or a new version.
455 * The "isl_export" attribute is automatically added
456 * after each "isl_subclass" attribute. If it appears in the list before
457 * any "isl_subclass" is encountered, then this must be a reversed list.
459 std::vector
<string
> generator::find_superclasses(Decl
*decl
)
461 vector
<string
> super
;
462 bool reversed
= false;
464 if (!decl
->hasAttrs())
467 string sub
= "isl_subclass";
468 size_t len
= sub
.length();
469 AttrVec attrs
= decl
->getAttrs();
470 for (AttrVec::const_iterator i
= attrs
.begin(); i
!= attrs
.end(); ++i
) {
471 const AnnotateAttr
*ann
= dyn_cast
<AnnotateAttr
>(*i
);
474 string s
= ann
->getAnnotation().str();
475 if (s
== "isl_export" && super
.size() == 0)
477 if (s
.substr(0, len
) == sub
) {
478 s
= s
.substr(len
+ 1, s
.length() - len
- 2);
482 super
.insert(super
.begin(), s
);
489 /* Is "decl" marked as describing subclasses?
491 bool generator::is_subclass(FunctionDecl
*decl
)
493 return find_superclasses(decl
).size() > 0;
496 /* Is decl marked as being part of an overloaded method?
498 bool generator::is_overload(Decl
*decl
)
500 return has_annotation(decl
, "isl_overload");
503 /* Is decl marked as a constructor?
505 bool generator::is_constructor(Decl
*decl
)
507 return has_annotation(decl
, "isl_constructor");
510 /* Is decl marked as consuming a reference?
512 bool generator::takes(Decl
*decl
)
514 return has_annotation(decl
, "isl_take");
517 /* Is decl marked as preserving a reference?
519 bool generator::keeps(Decl
*decl
)
521 return has_annotation(decl
, "isl_keep");
524 /* Is decl marked as returning a reference that is required to be freed.
526 bool generator::gives(Decl
*decl
)
528 return has_annotation(decl
, "isl_give");
531 /* Return the class that has a name that best matches the initial part
532 * of the name of function "fd" or NULL if no such class could be found.
534 isl_class
*generator::method2class(FunctionDecl
*fd
)
537 map
<string
, isl_class
>::iterator ci
;
538 string name
= fd
->getNameAsString();
540 for (ci
= classes
.begin(); ci
!= classes
.end(); ++ci
) {
541 size_t len
= ci
->first
.length();
542 if (len
> best
.length() && name
.substr(0, len
) == ci
->first
&&
547 if (classes
.find(best
) == classes
.end()) {
548 cerr
<< "Unable to find class of " << name
<< endl
;
552 return &classes
[best
];
555 /* Is "type" the type "isl_ctx *"?
557 bool generator::is_isl_ctx(QualType type
)
559 if (!type
->isPointerType())
561 type
= type
->getPointeeType();
562 if (type
.getAsString() != "isl_ctx")
568 /* Is the first argument of "fd" of type "isl_ctx *"?
570 bool generator::first_arg_is_isl_ctx(FunctionDecl
*fd
)
574 if (fd
->getNumParams() < 1)
577 param
= fd
->getParamDecl(0);
578 return is_isl_ctx(param
->getOriginalType());
584 /* Return the first location in the range returned by
585 * clang::SourceManager::getImmediateExpansionRange.
586 * Older versions of clang return a pair of SourceLocation objects.
587 * More recent versions return a CharSourceRange.
589 static SourceLocation
range_begin(
590 const std::pair
<SourceLocation
,SourceLocation
> &p
) {
593 static SourceLocation
range_begin(const CharSourceRange
&range
) {
594 return range
.getBegin();
600 /* Does the callback argument "param" take its argument at position "pos"?
602 * The memory management annotations of arguments to function pointers
603 * are not recorded by clang, so the information cannot be extracted
604 * from the type of "param".
605 * Instead, go to the location in the source where the callback argument
606 * is declared, look for the right argument of the callback itself and
607 * then check if it has an "__isl_take" memory management annotation.
609 * If the return value of the function has a memory management annotation,
610 * then the spelling of "param" will point to the spelling
611 * of this memory management annotation. Since the macro is defined
612 * on the command line (in main), this location does not have a file entry.
613 * In this case, move up one level in the macro expansion to the location
614 * where the memory management annotation is used.
616 bool generator::callback_takes_argument(ParmVarDecl
*param
,
620 const char *s
, *end
, *next
;
623 loc
= param
->getSourceRange().getBegin();
624 if (!SM
.getFileEntryForID(SM
.getFileID(SM
.getSpellingLoc(loc
))))
625 loc
= ClangAPI::range_begin(SM
.getImmediateExpansionRange(loc
));
626 s
= SM
.getCharacterData(loc
);
628 die("No character data");
631 die("Cannot find function pointer");
632 s
= strchr(s
+ 1, '(');
634 die("Cannot find function pointer arguments");
635 end
= strchr(s
+ 1, ')');
637 die("Cannot find end of function pointer arguments");
639 s
= strchr(s
+ 1, ',');
641 die("Cannot find function pointer argument");
643 next
= strchr(s
+ 1, ',');
644 if (next
&& next
< end
)
646 s
= strchr(s
+ 1, '_');
648 die("Cannot find function pointer argument annotation");
649 takes
= prefixcmp(s
, "__isl_take") == 0;
650 keeps
= prefixcmp(s
, "__isl_keep") == 0;
651 if (!takes
&& !keeps
)
652 die("Cannot find function pointer argument annotation");
657 /* Is "type" that of a pointer to an isl_* structure?
659 bool generator::is_isl_type(QualType type
)
661 if (type
->isPointerType()) {
664 type
= type
->getPointeeType();
665 if (type
->isFunctionType())
667 s
= type
.getAsString();
668 return s
.substr(0, 4) == "isl_";
674 /* Is "type" one of the integral types with a negative value
675 * indicating an error condition?
677 bool generator::is_isl_neg_error(QualType type
)
679 return is_isl_bool(type
) || is_isl_stat(type
) || is_isl_size(type
);
682 /* Is "type" the primitive type with the given name?
684 static bool is_isl_primitive(QualType type
, const char *name
)
688 if (type
->isPointerType())
691 s
= type
.getAsString();
695 /* Is "type" the type isl_bool?
697 bool generator::is_isl_bool(QualType type
)
699 return is_isl_primitive(type
, "isl_bool");
702 /* Is "type" the type isl_stat?
704 bool generator::is_isl_stat(QualType type
)
706 return is_isl_primitive(type
, "isl_stat");
709 /* Is "type" the type isl_size?
711 bool generator::is_isl_size(QualType type
)
713 return is_isl_primitive(type
, "isl_size");
716 /* Is "type" that of a pointer to a function?
718 bool generator::is_callback(QualType type
)
720 if (!type
->isPointerType())
722 type
= type
->getPointeeType();
723 return type
->isFunctionType();
726 /* Is "type" that of "char *" of "const char *"?
728 bool generator::is_string(QualType type
)
730 if (type
->isPointerType()) {
731 string s
= type
->getPointeeType().getAsString();
732 return s
== "const char" || s
== "char";
738 /* Is "type" that of "long"?
740 bool generator::is_long(QualType type
)
742 const BuiltinType
*builtin
= type
->getAs
<BuiltinType
>();
743 return builtin
&& builtin
->getKind() == BuiltinType::Long
;
746 /* Is "type" that of "unsigned int"?
748 static bool is_unsigned_int(QualType type
)
750 const BuiltinType
*builtin
= type
->getAs
<BuiltinType
>();
751 return builtin
&& builtin
->getKind() == BuiltinType::UInt
;
754 /* Return the name of the type that "type" points to.
755 * The input "type" is assumed to be a pointer type.
757 string
generator::extract_type(QualType type
)
759 if (type
->isPointerType())
760 return type
->getPointeeType().getAsString();
761 die("Cannot extract type from non-pointer type");
764 /* Given the type of a function pointer, return the corresponding
765 * function prototype.
767 const FunctionProtoType
*generator::extract_prototype(QualType type
)
769 return type
->getPointeeType()->getAs
<FunctionProtoType
>();
772 /* Return the function name suffix for the type of "param".
774 * If the type of "param" is an isl object type,
775 * then the suffix is the name of the type with the "isl" prefix removed,
776 * but keeping the "_".
777 * If the type is an unsigned integer, then the type suffix is "_ui".
779 static std::string
type_suffix(ParmVarDecl
*param
)
783 type
= param
->getOriginalType();
784 if (generator::is_isl_type(type
))
785 return generator::extract_type(type
).substr(3);
786 else if (is_unsigned_int(type
))
788 generator::die("Unsupported type suffix");
791 /* If "suffix" is a suffix of "s", then return "s" with the suffix removed.
792 * Otherwise, simply return "s".
794 static std::string
drop_suffix(const std::string
&s
, const std::string
&suffix
)
796 size_t len
, suffix_len
;
799 suffix_len
= suffix
.length();
801 if (len
>= suffix_len
&& s
.substr(len
- suffix_len
) == suffix
)
802 return s
.substr(0, len
- suffix_len
);
807 /* If "method" is overloaded, then return its name with the suffixes
808 * corresponding to the types of the final arguments removed.
809 * Otherwise, simply return the name of the function.
810 * Start from the final argument and keep removing suffixes
811 * matching arguments, independently of whether previously considered
814 string
isl_class::name_without_type_suffixes(FunctionDecl
*method
)
819 name
= method
->getName().str();
820 if (!generator::is_overload(method
))
823 num_params
= method
->getNumParams();
824 for (int i
= num_params
- 1; i
>= 0; --i
) {
828 param
= method
->getParamDecl(i
);
829 type
= type_suffix(param
);
831 name
= drop_suffix(name
, type
);
837 /* Is function "fd" with the given name a "get" method?
839 * A "get" method is an instance method
840 * with a name that starts with the get method prefix.
842 bool isl_class::is_get_method_name(FunctionDecl
*fd
, const string
&name
) const
844 return !is_static(fd
) && prefixcmp(name
.c_str(), get_prefix
) == 0;
847 /* Extract the method name corresponding to "fd".
849 * If "fd" is a "get" method, then drop the "get" method prefix.
851 string
isl_class::method_name(FunctionDecl
*fd
) const
853 string base
= base_method_name(fd
);
855 if (is_get_method_name(fd
, base
))
856 return base
.substr(strlen(get_prefix
));