v8toolkit  0.0.1
Utility library for embedding V8 Javascript engine in a c++ program
class_handler.cpp
Go to the documentation of this file.
1 #include "wrapped_class.h"
2 #include "class_handler.h"
3 
4 
5 
6 void ClassHandler::run(const ast_matchers::MatchFinder::MatchResult &Result) {
7 
9 
10  if (matched_classes_returned % 10000 == 0) {
11  std::cerr << fmt::format("\n### Matcher results processed: {}", matched_classes_returned) << std::endl;
12  }
13 
14  // if the current result is matched from the "not std:: class"-bound matcher
15  if (const CXXRecordDecl * klass = Result.Nodes.getNodeAs<clang::CXXRecordDecl>("not std:: class")) {
16  auto class_name = get_canonical_name_for_decl(klass);
17 
18  std::cerr << fmt::format("Got a class that's not a std:: class {}", class_name) << std::endl;
19 
20 
21  if (klass->isDependentType()) {
22  cerr << "Skipping 'class with annotation' dependent type: " << class_name << endl;
23  return;
24  }
25 
26  auto name = get_canonical_name_for_decl(klass);
27  if (std::regex_match(name, regex("^(class\\s+|struct\\s+)?std::.*$"))) {
28  return;
29  }
30  if (std::regex_match(name, regex("^(class\\s+|struct\\s+)?__.*$"))) {
31  return;
32  }
33 
34  cerr << endl << "Got class definition: " << class_name << endl;
35  fprintf(stderr, "decl ptr: %p\n", (void *)klass);
36 
37 
38  if (!is_good_record_decl(klass)) {
39  cerr << "SKIPPING BAD RECORD DECL" << endl;
40  }
41 
42  cerr << "Storing it for later processing (unless dupe)" << endl;
43 
45  }
46 
47  // Store any annotations on templates so the annotations can be merged later with types instantiated from the template
48  if (const CXXRecordDecl * klass = Result.Nodes.getNodeAs<clang::CXXRecordDecl>("forward declaration with annotation")) {
49 
50  auto class_name = get_canonical_name_for_decl(klass);
51  cerr << endl << "Got forward declaration with annotation: " << class_name << endl;
52 
53  /* check to see if this has any annotations we should associate with its associated template */
54  auto described_tmpl = klass->getDescribedClassTemplate();
55  if (klass->isDependentType() && described_tmpl) {
56  fprintf(stderr, "described template %p, %s\n", (void *)described_tmpl, described_tmpl->getQualifiedNameAsString().c_str());
57  printf("Merging %d annotations with template %p\n", (int)Annotations(klass).get().size(), (void*)described_tmpl);
58  Annotations::annotations_for_class_templates[described_tmpl].merge(Annotations(klass));
59  }
60  }
61 
62 
63  if (const CXXRecordDecl * klass = Result.Nodes.getNodeAs<clang::CXXRecordDecl>("class derived from WrappedClassBase")) {
64  cerr << endl << "Got class derived from v8toolkit::WrappedClassBase: " << get_canonical_name_for_decl(klass) << endl;
65  if (!is_good_record_decl(klass)) {
66  cerr << "skipping 'bad' record decl" << endl;
67  return;
68  }
69  if (klass->isDependentType()) {
70  cerr << "skipping dependent type" << endl;
71  return;
72  }
73 
74  auto name = get_canonical_name_for_decl(klass);
75  if (std::regex_match(name, regex("^(class\\s+|struct\\s+)?std::.*$"))) {
76  return;
77  }
78  if (std::regex_match(name, regex("^(class\\s+|struct\\s+)?__.*$"))) {
79  return;
80  }
81 
82 
83  if (Annotations(klass).has(V8TOOLKIT_NONE_STRING)) {
84  cerr << "Skipping class because it's explicitly marked SKIP" << endl;
85  return;
86  }
87 
88 
90 
91 
92  if (!is_good_record_decl(klass)) {
93  cerr << "SKIPPING BAD RECORD DECL" << endl;
94  }
95 
96  cerr << "Storing it for later processing (unless dupe)" << endl;
98  }
99 
100  // Store annotations associated with a "using" statement to be merged with the "real" type
101  // only pick off the typedefNameDecl entries, but in 3.8, typedefNameDecl() matcher isn't available
102  if (auto typedef_decl = Result.Nodes.getNodeAs<clang::TypedefNameDecl>("named decl")) {
103  auto qual_type = typedef_decl->getUnderlyingType();
104  auto record_decl = qual_type->getAsCXXRecordDecl();
105 
106  // not interesting - it's for something like a primitive type like 'long'
107  if (!record_decl) {
108  return;
109  }
110  auto name = get_canonical_name_for_decl(record_decl);
111  if (std::regex_match(name, regex("^(class\\s+|struct\\s+)?std::.*$"))) {
112  return;
113  }
114  if (std::regex_match(name, regex("^(class\\s+|struct\\s+)?__.*$"))) {
115  return;
116  }
117 
118  Annotations::annotations_for_record_decls[record_decl].merge(Annotations(typedef_decl));
119 
120  if (Annotations(typedef_decl).has(V8TOOLKIT_NAME_ALIAS_STRING)) {
121  string name_alias = typedef_decl->getNameAsString();
122  std::cerr << fmt::format("Annotated type name: {} => {}", record_decl->getQualifiedNameAsString(), typedef_decl->getNameAsString()) << std::endl;
123  Annotations::names_for_record_decls[record_decl] = name_alias;
124 
125  // if the class has already been parsed, update it now
126  if (auto wrapped_class = WrappedClass::get_if_exists(record_decl)) {
127  wrapped_class->name_alias = name_alias;
128  }
129 
130  }
131 
132  }
133 
134 #ifdef TEMPLATE_INFO_ONLY
135 
136  if (const ClassTemplateSpecializationDecl * klass = Result.Nodes.getNodeAs<clang::ClassTemplateSpecializationDecl>("class")) {
137  auto class_name = get_canonical_name_for_decl(klass);
138 
139  bool print_logging = false;
140 
141  if (std::regex_search(class_name, std::regex("^(class|struct)\\s+v8toolkit"))) {
142  // if (std::regex_search(class_name, std::regex("remove_reference"))) {
143  print_logging = true;
144  cerr << fmt::format("Got class {}", class_name) << endl;
145  }
146 
147 
148 #ifdef TEMPLATE_FILTER_STD
149  if (std::regex_search(class_name, std::regex("^std::"))) {
150  if (print_logging) cerr << "Filtering out because in std::" << endl;
151  return;
152  }
153 #endif
154 
155 
156 
157  auto tmpl = klass->getSpecializedTemplate();
158  if (print_logging) {
159  cerr << "got specialized template " << tmpl->getQualifiedNameAsString() << endl;
160  }
161 
162 
163 
164 #ifdef TEMPLATE_FILTER_STD
165  if (std::regex_search(tmpl->getQualifiedNameAsString(), std::regex("^std::"))) {
166  return;
167  }
168 #endif
169 
170 
172 
173 
174  }
175 
176  if (const CXXMethodDecl * method = Result.Nodes.getNodeAs<clang::CXXMethodDecl>("method")) {
177  auto method_name = method->getQualifiedNameAsString();
178  const FunctionDecl * pattern = nullptr;
179 
180  if (!method->isTemplateInstantiation()) {
181  return;
182  }
183 #ifdef TEMPLATE_FILTER_STD
184  if (std::regex_search(method_name, std::regex("^std::"))) {
185  return;
186  }
187 #endif
188 
189  pattern = method->getTemplateInstantiationPattern();
190  if (!pattern) {
191  pattern = method;
192  }
193 
194  if (!pattern) {
195  llvm::report_fatal_error("method is template insantiation but pattern still nullptr");
196  }
197 
199 
200 
201 #if 0
202  bool print_logging = false;
203 
204  if (std::regex_search(method_name, std::regex("function_in_temp"))) {
205  cerr << endl << "*******Found function in templated class decl" << endl;
206  fprintf(stderr, "Method decl ptr: %p\n", (void*) method);
207  cerr << "is dependent context: " << method->isDependentContext() << endl;
208  cerr << "has dependent template info: " << (method->getDependentSpecializationInfo() != nullptr) << endl;
209  cerr << "is template instantiation: " << (method->isTemplateInstantiation()) << endl;
210  cerr << "has instantiation pattern: " << (method->getTemplateInstantiationPattern() != nullptr) << endl;
211  if (method->getTemplateInstantiationPattern()) {
212  fprintf(stderr, "template instantiation pattern ptr: %p\n", (void*) method->getTemplateInstantiationPattern());
213  }
214  print_logging = true;
215  }
216 
217  const FunctionTemplateDecl * function_template_decl = method->getDescribedFunctionTemplate();
218 
219  if (function_template_decl == nullptr && method->getTemplateSpecializationInfo()) {
220  function_template_decl = method->getTemplateSpecializationInfo()->getTemplate();
221  }
222 
223  if (function_template_decl) {
224  cerr << fmt::format("'real' templated method {} has instantiation pattern: {}", method_name, method->getTemplateInstantiationPattern() != nullptr) << endl;
225  fprintf(stderr, "method: %p, instantiation pattern: %p\n", (void *)method, (void*)method->getTemplateInstantiationPattern());
226  if (print_logging)
227  cerr << fmt::format("Got method {}", method_name) << endl;
228  FunctionTemplate::get_or_create(function_template_decl).instantiated();
229  } else {
230  if (print_logging) cerr << "not interesting method" << endl;
231  }
232  return;
233 
234 #endif
235 
236  }
237 #endif // end TEMPLATE_INFO_ONLY
238 }
239 
240 #if 0
241 void ClassHandler::handle_class(WrappedClass & wrapped_class, // class currently being handled (not necessarily top level)
242  bool top_level,
243  const std::string & indentation) {
244 
245 
246 
247 
248 
249 
250 
251  // prints out source for decl
252  //fprintf(stderr,"class at %s", decl2str(klass, this->ci).c_str());
253 
254  auto full_source_loc = FullSourceLoc(wrapped_class.decl->getLocation(), this->source_manager);
255 
256  auto file_id = full_source_loc.getFileID();
257 
258  // fprintf(stderr,"%sClass/struct: %s\n", indentation.c_str(), class_name.c_str());
259  // don't do the following code for inherited classes
260  if (top_level) {
261  top_level_class->include_files.insert(get_include_string_for_fileid(this->ci, file_id));
262  }
263 
264 // fprintf(stderr,"%s Decl at line %d, file id: %d %s\n", indentation.c_str(), full_source_loc.getExpansionLineNumber(),
265 // full_source_loc.getFileID().getHashValue(), this->ci.getBufferName(full_source_loc));
266 
267 // auto type_decl = dyn_cast<TypeDecl>(klass);
268 // assert(type_decl);
269 // auto type = type_decl->getTypeForDecl();
270 
271 //
272  // non-top-level methods handled by javascript prototype
273 // if (top_level) {
274 // if (print_logging) cerr << "About to process methods" << endl;
275 // for (CXXMethodDecl *method : wrapped_class.decl->methods()) {
276 // if (method->hasInheritedPrototype()) {
277 // cerr << fmt::format("Skipping method {} because it has inherited prototype",
278 // method->getNameAsString()) << endl;
279 // continue;
280 // }
281 //
282 // handle_method(*top_level_class, method);
283 // }
284 // }
285 
286  if (print_logging) cerr << "About to process fields for " << wrapped_class.name_alias << endl;
287 //
288 
289  if (print_logging) cerr << "About to process base class info" << endl;
290  // if this is true and the type ends up with no base type, it's an error
291 
292 
293 
294  // Only process constructors on the top-level object
295  if (top_level) {
296  if (wrapped_class.found_method == FOUND_BASE_CLASS) {
297  cerr << fmt::format(
298  "Not wrapping constructor for class only wrapped because it's base class of another wrapped type")
299  << endl;
300  } else if (wrapped_class.annotations.has(V8TOOLKIT_DO_NOT_WRAP_CONSTRUCTORS_STRING)) {
301  cerr << fmt::format("Not wrapping because class has DO NOT WRAP CONSTRUCTORS annotation") << endl;
302  } else if (wrapped_class.decl->isAbstract()) {
303  cerr << "Skipping all constructors because class is abstract: " << class_name << endl;
304  } else if (wrapped_class.force_no_constructors) {
305  cerr << fmt::format("'force no constructors' set on {} so skipping making constructors", class_name)
306  << endl;
307  } else {
308  if (print_logging)
309  cerr << fmt::format(
310  "About to process constructors for {} -- passed all checks to skip class constructors.",
311  wrapped_class.class_name) << endl;
312 
313 
314 
315  }
316  // if there's no constructor but there is a static method, then add the special command to expose
317  // the static methods
318  if (top_level_class->constructors.empty() && top_level_class->has_static_method) {
319  std::string static_name = wrapped_class.name_alias;
320  auto static_name_annotation = top_level_class->annotations.get_regex(
322  if (!static_name_annotation.empty()) {
323  static_name = static_name_annotation[0];
324  }
325  top_level_class->constructors.insert(
326  fmt::format("{} class_wrapper.expose_static_methods(\"{}\", isolate);\n", indentation,
327  static_name)
328  );
329  }
330 
331  // if this is a bidirectional class, make a minimal wrapper for it
333 
334  if (print_logging)
335  cerr << "Type " << top_level_class->class_name << " **IS** bidirectional" << endl;
336 
337  auto generated_header_name = fmt::format("\"v8toolkit_generated_bidirectional_{}.h\"",
338  top_level_class->get_short_name());
339 
340 
341  auto bidirectional_class_name = fmt::format("JS{}", top_level_class->get_short_name());
342  // auto js_wrapped_classes = get_wrapped_class_regex(bidirectional_class_name + "$"); SEE COMMENT BELOW
343  WrappedClass *js_wrapped_class = nullptr;
344  //if (js_wrapped_classes.empty()) { SEE COMMENT BELOW
345  cerr << "Creating new Wrapped class object for " << bidirectional_class_name << endl;
346  js_wrapped_class = new WrappedClass(bidirectional_class_name,
347  this->ci);
348  WrappedClass::insert_wrapped_class(js_wrapped_class);
349 
350  auto &bidirectional = *js_wrapped_class;
351  bidirectional.base_types.insert(top_level_class);
352 
353  cerr << fmt::format("Adding derived bidirectional type {} to base type: {}",
354  bidirectional.class_name, wrapped_class.name_alias) << endl;
355 
356  // set the bidirectional class as being a subclass of the non-bidirectional type
357  wrapped_class.derived_types.insert(&bidirectional);
358 
359  bidirectional.include_files.insert(generated_header_name);
360  bidirectional.my_include = generated_header_name;
361 
362  BidirectionalBindings bd(this->ci, *js_wrapped_class, wrapped_class);
363  bd.generate_bindings();
364 
365 
366  } else {
367  if (print_logging)
368  cerr << "Type " << top_level_class->class_name << " is not bidirectional" << endl;
369  }
370 
371  } // if top level
372 } // end method
373 #endif
374 
375 
377 
378  for (auto & warning : data_warnings) {
379  cerr << warning << endl;
380  }
381 
382 
383 
384  if (!data_errors.empty()) {
385  cerr << "Errors detected:" << endl;
386  for(auto & error : data_errors) {
387  cerr << error << endl;
388  }
389  llvm::report_fatal_error("Errors detected in source data");
390  exit(1);
391  }
392 
393 
394  cerr << "*************" << endl << "ABOUT TO GENERATE OUTPUT FILES" << endl << "*****************" << endl;
395 
396  generate_javascript_stub("js-api.js");
399 }
400 
401 
402 #if 0
403 void ClassHandler::handle_method(WrappedClass & klass, CXXMethodDecl * method) {
404 
405 
406  std::string full_method_name(method->getQualifiedNameAsString());
407  std::string short_method_name(method->getNameAsString());
408 
409  Annotations annotations(method);
410 
411 
412 
413 
414  if (PRINT_SKIPPED_EXPORT_REASONS) cerr << fmt::format("Handling method: {}", full_method_name) << endl;
415  // if (print_logging) cerr << "changing method name from " << full_method_name << " to ";
416 //
417 // auto regex = std::regex(fmt::format("{}::{}$", containing_class->getName().str(), short_method_name));
418 // auto replacement = fmt::format("{}::{}", top_level_class_decl->getName().str(), short_method_name);
419 // full_method_name = std::regex_replace(full_method_name, regex, replacement);
420 // if (print_logging) cerr << full_method_name << endl;
421 
422 
423  auto export_type = get_export_type(method, EXPORT_ALL);
424 
425  if (export_type != EXPORT_ALL && export_type != EXPORT_EXCEPT) {
426  if (PRINT_SKIPPED_EXPORT_REASONS) printf("Skipping method %s because not supposed to be exported %d\n",
427  full_method_name.c_str(), export_type);
428  return;
429  }
430 
431  // only deal with public methods
432  if (method->getAccess() != AS_public) {
433  if (PRINT_SKIPPED_EXPORT_REASONS) printf("**%s is not public, skipping\n", full_method_name.c_str());
434  return;
435  }
436 
437  // list of overloaded operator enumerated values
438  // http://llvm.org/reports/coverage/tools/clang/include/clang/Basic/OperatorKinds.def.gcov.html
439  if (method->isOverloadedOperator()) {
440 
441  // if it's a call operator (operator()), grab it
442  if (OO_Call == method->getOverloadedOperator()) {
443  // nothing specific to do, just don't skip it only because it's an overloaded operator
444  } else {
445 
446  // otherwise skip overloaded operators
448  printf("**skipping overloaded operator %s\n", full_method_name.c_str());
449  return;
450  }
451  }
452  if (dyn_cast<CXXConstructorDecl>(method)) {
453  if (PRINT_SKIPPED_EXPORT_REASONS) printf("**skipping constructor %s\n", full_method_name.c_str());
454  return;
455  }
456  if (dyn_cast<CXXDestructorDecl>(method)) {
457  if (PRINT_SKIPPED_EXPORT_REASONS) printf("**skipping destructor %s\n", full_method_name.c_str());
458  return;
459  }
460  // still want to keep the interface even if it's not implemented here
461  // if (method->isPure()) {
462  // if(!method->isVirtual()) {
463  // llvm::report_fatal_error("Got pure non-virtual method - not sure what that even means", false);
464  // }
465  // if (PRINT_SKIPPED_EXPORT_REASONS) printf("%s**skipping pure virtual %s\n", indentation.c_str(), full_method_name.c_str());
466  // return;
467  // }
468 
469 
470  // If the function is wrapped in derived classes as well, you run into problems where you can't find the right type to
471  // cast the internal field to to find a match for the function type. You may only get a Base* when you need to call void(Derived::*)()
472  // so if you only have the virtual function wrapped in Base, you'll always find the right type of object
473 // if (method->isVirtual()) {
474 // fprintf(stderr, "%s :: %s is virtual with %d overrides\n", klass.class_name.c_str(), full_method_name.c_str(), (int)method->size_overridden_methods());
475 // } else {
476 // fprintf(stderr, "%s :: %s isn't virtual\n", klass.class_name.c_str(), full_method_name.c_str());
477 // }
478  if (method->isVirtual() && method->size_overridden_methods()) {
479  if (PRINT_SKIPPED_EXPORT_REASONS) printf("**skipping derived-class override of base class virtual function %s\n", full_method_name.c_str());
480  return;
481  }
482 
483  if (dyn_cast<CXXConversionDecl>(method)) {
484  if (PRINT_SKIPPED_EXPORT_REASONS) cerr << fmt::format("**skipping user-defined conversion operator") << endl;
485  return;
486  }
487  if (PRINT_SKIPPED_EXPORT_REASONS) cerr << "Method passed all checks" << endl;
488 
489 
490 
491 
492 
493 
494  // cerr << "Checking if method name already used" << endl;
495  if (top_level_class->names.count(short_method_name)) {
496  data_error(fmt::format("Skipping duplicate name {}/{} :: {}\n",
497  top_level_class->class_name,
498  klass.class_name,
499  short_method_name));
500  return;
501  }
502  //cerr << "Inserting short name" << endl;
503  top_level_class->names.insert(short_method_name);
504 
505  auto parsed_method = make_unique<ParsedMethod>(this->ci, klass, method);
506 
507 } // end handle_method
508 
509 #endif
510 
511 
512 #if 0
513 std::string ClassHandler::handle_data_member(WrappedClass & containing_class, FieldDecl * field, const std::string & indentation) {
514  std::stringstream result;
515  auto export_type = get_export_type(field, EXPORT_ALL);
516  auto short_field_name = field->getNameAsString();
517  auto full_field_name = field->getQualifiedNameAsString();
518 
519 
520  cerr << "Processing data member for: " << containing_class.name_alias << ": " << full_field_name << endl;
521 // if (containing_class != top_level_class_decl) {
522 // if (print_logging) cerr << "************";
523 // }
524 // if (print_logging) cerr << "changing data member from " << full_field_name << " to ";
525 //
526 // std::string regex_string = fmt::format("{}::{}$", containing_class->getName().str(), short_field_name);
527 // auto regex = std::regex(regex_string);
528 // auto replacement = fmt::format("{}::{}", top_level_class_decl->getName().str(), short_field_name);
529 // full_field_name = std::regex_replace(full_field_name, regex, replacement);
530 // if (print_logging) cerr << full_field_name << endl;
531 
532 
533  top_level_class->names.insert(short_field_name);
534 
535 
536  // made up number to represent the overhead of making a new wrapped class
537  // even before adding methods/members
538  // This means that two wrapped classes will count as much towards rolling to the next file as
539  // one wrapped class with <THIS NUMBER> of wrapped members/functions
540 
541  update_wrapped_class_for_type(ci, *top_level_class, field->getType());
542 
543  string full_type_name = get_type_string(field->getType());
544 
545  std::cerr << fmt::format("incrementing declaration count for {} - data member", top_level_class->name_alias) << std::endl;
546 
547 
548 // printf("%sData member %s, type: %s\n",
549 // indentation.c_str(),
550 // field->getNameAsString().c_str(),
551 // field->getType().getAsString().c_str());
552  return result.str();
553 }
554 #endif
static map< const ClassTemplateDecl *, Annotations > annotations_for_class_templates
Definition: annotations.h:80
int matched_classes_returned
static WrappedClass & get_or_insert_wrapped_class(const CXXRecordDecl *decl, CompilerInstance &compiler_instance, FOUND_METHOD found_method)
string name_alias
this is the possibly shortened javascript name of the type - not necessarily valid in generated c++ ...
Definition: wrapped_class.h:46
int print_logging
::std::string string
Definition: gtest-port.h:1097
void data_error(const string &error)
static ClassTemplate & get_or_create(const ClassTemplateDecl *decl)
void update_wrapped_class_for_type(WrappedClass &wrapped_class, QualType qual_type)
std::string get_type_string(QualType qual_type, const std::string &indentation)
static FunctionTemplate & get_or_create(const FunctionDecl *decl)
set< string > include_files
Definition: wrapped_class.h:48
bool is_good_record_decl(const CXXRecordDecl *decl)
#define V8TOOLKIT_NAME_ALIAS_STRING
Definition: class_parser.h:57
virtual void onEndOfTranslationUnit() override
#define V8TOOLKIT_BIDIRECTIONAL_CLASS_STRING
Definition: class_parser.h:106
bool force_no_constructors
Definition: wrapped_class.h:80
#define PRINT_SKIPPED_EXPORT_REASONS
Definition: class_handler.h:5
vector< string > data_errors
std::string get_include_string_for_fileid(CompilerInstance &compiler_instance, FileID &file_id)
FOUND_METHOD found_method
Definition: wrapped_class.h:79
CompilerInstance & ci
Definition: class_handler.h:19
void print_specialization_info(const CXXRecordDecl *decl)
std::vector< string > get_regex(const string &regex_string) const
Definition: annotations.h:51
#define V8TOOLKIT_EXPOSE_STATIC_METHODS_AS_PREFIX
Definition: class_parser.h:73
void generate_bidirectional_classes(CompilerInstance &compiler_instance)
EXPORT_TYPE get_export_type(const NamedDecl *decl, EXPORT_TYPE previous)
virtual void run(const ast_matchers::MatchFinder::MatchResult &Result) override
#define V8TOOLKIT_NONE_STRING
Definition: class_parser.h:17
bool has(const std::string &target) const
Definition: annotations.h:67
static WrappedClass * get_if_exists(const CXXRecordDecl *decl)
std::string handle_data_member(WrappedClass &containing_class, FieldDecl *field, const std::string &indentation)
void generate_javascript_stub(std::string const &filename)
set< WrappedClass * > derived_types
Definition: wrapped_class.h:64
static void insert_wrapped_class(WrappedClass *wrapped_class)
bool has_static_method()
Definition: wrapped_class.h:95
string class_name
Definition: wrapped_class.h:43
vector< string > data_warnings
std::string get_canonical_name_for_decl(const TypeDecl *decl)
CXXRecordDecl const * decl
Definition: wrapped_class.h:31
static map< const CXXRecordDecl *, string > names_for_record_decls
Definition: annotations.h:88
Annotations annotations
Definition: wrapped_class.h:76
static map< const CXXRecordDecl *, Annotations > annotations_for_record_decls
Definition: annotations.h:83
void handle_class(WrappedClass &wrapped_class, bool top_level=true, const std::string &indentation="")
void generate_bindings()
#define V8TOOLKIT_DO_NOT_WRAP_CONSTRUCTORS_STRING
Definition: class_parser.h:102
std::string get_short_name() const
Definition: wrapped_class.h:88