v8toolkit  0.0.1
Utility library for embedding V8 Javascript engine in a c++ program
class_parser.cpp
Go to the documentation of this file.
1 /**
2 
3 FOR COMMENT PARSING, LOOK AT ASTContext::getCommentForDecl
4 
5 http://clang.llvm.org/doxygen/classclang_1_1ASTContext.html#aa3ec454ca3698f73c421b08f6edcea92
6 
7  */
8 
9 /**
10  * This clang plugin looks for classes annotated with V8TOOLKIT_WRAPPED_CLASS and/or V8TOOLKIT_BIDIRECTIONAL_CLASS
11  * and automatically generates source files for class wrappings and/or JSWrapper objects for those classes.
12  *
13  * Each JSWrapper object type will go into its own .h file called v8toolkit_generated_bidirectional_<ClassName>.h
14  * These files should be included from within the header file defining the class.
15  *
16  * MISSING DOCS FOR CLASS WRAPPER CODE GENERATION
17  */
18 
19 // This program will only work with clang but the output should be useable on any platform.
20 
21 /**
22  * KNOWN BUGS:
23  * Doesn't properly understand virtual methods and will duplicate them across the inheritence hierarchy
24  * Doesn't include root file of compilation - if this is pretty safe in a unity build, as the root file is a file that
25  * just includes all the other files
26  */
27 
28 
29 /**
30 How to run over complete code base using cmake + cotiregg1
31 
32 add_library(api-gen-template OBJECT ${YOUR_SOURCE_FILES})
33 target_compile_options(api-gen-template
34  PRIVATE -Xclang -ast-dump -fdiagnostics-color=never <== this isn't right yet, this just dumps the ast
35  )
36 set_target_properties(api-gen-template PROPERTIES COTIRE_UNITY_TARGET_NAME "api-gen")
37 cotire(api-gen-template)
38  */
39 
40 #include <vector>
41 #include <string>
42 #include <map>
43 using namespace std;
44 
45 
46 //////////////////////////////
47 // CUSTOMIZE THESE VARIABLES
48 //////////////////////////////
49 
50 // if this is defined, only template info will be printed
51 //#define TEMPLATE_INFO_ONLY
52 #define TEMPLATE_FILTER_STD
53 
54 #define TEMPLATED_CLASS_PRINT_THRESHOLD 10
55 #define TEMPLATED_FUNCTION_PRINT_THRESHOLD 100
56 
57 
58 // Generate an additional file with sfinae for each wrapped class type
60 
61 // Any base types you want to always ignore -- v8toolkit::WrappedClassBase must remain, others may be added/changed
62 vector<string> base_types_to_ignore = {"class v8toolkit::WrappedClassBase", "class Subscriber"};
63 
64 
65 // Top level types that will be immediately discarded
66 vector<string> types_to_ignore_regex = {"^struct has_custom_process[<].*[>]::mixin$"};
67 
68 vector<string> includes_for_every_class_wrapper_file = {"\"js_casts.h\"", "<v8toolkit/v8_class_wrapper_impl.h>"};
69 
70 // error if bidirectional types don't make it in due to include file ordering
71 // disable "fast_compile" so the V8ClassWrapper code can be generated
72 string header_for_every_class_wrapper_file = "#define NEED_BIDIRECTIONAL_TYPES\n#undef V8TOOLKIT_WRAPPER_FAST_COMPILE\n";
73 
74 // sometimes files sneak in that just shouldn't be
75 vector<string> never_include_for_any_file = {"\"v8helpers.h\""};
76 
77 
78 
79 map<string, string> static_method_renames = {{"name", "get_name"}};
80 
81 // http://usejsdoc.org/tags-type.html
82 map<string, string> cpp_to_js_type_conversions = {{"^(?:std::|eastl)?vector", "Array.{$1}"},
83  {"^(?:std::|eastl::)?(?:vector_)?(?:multi)?map", "Object.{$1, $2}"},
84  //{"^([^<]+)\\s*[<]\\s*(.+?)\\s*[>]\\s*([^>]*)$", "$1<$2>$3"},
85  {"^(?:const)?\\s*(?:unsigned)?\\s*(?:char|short|int|long|long long|float|double|long double)\\s*(?:const)?\\s*[*]?\\s*[&]?$", "Number"},
86  {"^(?:const)?\\s*_?[Bb]ool\\s*(?:const)?\\s*[*]?\\s*[&]?$", "Boolean"},
87  {"^(?:const)?\\s*(?:char\\s*[*]|(?:std::)?string)\\s*(?:const)?\\s*\\s*[&]?$", "String"},
88  {"^void$", "Undefined"},
89  {"^(?:std::)?unique_ptr", "$1"},
90  {"^(?:std::)?basic_string", "String"},
91  {"^\\s*nullptr\\s*$", "null"}
92 };
93 
94 // regex for @callback instead of @param: ^(const)?\s*(std::)?function[<][^>]*[>]\s*(const)?\s*\s*[&]?$
95 
96 std::string js_api_header = R"JS_API_HEADER(
97 
98 /**
99  * @type World
100  */
101 var world;
102 
103 /**
104  * @type Map
105  */
106 var map;
107 
108 /**
109  * @type Game
110  */
111 var game;
112 
113 /**
114  * Prints a string and appends a newline
115  * @param {String} s the string to be printed
116  */
117 function println(s){}
118 
119 /**
120  * Prints a string without adding a newline to the end
121  * @param {String} s the string to be printed
122  */
123 function print(s){}
124 
125 /**
126  * Dumps the contents of the given variable - only 'own' properties
127  * @param o {Object} the variable to be dumped
128  */
129 function printobj(o){}
130 
131 /**
132  * Dumps the contents of the given variable - all properties including those of prototype chain
133  * @param o {Object} the variable to be dumped
134  */
135 function printobjall(o){}
136 
137 /**
138  * Attempts to load the given module and returns the exported data. Requiring the same module
139  * more than once will return the cached result, not re-execute the source.
140  * @param {String} module_name name of the module to require
141  */
142 function require(module_name){}
143 
144 
145 )JS_API_HEADER";
146 
147 #include <iostream>
148 #include <fstream>
149 #include <sstream>
150 #include <set>
151 #include <regex>
152 
153 #include <fmt/ostream.h>
154 
155 #include "clang.h"
156 
157 #include "class_parser.h"
158 
159 
160 
162 
163 
164 #include "wrapped_class.h"
165 #include "ast_action.h"
166 #include "ast_consumer.h"
167 #include "annotations.h"
168 #include "class_handler.h"
169 #include "parsed_method.h"
170 
171 
173 
174 vector<WrappedClass *> WrappedClass::wrapped_classes;
175 
176 
177 
178 /*
179 std::string handle_std(const std::string & input) {
180  smatch matches;
181 string result = input;
182 // EricWF said to remove __[0-9] just to be safe for future updates
183  if (regex_match(input, matches, regex("^((?:const\\s+|volatile\\s+)*)(?:class |struct )?(?:std::(?:__[0-9]::)?)?(.*)"))) {
184  // space before std:: is handled from const/volatile if needed
185  result = matches[1].str() + "std::" + matches[2].str();
186 
187 }
188 
189 if (print_logging) cerr << "Stripping std from " << input << " results in " << result << endl;
190  return result;
191 }
192 
193 
194 bool has_std(const std::string & input) {
195  return std::regex_match(input, regex("^(const\\s+|volatile\\s+)*(class |struct )?\\s*std::.*$"));
196 }
197 */
198 
199 
200 void print_vector(const vector<string> & vec, const string & header, const string & indentation, bool ignore_empty) {
201 
202  if (ignore_empty && vec.empty()) {
203  return;
204  }
205 
206  if (header != "") {
207  cerr << indentation << header << endl;
208  }
209  for (auto & str : vec) {
210  cerr << indentation << " - " << str << endl;
211  }
212  if (vec.empty()) {
213  cerr << indentation << " * (EMPTY VECTOR)" << endl;
214  }
215 }
216 
217 
218 /* For some classes (ones with dependent base types among them), there will be two copies, one will be
219  * unusable for an unknown reason. This tests if it is that
220  */
221 bool is_good_record_decl(const CXXRecordDecl * decl) {
222  if (decl == nullptr) {
223  return false;
224  }
225 
226  if (!decl->isThisDeclarationADefinition()) {
227  return true;
228  }
229 
230  for (auto base : decl->bases()) {
231  if (base.getType().getTypePtrOrNull() == nullptr) {
232  llvm::report_fatal_error(fmt::format("base type ptr was null for {}", decl->getNameAsString()).c_str());
233  }
234  if (!is_good_record_decl(base.getType()->getAsCXXRecordDecl())) {
235  return false;
236  }
237  }
238  return true;
239 }
240 
241 
242 // Finds where file_id is included, how it was included, and returns the string to duplicate
243 // that inclusion
244 std::string get_include_string_for_fileid(CompilerInstance & compiler_instance, FileID & file_id) {
245  auto include_source_location = compiler_instance.getPreprocessor().getSourceManager().getIncludeLoc(file_id);
246 
247  // If it's in the "root" file (file id 1), then there's no include for it
248  if (include_source_location.isValid()) {
249  auto header_file = include_source_location.printToString(compiler_instance.getPreprocessor().getSourceManager());
250 // if (print_logging) cerr << "include source location: " << header_file << endl;
251  // wrapped_class.include_files.insert(header_file);
252  } else {
253 // if (print_logging) cerr << "No valid source location" << endl;
254  return "";
255  }
256 
257  bool invalid;
258  // This gets EVERYTHING after the start of the filename in the include. "asdf.h"..... or <asdf.h>.....
259  const char * text = compiler_instance.getPreprocessor().getSourceManager().getCharacterData(include_source_location, &invalid);
260  const char * text_end = text + 1;
261  while(*text_end != '>' && *text_end != '"') {
262  text_end++;
263  }
264 
265  return string(text, (text_end - text) + 1);
266 
267 }
268 
269 
270 std::string get_include_for_source_location(CompilerInstance & compiler_instance, const SourceLocation & source_location) {
271  auto full_source_loc = FullSourceLoc(source_location, compiler_instance.getPreprocessor().getSourceManager());
272 
273  auto file_id = full_source_loc.getFileID();
274  return get_include_string_for_fileid(compiler_instance, file_id);
275 }
276 
277 std::string get_include_for_type_decl(CompilerInstance & compiler_instance, const TypeDecl * type_decl) {
278  if (type_decl == nullptr) {
279  return "";
280  }
281  return get_include_for_source_location(compiler_instance, type_decl->getLocStart());
282 }
283 
284 
285 //
286 // std::string decl2str(const clang::Decl *d, SourceManager &sm) {
287 // // (T, U) => "T,,"
288 // std::string text = Lexer::getSourceText(CharSourceRange::getTokenRange(d->getSourceRange()), sm, LangOptions(), 0);
289 // if (text.at(text.size()-1) == ',')
290 // return Lexer::getSourceText(CharSourceRange::getCharRange(d->getSourceRange()), sm, LangOptions(), 0);
291 // return text;
292 // }
293 
294 
295 std::string get_source_for_source_range(SourceManager & sm, SourceRange source_range) {
296  std::string text = Lexer::getSourceText(CharSourceRange::getTokenRange(source_range), sm, LangOptions(), 0);
297  if (!text.empty() && text.at(text.size()-1) == ',')
298  return Lexer::getSourceText(CharSourceRange::getCharRange(source_range), sm, LangOptions(), 0);
299  return text;
300 }
301 #if 0
302 
303 vector<string> count_top_level_template_parameters(const std::string & source) {
304  int open_angle_count = 0;
305  vector<string> parameter_strings;
306  std::string * current;
307  for (char c : source) {
308  if (isspace(c)) {
309  continue;
310  }
311  if (c == '<') {
312  open_angle_count++;
313  if (open_angle_count > 1) {
314  *current += c;
315  }
316  } else if (c == '>') {
317  open_angle_count--;
318  if (open_angle_count > 0) {
319  *current += c;
320  }
321  } else {
322  if (open_angle_count == 1) {
323  if (parameter_strings.size() == 0) {
324  parameter_strings.push_back("");
325  current = &parameter_strings.back();
326  }
327  if (c == ',') {
328  parameter_strings.push_back("");
329  current = &parameter_strings.back();
330  if (open_angle_count > 1) {
331  *current += c;
332  }
333  } else {
334  *current += c;
335  }
336  } else if (open_angle_count > 1) {
337  *current += c;
338  }
339  }
340  }
341  if (print_logging) if (print_logging) cerr << "^^^^^^^^^^^^^^^ Counted " << parameter_strings.size() << " for " << source << endl;
342  for (auto & str : parameter_strings) {
343  if (print_logging) cerr << "^^^^^^^^^^^^^^^" << str << endl;
344  }
345  return parameter_strings;
346 }
347 #endif
348 
349 
350 
351 
353 vector<std::unique_ptr<ClassTemplate>> class_templates;
356  const ClassTemplateDecl * decl;
357  int instantiations = 0;
358 
359  ClassTemplate(const ClassTemplateDecl * decl) : decl(decl) {
360  name = decl->getQualifiedNameAsString();
361  // cerr << fmt::format("Created class template for {}", name) << endl;
362  }
363 
364  void instantiated(){ instantiations++; }
365 
366 
367 
368  static ClassTemplate & get_or_create(const ClassTemplateDecl * decl) {
369  for(auto & tmpl : class_templates) {
370  if (tmpl->decl == decl) {
371  return *tmpl;
372  }
373  }
374  class_templates.emplace_back(make_unique<ClassTemplate>(decl));
375  return *class_templates.back();
376  }
377 };
378 
380 vector<unique_ptr<FunctionTemplate>> function_templates;
383  //const FunctionTemplateDecl * decl;
384 
385  // not all functions instantiated because of a template are templated themselves
386  const FunctionDecl * decl;
387  int instantiations = 0;
388 
389  FunctionTemplate(const FunctionDecl * decl) : decl(decl) {
390  name = decl->getQualifiedNameAsString();
391  // cerr << fmt::format("Created function template for {}", name) << endl;
392  }
393 
394  void instantiated(){ instantiations++; }
395 
396 
397  static FunctionTemplate & get_or_create(const FunctionDecl * decl) {
398 
399  for(auto & tmpl : function_templates) {
400  if (tmpl->decl == decl) {
401  return *tmpl;
402  }
403  }
404  function_templates.emplace_back(make_unique<FunctionTemplate>(decl));
405  return *function_templates.back();
406  }
407 };
408 
409 // store all the constructor names used, since they all go into the same global object template used as for building contexts
410 std::vector<std::string> used_constructor_names;
411 
412 
413 /*
414 vector<WrappedClass *> get_wrapped_class_regex(const string & regex_string) {
415 cerr << "Searching with regex: " << regex_string << endl;
416 vector<WrappedClass *> results;
417 for (auto & wrapped_class : wrapped_classes) {
418  cerr << " -- " << wrapped_class->class_name << endl;
419  if (regex_search(wrapped_class->class_name, regex(regex_string))) {
420  cerr << " -- ** MATCH ** " << endl;
421  results.push_back(wrapped_class.get());
422  }
423 }
424 cerr << fmt::format("Returning {} results", results.size()) << endl;
425 return results;
426 }
427 */
428 
429 bool has_wrapped_class(const CXXRecordDecl * decl) {
430  auto class_name = get_canonical_name_for_decl(decl);
431 
432  for (auto & wrapped_class : WrappedClass::wrapped_classes) {
433 
434  if (wrapped_class->class_name == class_name) {
435  return true;
436  }
437  }
438  return false;
439 }
440 
441 
442 
443 
444 
445 string get_sfinae_matching_wrapped_classes(const vector<unique_ptr<WrappedClass>> & wrapped_classes) {
446  vector<string> sfinaes;
447  string forward_declarations = "#define V8TOOLKIT_V8CLASSWRAPPER_FORWARD_DECLARATIONS ";
448  for (auto & wrapped_class : wrapped_classes) {
449  if (wrapped_class->found_method == FOUND_INHERITANCE) {
450  continue;
451  }
452  if (!wrapped_class->should_be_wrapped()) {
453  continue;
454  }
455  sfinaes.emplace_back(wrapped_class->make_sfinae_to_match_wrapped_class());
456  forward_declarations += wrapped_class->class_name + "; ";
457  }
458 
459 
460  for(int i = sfinaes.size() - 1; i >= 0; i--) {
461  if (sfinaes[i] == "") {
462  sfinaes.erase(sfinaes.begin() + i);
463  }
464  }
465 
466 
467  // too many forward declarations do bad things to compile time / ram usage, so try to catch any silly mistakes
468  if (sfinaes.size() > 40 /* 40 is arbitrary */) {
469  cerr << join(sfinaes, " || ") << endl;
470  llvm::report_fatal_error("more 'sfinae's than arbitrary number used to catch likely errors - can be increased if needed");
471  }
472 
473 
474 
475  std::string sfinae = "";
476  if (!sfinaes.empty()) {
477  sfinae = string("#define V8TOOLKIT_V8CLASSWRAPPER_FULL_TEMPLATE_SFINAE ") + join(sfinaes, " || ") + "\n";
478  } // else if it's empty, leave it undefined
479 
480  forward_declarations += "\n";
481  return sfinae + "\n" + forward_declarations;
482 }
483 
484 
485 
486 
487 
488 
489 
490 
491 
492 
493 
494 
495 
496 
497 // std::string strip_path_from_filename(const std::string & filename) {
498 //
499 // // naive regex to grab everything after the last slash or backslash
500 // auto regex = std::regex("([^/\\\\]*)$");
501 //
502 // std::smatch matches;
503 // if (std::regex_search(filename, matches, regex)) {
504 // return matches[1];
505 // }
506 // if (print_logging) cerr << fmt::format("Unrecognizable filename {}", filename);
507 // throw std::exception();
508 // }
509 
510 #if 0
511 
512 
513 // Returns true if qual_type is a 'trivial' std:: type like
514 // std::string
515 bool is_trivial_std_type(QualType & qual_type, std::string & output) {
516  std::string name = qual_type.getAsString();
517  std::string canonical_name = qual_type.getCanonicalType().getAsString();
518 
519  // if it's a std:: type and not explicitly user-specialized, pass it through
520  if (std::regex_match(name, regex("^(const\\s+|volatile\\s+)*(class\\s+|struct\\s+)?std::[^<]*$"))) {
521  output = handle_std(name);a
522  return true;
523  }
524  // or if the canonical type has std:: in it and it's not user-customized
525  else if (has_std(canonical_name) &&
526  std::regex_match(name, regex("^[^<]*$"))) {
527  output = handle_std(name);
528  return true;
529  }
530  return false;
531 }
532 
533 // Returns true if qual_type is a 'non-trivial' std:: type (containing user-specified template types like
534 // std::map<MyType1, MyType2>
535 bool is_nontrivial_std_type(QualType & qual_type, std::string & output) {
536 
537  std::string name = qual_type.getAsString();
538  std::string canonical_name = qual_type.getCanonicalType().getAsString();
539  if (print_logging) cerr << "Checking nontrivial std type on " << name << " : " << canonical_name << endl;
540  smatch matches;
541 
542 
543  // if it's in standard (according to its canonical type) and has user-specified types
544  if (has_std(canonical_name) &&
545  std::regex_match(name, matches, regex("^([^<]*<).*$"))) {
546  output = handle_std(matches[1].str());
547  if (print_logging) cerr << "Yes" << endl;
548  return true;
549  }
550  if (print_logging) cerr << "No" << endl;
551  return false;
552 }
553 
554 
555 #endif
556 
557 std::string get_type_string(QualType qual_type,
558  const std::string & indentation) {
559 
560  auto original_qualifiers = qual_type.getLocalFastQualifiers();
561  // chase any typedefs to get the "real" type
562  while (auto typedef_type = dyn_cast<TypedefType>(qual_type)) {
563  qual_type = typedef_type->getDecl()->getUnderlyingType();
564  }
565 
566  // re-apply qualifiers to potentially new qualtype
567  qual_type.setLocalFastQualifiers(original_qualifiers);
568 
569  auto canonical_qual_type = qual_type.getCanonicalType();
570 // cerr << "canonical qual type typeclass: " << canonical_qual_type->getTypeClass() << endl;
571  static PrintingPolicy pp = PrintingPolicy(LangOptions());
572  pp.adjustForCPlusPlus();
573  auto canonical = canonical_qual_type.getAsString(pp);
574  return regex_replace(canonical, regex("std::__1::"), "std::");
575 
576 #if 0
577  std::string source = input_source;
578 
579  bool turn_logging_off = false;
580 /*
581  if (regex_match(qual_type.getAsString(), regex("^.*glm.*$") )) {
582  print_logging++;
583  turn_logging_off = true;
584  }
585 */
586 
587  if (print_logging) cerr << indentation << "Started at " << qual_type.getAsString() << endl;
588  if (print_logging) cerr << indentation << " And canonical name: " << qual_type.getCanonicalType().getAsString() << endl;
589  if (print_logging) cerr << indentation << " And source " << source << endl;
590 
591  std::string std_result;
592  if (is_trivial_std_type(qual_type, std_result)) {
593  if (print_logging) cerr << indentation << "Returning trivial std:: type: " << std_result << endl << endl;
594  if (turn_logging_off) print_logging--;
595  return std_result;
596  }
597 
598  bool is_reference = qual_type->isReferenceType();
599  string reference_suffix = is_reference ? "&" : "";
600  qual_type = qual_type.getNonReferenceType();
601 
602  stringstream pointer_suffix;
603  bool changed = true;
604  while(changed) {
605  changed = false;
606  if (!qual_type->getPointeeType().isNull()) {
607  changed = true;
608  pointer_suffix << "*";
609  qual_type = qual_type->getPointeeType();
610  if (print_logging) cerr << indentation << "stripped pointer, went to: " << qual_type.getAsString() << endl;
611  continue; // check for more pointers first
612  }
613 
614  // This code traverses all the typdefs and pointers to get to the actual base type
615  if (dyn_cast<TypedefType>(qual_type) != nullptr) {
616  changed = true;
617  if (print_logging) cerr << indentation << "stripped typedef, went to: " << qual_type.getAsString() << endl;
618  qual_type = dyn_cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType();
619  source = ""; // source is invalidated if it's a typedef
620  }
621  }
622 
623  if (print_logging) cerr << indentation << "CHECKING TO SEE IF " << qual_type.getUnqualifiedType().getAsString() << " is a template specialization"<< endl;
624  auto base_type_record_decl = qual_type.getUnqualifiedType()->getAsCXXRecordDecl();
625  if (dyn_cast<ClassTemplateSpecializationDecl>(base_type_record_decl)) {
626 
627 
628 
629  if (print_logging) cerr << indentation << "!!!!! Started with template specialization: " << qual_type.getAsString() << endl;
630 
631 
632 
633  std::smatch matches;
634  string qual_type_string = qual_type.getAsString();
635 
636  std::string std_type_output;
637  bool nontrivial_std_type = false;
638  if (is_nontrivial_std_type(qual_type, std_type_output)) {
639  if (print_logging) cerr << indentation << "is nontrivial std type and got result: " << std_type_output << endl;
640  nontrivial_std_type = true;
641  result << std_type_output;
642  }
643  // Get everything EXCEPT the template parameters into matches[1] and [2]
644  else if (!regex_match(qual_type_string, matches, regex("^([^<]+<).*(>[^>]*)$"))) {
645  if (print_logging) cerr << indentation << "short circuiting on " << original_qual_type.getAsString() << endl;
646  if (turn_logging_off) print_logging--;
647  return original_qual_type.getAsString();
648  } else {
649  result << matches[1];
650  if (print_logging) cerr << indentation << "is NOT nontrivial std type" << endl;
651  }
652  auto template_specialization_decl = dyn_cast<ClassTemplateSpecializationDecl>(base_type_record_decl);
653 
654  auto user_specified_template_parameters = count_top_level_template_parameters(source);
655 
656 
657  auto & template_arg_list = template_specialization_decl->getTemplateArgs();
658  if (user_specified_template_parameters.size() > template_arg_list.size()) {
659  llvm::report_fatal_error(fmt::format("ERROR: detected template parameters > actual list size for {}", qual_type_string).c_str());
660  }
661 
662  auto template_types_to_handle = user_specified_template_parameters.size();
663  if (source == "") {
664  // if a typedef was followed, then user sources is meaningless
665  template_types_to_handle = template_arg_list.size();
666  }
667 
668 // for (decltype(template_arg_list.size()) i = 0; i < template_arg_list.size(); i++) {
669  for (decltype(template_arg_list.size()) i = 0; i < template_types_to_handle; i++) {
670  if (i > 0) {
671  result << ", ";
672  }
673  if (print_logging) cerr << indentation << "Working on template parameter " << i << endl;
674  auto & arg = template_arg_list[i];
675 
676  switch(arg.getKind()) {
677  case clang::TemplateArgument::Type: {
678  if (print_logging) cerr << indentation << "processing as type argument" << endl;
679  auto template_arg_qual_type = arg.getAsType();
680  auto template_type_string = get_type_string(template_arg_qual_type,
681  indentation + " ");
682  if (print_logging) cerr << indentation << "About to append " << template_type_string << " template type string onto existing: " << result.str() << endl;
683  result << template_type_string;
684  break; }
685  case clang::TemplateArgument::Integral: {
686  if (print_logging) cerr << indentation << "processing as integral argument" << endl;
687  auto integral_value = arg.getAsIntegral();
688  if (print_logging) cerr << indentation << "integral value radix10: " << integral_value.toString(10) << endl;
689  result << integral_value.toString(10);
690  break;}
691  default:
692  if (print_logging) cerr << indentation << "Oops, unhandled argument type" << endl;
693  }
694  }
695  result << ">" << pointer_suffix.str() << reference_suffix;
696  if (print_logging) cerr << indentation << "!!!!!Finished stringifying templated type to: " << result.str() << endl << endl;
697  if (turn_logging_off) print_logging--;
698  return result.str();
699 
700 // } else if (std::regex_match(qual_type.getAsString(), regex("^(class |struct )?std::.*$"))) {
701 //
702 //
703 // if (print_logging) cerr << indentation << "checking " << qual_type.getAsString();
704 // if (dyn_cast<TypedefType>(qual_type)) {
705 // if (print_logging) cerr << indentation << " and returning " << dyn_cast<TypedefType>(qual_type)->getDecl()->getQualifiedNameAsString() <<
706 // endl << endl;
707 // return dyn_cast<TypedefType>(qual_type)->getDecl()->getQualifiedNameAsString() +
708 // (is_reference ? " &" : "");
709 // } else {
710 // if (print_logging) cerr << indentation << " and returning (no typedef) " << qual_type.getAsString() << endl << endl;
711 // return qual_type.getAsString() + pointer_suffix.str() + reference_suffix;
712 // }
713 
714  } else {
715 
716  // THIS APPROACH DOES NOT GENERATE PORTABLE STL NAMES LIKE THE LINE BELOW IS libc++ only not libstdc++
717  // std::__1::basic_string<char, struct std::__1::char_traits<char>, class std::__1::allocator<char> >
718 
719  // this isn't great because it loses the typedef'd names of things, but it ALWAYS works
720  // There is no confusion with reference types or typedefs or const/volatile
721  // EXCEPT: it generates a elaborated type specifier which can't be used in certain places
722  // http://en.cppreference.com/w/cpp/language/elaborated_type_specifier
723  auto canonical_qual_type = original_qual_type.getCanonicalType();
724 
725  //printf("Canonical qualtype typedeftype cast: %p\n",(void*) dyn_cast<TypedefType>(canonical_qual_type));
726 
727  if (print_logging) cerr << indentation << "returning canonical: " << canonical_qual_type.getAsString() << endl << endl;
728  if (turn_logging_off) print_logging--;
729 
730  return canonical_qual_type.getAsString();
731  }
732 #endif
733 }
734 
735 
736 
737 /**
738  * Returns a vector of the default value for each parameter for the method - if there is no default
739  * parameter, an empty string is returned in that position (which is different than a default parameter of
740  * an empty string which would be returned as the string '""'
741  */
742 vector<string> get_default_argument_values(CompilerInstance & compiler_instance,
743  const CXXMethodDecl * method,
744  const string & annotation = "") {
745  vector<string> results;
746  auto parameter_count = method->getNumParams();
747  for (unsigned int i = 0; i < parameter_count; i++) {
748  auto param_decl = method->getParamDecl(i);
749 
750  if (param_decl->hasDefaultArg()) {
751  auto default_argument = param_decl->getDefaultArg();
752  auto source_range = default_argument->getSourceRange();
753  auto source = get_source_for_source_range(compiler_instance.getSourceManager(), source_range);
754  results.push_back(source);
755  } else {
756  results.push_back("");
757  }
758 
759  }
760  return results;
761 }
762 
763 vector<QualType> get_method_param_qual_types(CompilerInstance & compiler_instance,
764  const CXXMethodDecl * method,
765  string const & annotation) {
766  vector<QualType> results;
767  auto parameter_count = method->getNumParams();
768  for (unsigned int i = 0; i < parameter_count; i++) {
769  auto param_decl = method->getParamDecl(i);
770 
771 
772  Annotations annotations(param_decl);
773  if (annotation != "" && !annotations.has(annotation)) {
774  if (print_logging) cerr << "Skipping method parameter because it didn't have requested annotation: " << annotation << endl;
775  continue;
776  }
777  auto param_qual_type = param_decl->getType();
778  results.push_back(param_qual_type);
779  }
780  return results;
781 }
782 
783 vector<string> generate_variable_names(vector<QualType> qual_types, bool with_std_move) {
784  vector<string> results;
785  for (std::size_t i = 0; i < qual_types.size(); i++) {
786  if (with_std_move && qual_types[i]->isRValueReferenceType()) {
787  results.push_back(fmt::format("std::move(var{})", i+1));
788  } else {
789  results.push_back(fmt::format("var{}", i+1));
790  }
791  }
792  return results;
793 }
794 
795 
796 
797 
798 
799 
800 void print_specialization_info(const CXXRecordDecl * decl) {
801  auto name = get_canonical_name_for_decl(decl);
802  cerr << "*****" << endl;
803  if (decl->isDependentType()) {
804  cerr << fmt::format("{} is a dependent type", name) << endl;
805  }
806  if (auto spec = dyn_cast<ClassTemplateSpecializationDecl>(decl)) {
807  fprintf(stderr, "decl is a ClassTemplateSpecializationDecl: %p\n", (void *)decl);
808  cerr << name << endl;
809 
810  if (auto spec_tmpl = spec->getSpecializedTemplate()) {
811  fprintf(stderr, "Specialized template: %p, %s\n", (void *)spec_tmpl, spec_tmpl->getQualifiedNameAsString().c_str());
812  print_vector(Annotations(spec_tmpl).get(), "specialized template annotations", "", false);
813  } else {
814  cerr << "no spec tmpl" << endl;
815  }
816 
817 
818  if (dyn_cast<ClassTemplatePartialSpecializationDecl>(decl)) {
819  cerr << "It is also a partial specialization decl" << endl;
820  } else {
821  cerr << "It is NOT a PARTIAL specialization decl" << endl;
822  }
823 
824 
825  } else {
826  cerr << name << " is not a class template specialization decl" << endl;
827  }
828  cerr << "*****END" << endl;
829 
830 }
831 
832 
833 
834 
835 
836 /*
837 
838 CXXConstructorDecl * get_bidirectional_constructor(const CXXRecordDecl * klass) {
839  CXXConstructorDecl * result = nullptr;
840  bool got_constructor = false;
841  foreach_constructor(klass, [&](auto constructor){
842  if (got_constructor) {
843  cerr << "ERROR, MORE THAN ONE BIDIRECTIONAL CONSTRUCTOR" << endl;
844  }
845  got_constructor = true;
846  result = constructor;
847 
848  }, V8TOOLKIT_BIDIRECTIONAL_CONSTRUCTOR_STRING);
849  if (!got_constructor) {
850  cerr << "ERROR, NO BIDIRECTIONAL CONSTRUCTOR FOUND IN " << klass->getNameAsString() << endl;
851  }
852  return result;
853 }
854  */
855 
856 // This may be useful for autogenerating factory type information
857 // string get_bidirectional_constructor_parameter_typelists(const CXXRecordDecl * klass, bool leading_comma) {
858 // auto constructor = get_bidirectional_constructor(klass);
859 //
860 // // all internal params must come before all external params
861 // bool found_external_param = false;
862 // auto parameter_count = constructor->getNumParams();
863 // vector<string> internal_params;
864 // vector<string> external_params;
865 // for (unsigned int i = 0; i < parameter_count; i++) {
866 // auto param_decl = constructor->getParamDecl(i);
867 // if (has_annotation(param_decl, V8TOOLKIT_BIDIRECTIONAL_INTERNAL_PARAMETER_STRING)) {
868 // if (found_external_param) {
869 // cerr << "ERROR: Found internal parameter after external parameter found in " << klass->getNameAsString() << endl;
870 // throw std::exception();
871 // }
872 // internal_params.push_back(param_decl->getType().getAsString());
873 // } else {
874 // found_external_param = true;
875 // external_params.push_back(param_decl->getType().getAsString());
876 // }
877 // }
878 //
879 // stringstream result;
880 // if (leading_comma) {
881 // result << ", ";
882 // }
883 // result << "v8toolkit::TypeList<" << join(internal_params, ", ") << ">";
884 // result << ", ";
885 // result << "v8toolkit::TypeList<" << join(external_params, ", ") << ">";
886 //
887 // return result.str();
888 // }
889 
890 
891 
892 
893 
894 
895 
896 map<string, int> template_instantiations;
897 
898 // called for handling matches from the matcher
899 
900 // contains the matchers
901 
902 
903 
int matched_classes_returned
std::string name
vector< string > types_to_ignore_regex
int print_logging
::std::string string
Definition: gtest-port.h:1097
map< string, int > template_instantiations
bool generate_v8classwrapper_sfinae
STL namespace.
static ClassTemplate & get_or_create(const ClassTemplateDecl *decl)
vector< QualType > get_method_param_qual_types(CompilerInstance &compiler_instance, const CXXMethodDecl *method, string const &annotation)
ClassTemplate(const ClassTemplateDecl *decl)
std::string get_include_for_source_location(CompilerInstance &compiler_instance, const SourceLocation &source_location)
std::string get_type_string(QualType qual_type, const std::string &indentation)
std::string name
static vector< WrappedClass * > wrapped_classes
Definition: wrapped_class.h:29
static FunctionTemplate & get_or_create(const FunctionDecl *decl)
string header_for_every_class_wrapper_file
string get_sfinae_matching_wrapped_classes(const vector< unique_ptr< WrappedClass >> &wrapped_classes)
std::string get_include_for_type_decl(CompilerInstance &compiler_instance, const TypeDecl *type_decl)
bool is_good_record_decl(const CXXRecordDecl *decl)
vector< unique_ptr< FunctionTemplate > > function_templates
const FunctionDecl * decl
vector< string > get_default_argument_values(CompilerInstance &compiler_instance, const CXXMethodDecl *method, const string &annotation="")
std::string js_api_header
std::vector< std::string > used_constructor_names
std::string get_include_string_for_fileid(CompilerInstance &compiler_instance, FileID &file_id)
vector< string > includes_for_every_class_wrapper_file
void print_specialization_info(const CXXRecordDecl *decl)
vector< string > generate_variable_names(vector< QualType > qual_types, bool with_std_move)
vector< std::unique_ptr< ClassTemplate > > class_templates
const ClassTemplateDecl * decl
FunctionTemplate(const FunctionDecl *decl)
bool has(const std::string &target) const
Definition: annotations.h:67
std::string get_source_for_source_range(SourceManager &sm, SourceRange source_range)
vector< string > never_include_for_any_file
std::string get_canonical_name_for_decl(const TypeDecl *decl)
vector< string > base_types_to_ignore
map< string, string > cpp_to_js_type_conversions
std::string join(const T &source, const std::string &between=", ", bool leading_between=false)
bool has_wrapped_class(const CXXRecordDecl *decl)
map< string, string > static_method_renames
void print_vector(const vector< string > &vec, const string &header, const string &indentation, bool ignore_empty)