v8toolkit  0.0.1
Utility library for embedding V8 Javascript engine in a c++ program
wrapped_class.h
Go to the documentation of this file.
1 
2 #pragma once
3 
4 #include <sstream>
5 #include <set>
6 
7 #include "parsed_method.h"
8 #include "annotations.h"
9 
10 extern int MAX_DECLARATIONS_PER_FILE;
11 
12 // should be named "ParsedClass" or something, since both classes that will and will not be wrapped
13 // are put into this data structure
14 class MemberFunction;
15 class StaticFunction;
16 struct WrappedClass {
17 private:
18 
19  bool methods_parsed = false;
20  set<unique_ptr<MemberFunction>> member_functions;
21  set<unique_ptr<StaticFunction>> static_functions;
22 
23  bool members_parsed = false;
24  set<unique_ptr<DataMember>> members;
25  set<unique_ptr<ConstructorFunction>> constructors;
26 
27 
28 public:
29  static vector<WrappedClass *> wrapped_classes;
30 
31  CXXRecordDecl const * decl = nullptr;
32 
33  // if this wrapped class is a template instantiation, what was it patterned from -- else nullptr
34  CXXRecordDecl const * instantiation_pattern = nullptr;
35 
36  /**
37  * Builds data structures associated with the methods of this class
38  */
39  void parse_all_methods();
40 
41 
42  // name of type that is guaranteed valid c++ (with appropriate included headers)
43  string class_name;
44 
45  /// this is the possibly shortened javascript name of the type - not necessarily valid in generated c++
46  string name_alias;
47 
48  set<string> include_files;
49 
50  // value here is the "compilation cost" of creating the class itself even if it's empty.
51  // increase if too many empty classes end up in one file and make the compilation too big/take too long
53 
54  string my_header_filename = "";
55 
56  set<unique_ptr<MemberFunction>> const & get_member_functions();
57  set<unique_ptr<StaticFunction>> const & get_static_functions();
58  set<unique_ptr<DataMember>> & get_members();
59  set<unique_ptr<ConstructorFunction>> const & get_constructors();
60 
61  set<string> used_member_names;
62  set<string> used_static_names;
63  vector<string> data_errors;
64  set<WrappedClass *> derived_types;
65 
66  /// tracked base_types - if it's more than one, that's a data error because javascript only allows one
67  set<WrappedClass *> base_types;
68 
71  CompilerInstance & compiler_instance;
72 
73  string my_include; // the include for getting my type, including "" or <>
74  bool done = false;
75  bool valid = false; // guilty until proven innocent - don't delete !valid classes because they may be base classes for valid types
77  bool dumped = false; // this class has been dumped to file
78  set<WrappedClass *> used_classes; // classes this class uses in its wrapped functions/members/etc
80  bool force_no_constructors = false;
81 
82  // it's ok for types that won't be exposed to javascript to have wrapping errors associated with them
83  void set_error(string const & error_message);
84 
85  bool bidirectional = false;
86  CXXConstructorDecl const * bidirectional_constructor = nullptr;
87 
89  if (decl == nullptr) {
90  llvm::report_fatal_error(fmt::format("Tried to get_short_name on 'fake' WrappedClass {}", class_name).c_str());
91  }
92  return decl->getNameAsString();
93  }
94 
95  bool has_static_method(){return !this->static_functions.empty();}
96 
97  /**
98  * @return whether this type is a specialization of a template
99  */
101 
102  /**
103  * @param callback called on each parameterized type for this template specialization
104  */
105  void foreach_inheritance_level(function<void(WrappedClass &)> callback);
106 
107 
108  /**
109  * Sets a member name as being in use and sets valid = false if it was already in use
110  * For member functions and data members (not constructors or static methods)
111  * @param name name to add
112  */
113  void add_member_name(string const & name);
114 
115  void add_static_name(string const & name);
116 
117  // all the correct annotations and name overrides may not be available when the WrappedObject is initially created
118  void update_data() {
119  cerr << "Updating wrapped class data for " << class_name << endl;
120  string new_name = Annotations::names_for_record_decls[decl];
121  if (!new_name.empty()) {
122  cerr << "Got type alias: " << new_name << endl;
123  name_alias = new_name;
124  } else {
125  cerr << "no type alias" << endl;
126  }
127  cerr << "Went from " << this->annotations.get().size() << " annotations to ";
128  this->annotations = Annotations(this->decl);
129  cerr << this->annotations.get().size() << endl;
130  }
131 
133 
134  // if it was found by annotation, there's no way for V8ClassWrapper to know about it other than
135  // explicit sfinae for the specific class. Inheritance can be handled via a single std::is_base_of
136  if (found_method == FOUND_ANNOTATION) {
137  return fmt::format("std::is_same<T, {}>::value", class_name);
138  } else {
139  return "";
140  }
141  }
142 
143 
144  bool should_be_wrapped() const;
145 
146  bool ready_for_wrapping(set<WrappedClass *> dumped_classes) const;
147 
148  // return all the header files for all the types used by all the base types of the specified type
149  std::set<string> get_base_type_includes();
150 
151  std::set<string> get_derived_type_includes();
152 
153 
154  WrappedClass(const WrappedClass &) = delete;
155  WrappedClass & operator=(const WrappedClass &) = delete;
156 
157  // for newly created classes --- used for bidirectional classes that don't actually exist in the AST
158  WrappedClass(const std::string class_name, CompilerInstance & compiler_instance);
159 
160 
161  // for classes actually in the code being parsed
162  WrappedClass(const CXXRecordDecl * decl, CompilerInstance & compiler_instance, FOUND_METHOD found_method);
163 
165 
166  std::string get_derived_classes_string(int level = 0, const std::string indent = ""){
167  vector<string> results;
168  // printf("%s In (%d) %s looking at %d derived classes\n", indent.c_str(), level, class_name.c_str(), (int)derived_types.size());
169  for (WrappedClass * derived_class : derived_types) {
170  results.push_back(derived_class->class_name);
171  // only use directly derived types now
172  //results.push_back(derived_class->get_derived_classes_string(level + 1, indent + " "));
173  }
174  // printf("%s Returning %s\n", indent.c_str(), join(results).c_str());
175  return join(results);
176  }
177 
179  auto i = base_types.begin();
180  while(i != base_types.end()) {
181  if (std::find(base_types_to_ignore.begin(), base_types_to_ignore.end(), (*i)->class_name) !=
182  base_types_to_ignore.end()) {
183  base_types.erase(i++); // move iterator before erasing
184  } else {
185  i++;
186  }
187  };
188  if (base_types.size() > 1) {
189  data_error(fmt::format("Type {} has more than one base class - this isn't supported because javascript doesn't support MI\n", class_name));
190 
191  }
192  return base_types.size() ? (*base_types.begin())->class_name : "";
193  }
194 
196 
197  static void insert_wrapped_class(WrappedClass * wrapped_class) {
198  WrappedClass::wrapped_classes.push_back(wrapped_class);
199  }
200 
201  /**
202  * Returns the wrapped class corresponding to the decl if it exists
203  * @param decl the decl to search for in existing wrapped classes
204  * @return the existing wrapped class or nullptr if no match found
205  */
206  static WrappedClass * get_if_exists(const CXXRecordDecl * decl) {
207  if (decl == nullptr) {
208  return nullptr;
209  }
210  for (auto * wrapped_class : WrappedClass::wrapped_classes) {
211  if (wrapped_class->decl == decl) {
212  return wrapped_class;
213  }
214  }
215  return nullptr;
216  }
217 
218  static WrappedClass & get_or_insert_wrapped_class(const CXXRecordDecl * decl,
219  CompilerInstance & compiler_instance,
220  FOUND_METHOD found_method) {
221 
222  if (decl->isDependentType()) {
223  llvm::report_fatal_error("unpexpected dependent type");
224  }
225 
226  auto class_name = get_canonical_name_for_decl(decl);
227 
228  // if this decl isn't a definition, get the actual definition
229  if (!decl->isThisDeclarationADefinition()) {
230 
231  cerr << class_name << " is not a definition - getting definition..." << endl;
232  if (!decl->hasDefinition()) {
233 
234  llvm::report_fatal_error(fmt::format("{} doesn't have a definition", class_name).c_str());
235  }
236 
237  decl = decl->getDefinition();
238  }
239 
240 
241  //fprintf(stderr, "get or insert wrapped class %p\n", (void*)decl);
242  //fprintf(stderr, " -- class name %s\n", class_name.c_str());
243  for (auto & wrapped_class : wrapped_classes) {
244 
245  if (wrapped_class->class_name == class_name) {
246 
247  // promote found_method if FOUND_BASE_CLASS is specified - the type must ALWAYS be wrapped
248  // if it is the base of a wrapped type
249  if (found_method == FOUND_BASE_CLASS) {
250 
251  // if the class wouldn't otherwise be wrapped, need to make sure no constructors are created
252  if (!wrapped_class->should_be_wrapped()) {
253  wrapped_class->force_no_constructors = true;
254  }
255  wrapped_class->found_method = FOUND_BASE_CLASS;
256  }
257  //fprintf(stderr, "returning existing object: %p\n", (void *)wrapped_class.get());
258  return *wrapped_class;
259  }
260  }
261  auto new_wrapped_class = new WrappedClass(decl, compiler_instance, found_method);
262  WrappedClass::wrapped_classes.push_back(new_wrapped_class);
263  return *new_wrapped_class;
264 
265  }
266 
267  // returns true if the found_method on this class means the class will be wrapped
269 
270 
271 };
void set_error(string const &error_message)
bool bidirectional
Definition: wrapped_class.h:85
void add_member_name(string const &name)
set< WrappedClass * > used_classes
Definition: wrapped_class.h:78
vector< string > data_errors
Definition: wrapped_class.h:63
FOUND_METHOD
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
WrappedClass()=default
std::string get_derived_classes_string(int level=0, const std::string indent="")
::std::string string
Definition: gtest-port.h:1097
void data_error(const string &error)
const vector< string > get() const
Definition: annotations.h:42
std::string get_base_class_string()
bool found_method_means_wrapped()
bool should_be_wrapped() const
int declaration_count
Definition: wrapped_class.h:52
static vector< WrappedClass * > wrapped_classes
Definition: wrapped_class.h:29
void parse_all_methods()
set< string > wrapper_extension_methods
Definition: wrapped_class.h:69
set< string > used_static_names
Definition: wrapped_class.h:62
set< string > include_files
Definition: wrapped_class.h:48
bool is_template_specialization()
set< string > wrapper_custom_extensions
Definition: wrapped_class.h:70
bool force_no_constructors
Definition: wrapped_class.h:80
CXXConstructorDecl const * bidirectional_constructor
Definition: wrapped_class.h:86
string my_include
Definition: wrapped_class.h:73
std::string generate_js_stub()
FOUND_METHOD found_method
Definition: wrapped_class.h:79
int MAX_DECLARATIONS_PER_FILE
std::string make_sfinae_to_match_wrapped_class() const
set< unique_ptr< StaticFunction > > const & get_static_functions()
set< WrappedClass * > base_types
tracked base_types - if it&#39;s more than one, that&#39;s a data error because javascript only allows one ...
Definition: wrapped_class.h:67
std::set< string > get_derived_type_includes()
void foreach_inheritance_level(function< void(WrappedClass &)> callback)
static WrappedClass * get_if_exists(const CXXRecordDecl *decl)
CXXRecordDecl const * instantiation_pattern
Definition: wrapped_class.h:34
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
set< unique_ptr< DataMember > > & get_members()
void add_static_name(string const &name)
WrappedClass & operator=(const WrappedClass &)=delete
string class_name
Definition: wrapped_class.h:43
std::string get_canonical_name_for_decl(const TypeDecl *decl)
CXXRecordDecl const * decl
Definition: wrapped_class.h:31
set< unique_ptr< ConstructorFunction > > const & get_constructors()
static map< const CXXRecordDecl *, string > names_for_record_decls
Definition: annotations.h:88
set< string > used_member_names
Definition: wrapped_class.h:61
Annotations annotations
Definition: wrapped_class.h:76
std::string get_bindings()
vector< string > base_types_to_ignore
set< unique_ptr< MemberFunction > > const & get_member_functions()
std::set< string > get_base_type_includes()
std::string join(const T &source, const std::string &between=", ", bool leading_between=false)
CompilerInstance & compiler_instance
Definition: wrapped_class.h:71
string my_header_filename
Definition: wrapped_class.h:54
void update_data()
bool ready_for_wrapping(set< WrappedClass * > dumped_classes) const
std::string get_short_name() const
Definition: wrapped_class.h:88