df12a7636bd835f0717605df3244a7cf1917d8f2
[supertux.git] / tools / miniswig / create_wrapper.cpp
1 #include <config.h>
2
3 #include "tree.hpp"
4 #include <iostream>
5 #include <sstream>
6 #include <stdexcept>
7 #include "create_wrapper.hpp"
8 #include "globals.hpp"
9
10 void
11 WrapperCreator::create_wrapper(Namespace* ns)
12 {
13     std::string fromfile = original_file != "" ? original_file : inputfile;
14
15     if(selected_namespace != "") {
16         ns_prefix = selected_namespace;
17         ns_prefix += "::";
18     }
19
20     // hpp file
21     hppout
22         << "/**\n"
23         << " * WARNING: This file is automatically generated from:\n"
24         << " *  '" << fromfile << "'\n"
25         << " * DO NOT CHANGE\n"
26         << " */\n"
27         << "#ifndef __" << modulename << "_WRAPPER_H__\n"
28         << "#define __" << modulename << "_WRAPPER_H__\n"
29         << "\n"
30         << "#include <squirrel.h>\n"
31         << "\n"
32         << "namespace Scripting\n"
33         << "{\n"
34         << "\n";
35
36     hppout << "void register_" << modulename << "_wrapper(HSQUIRRELVM v);\n"
37            << "\n";
38
39     for(std::vector<AtomicType*>::iterator i = ns->types.begin();
40             i != ns->types.end(); ++i) {
41         AtomicType* type = *i;
42         Class* _class = dynamic_cast<Class*> (type);
43         if(_class == 0)
44             continue;
45
46         hppout << "class " << _class->name << ";\n";
47         hppout << "void create_squirrel_instance(HSQUIRRELVM v, "
48                << ns_prefix << _class->name
49                << "* object, bool setup_releasehook = false);\n";
50     }
51     hppout <<"\n"
52            << "}\n"
53            << "\n"
54            << "#endif\n";
55
56     // cpp header
57     out << "/**\n"
58         << " * WARNING: This file is automatically generated from:\n"
59         << " *  '" << fromfile << "'\n"
60         << " * DO NOT CHANGE\n"
61         << " */\n"
62         << "#include <config.h>\n"
63         << "\n"
64         << "#include <new>\n"
65         << "#include <assert.h>\n"
66         << "#include <string>\n"
67         << "#include <sstream>\n"
68         << "#include <squirrel.h>\n"
69         << "#include \"squirrel_error.hpp\"\n"
70         << "#include \"wrapper.interface.hpp\"\n"
71         << "\n"
72         << "namespace Scripting\n"
73         << "{\n"
74         << "namespace Wrapper\n"
75         << "{\n"
76         << "\n";
77
78     for(std::vector<AtomicType*>::iterator i = ns->types.begin();
79             i != ns->types.end(); ++i) {
80         AtomicType* type = *i;
81         Class* _class = dynamic_cast<Class*> (type);
82         if(_class != 0)
83             create_class_wrapper(_class);
84     }
85     for(std::vector<Function*>::iterator i = ns->functions.begin();
86             i != ns->functions.end(); ++i) {
87         create_function_wrapper(0, *i);
88     }
89
90     out << "} // end of namespace Wrapper\n";
91
92     for(std::vector<AtomicType*>::iterator i = ns->types.begin();
93             i != ns->types.end(); ++i) {
94         AtomicType* type = *i;
95         Class* _class = dynamic_cast<Class*> (type);
96         if(_class != 0)
97             create_squirrel_instance(_class);
98     }
99
100     out << "void register_" << modulename << "_wrapper(HSQUIRRELVM v)\n"
101         << "{\n"
102         << ind << "using namespace Wrapper;\n"
103         << "\n";
104
105     create_register_constants_code(ns);
106     create_register_functions_code(ns);
107     create_register_classes_code(ns);
108
109     out << "}\n"
110         << "\n"
111         << "} // end of namespace Scripting\n";
112 }
113
114 void
115 WrapperCreator::create_register_function_code(Function* function, Class* _class)
116 {
117     if(function->type == Function::DESTRUCTOR)
118         return;
119
120     out << ind << "sq_pushstring(v, \"" << function->name << "\", -1);\n";
121     out << ind << "sq_newclosure(v, &"
122         << (_class != 0 ? _class->name + "_" : "") << function->name
123         << "_wrapper, 0);\n";
124
125     if(function->custom) {
126       out << ind << "sq_setparamscheck(v, SQ_MATCHTYPEMASKSTRING, " << function->parameter_spec << ");\n";
127     } else {
128       out << ind << "sq_setparamscheck(v, SQ_MATCHTYPEMASKSTRING, \"";
129
130       out << "x|t";
131
132       if(!function->parameters.empty())
133         {
134           std::vector<Parameter>::iterator p = function->parameters.begin();
135           
136           // Skip the first parameter since its a HSQUIRRELVM that is
137           // handled internally
138           if (function->suspend) {
139             ++p;
140           } else if (p->type.atomic_type == HSQUIRRELVMType::instance()) {
141             ++p;
142           }
143
144           for(; p != function->parameters.end(); ++p) {
145             if(p->type.atomic_type == &BasicType::INT) {
146               out << "i";
147             } else if(p->type.atomic_type == &BasicType::FLOAT) {
148               out << "n";
149             } else if(p->type.atomic_type == &BasicType::BOOL) {
150               out << "b";
151             } else if(p->type.atomic_type == StringType::instance()) {
152               out << "s";
153             } else {
154               out << ".";
155             }
156           }
157       }
158       out << "\");\n";
159     }
160
161     create_register_slot_code("function", function->name);
162     out << "\n";
163 }
164
165 void
166 WrapperCreator::create_register_functions_code(Namespace* ns)
167 {
168     for(std::vector<Function*>::iterator i = ns->functions.begin();
169             i != ns->functions.end(); ++i) {
170         Function* function = *i;
171         create_register_function_code(function, 0);
172     }
173 }
174
175 void
176 WrapperCreator::create_register_classes_code(Namespace* ns)
177 {
178     for(std::vector<AtomicType*>::iterator i = ns->types.begin();
179             i != ns->types.end(); ++i) {
180         AtomicType* type = *i;
181         Class* _class = dynamic_cast<Class*> (type);
182         if(_class == 0)
183             continue;
184         if(_class->super_classes.size() > 0)
185             continue;
186
187         create_register_class_code(_class);
188     }
189 }
190
191 void
192 WrapperCreator::create_register_class_code(Class* _class)
193 {
194     out << ind << "// Register class " << _class->name << "\n";
195     out << ind << "sq_pushstring(v, \""
196         << _class->name << "\", -1);\n";
197
198     if(_class->super_classes.size() > 0) {
199         if(_class->super_classes.size() > 1) {
200             std::ostringstream msg;
201             msg << "Multiple inheritance not supported (at class '"
202                 << _class->name << "')";
203             throw std::runtime_error(msg.str());
204         }
205
206         out << ind << "sq_pushstring(v, \""
207             << _class->super_classes[0]->name << "\", -1);\n";
208         out << ind << "sq_get(v, -3);\n";
209     }
210     out << ind << "if(sq_newclass(v, "
211         << (_class->super_classes.size() > 0 ? "SQTrue" : "SQFalse")
212         << ") < 0) {\n";
213     out << ind << ind << "std::ostringstream msg;\n";
214     out << ind << ind << "msg << \"Couldn't create new class '"
215         << _class->name << "'\";\n";
216     out << ind << ind << "throw SquirrelError(v, msg.str());\n";
217     out << ind << "}\n";
218
219     for(std::vector<ClassMember*>::iterator i = _class->members.begin();
220             i != _class->members.end(); ++i) {
221         ClassMember* member = *i;
222         if(member->visibility != ClassMember::PUBLIC)
223             continue;
224         Function* function = dynamic_cast<Function*> (member);
225         if(function) {
226             create_register_function_code(function, _class);
227         }
228         Field* field = dynamic_cast<Field*> (member);
229         if(field) {
230             create_register_constant_code(field);
231         }
232     }
233
234     create_register_slot_code("class", _class->name);
235     out << "\n";
236
237     for(std::vector<Class*>::iterator i = _class->sub_classes.begin();
238             i != _class->sub_classes.end(); ++i) {
239         Class* _class = *i;
240         create_register_class_code(_class);
241     }
242 }
243
244 void
245 WrapperCreator::create_register_constants_code(Namespace* ns)
246 {
247     for(std::vector<Field*>::iterator i = ns->fields.begin();
248             i != ns->fields.end(); ++i) {
249         Field* field = *i;
250         create_register_constant_code(field);
251     }
252 }
253
254 void
255 WrapperCreator::create_register_constant_code(Field* field)
256 {
257     if(!field->has_const_value)
258         return;
259     out << ind << "sq_pushstring(v, \"" << field->name << "\", -1);\n";
260     if(field->type->atomic_type == &BasicType::INT) {
261         out << ind << "sq_pushinteger(v, " << field->const_int_value << ");\n";
262     } else if(field->type->atomic_type == &BasicType::FLOAT) {
263         out << ind << "sq_pushfloat(v, " << field->const_float_value << ");\n";
264     } else if(field->type->atomic_type == StringType::instance()) {
265         out << ind << "sq_pushstring(v, \""
266             << field->const_string_value << "\", -1);\n";
267     } else {
268       throw std::runtime_error("Constant is not int, float or string");
269     }
270     create_register_slot_code("constant", field->name);
271     out << "\n";
272 }
273
274 void
275 WrapperCreator::create_register_slot_code(const std::string& what,
276                                           const std::string& name)
277 {
278     out << ind << "if(SQ_FAILED(sq_createslot(v, -3))) {\n";
279     out << ind << ind << "throw SquirrelError(v, \""
280         << "Couldn't register " << what << " '" << name << "'\");\n";
281     out << ind << "}\n";
282 }
283
284 void
285 WrapperCreator::create_function_wrapper(Class* _class, Function* function)
286 {
287     if(function->type == Function::DESTRUCTOR)
288         assert(false);
289
290     std::string ns_prefix;
291     if(selected_namespace != "")
292         ns_prefix = selected_namespace + "::";
293     if(function->type == Function::CONSTRUCTOR)
294         function->name = "constructor";
295
296     out << "static SQInteger ";
297     if(_class != 0) {
298         out << _class->name << "_";
299     }
300     out << function->name << "_wrapper(HSQUIRRELVM vm)\n"
301         << "{\n";
302     // avoid warning...
303     if(_class == 0 && function->parameters.empty()
304             && function->return_type.is_void()
305             && function->type != Function::CONSTRUCTOR) {
306         out << ind << "(void) vm;\n";
307     }
308
309     // retrieve pointer to class instance
310     if(_class != 0 && function->type != Function::CONSTRUCTOR) {
311         out << ind << "SQUserPointer data;\n";
312         out << ind << "if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0)) || !data) {\n";
313         out << ind << ind << "sq_throwerror(vm, _SC(\"'" << function->name << "' called without instance\"));\n";
314         out << ind << ind << "return SQ_ERROR;\n";
315         out << ind << "}\n";
316         out << ind << ns_prefix <<  _class->name << "* _this = reinterpret_cast<" << ns_prefix << _class->name << "*> (data);\n";
317     }
318
319     // custom function?
320     if(function->custom) {
321         if(function->type != Function::FUNCTION) 
322             throw std::runtime_error(
323                     "custom not allow constructor+destructor yet");
324         if(function->return_type.atomic_type != SQIntegerType::instance())
325             throw std::runtime_error("custom function '" + function->name + "' has to return SQInteger");
326         if(function->parameters.size() != 1)
327             throw std::runtime_error(
328                     "custom function '" + function->name + "' must have 1 HSQUIRRELVM parameter");
329
330         out << ind << "return ";
331         if(_class != 0)
332             out << "_this->";
333         else
334             out << ns_prefix;
335         out << function->name << "(vm);\n";
336         out << "}\n";
337         out << "\n";
338         return;
339     }
340
341     // declare and retrieve arguments
342     int i = 0;
343     int arg_offset = 2;
344     for(std::vector<Parameter>::iterator p = function->parameters.begin();
345             p != function->parameters.end(); ++p) {
346         if(i == 0 && p->type.atomic_type == HSQUIRRELVMType::instance()) {
347             out << ind << "HSQUIRRELVM arg0 = vm;\n";
348             arg_offset--;
349         } else {
350             char argname[64];
351             snprintf(argname, sizeof(argname), "arg%d", i);
352             prepare_argument(p->type, i + arg_offset, argname);
353         }
354         ++i;
355     }
356
357     // call function
358     out << "\n";
359     out << ind << "try {\n";
360     out << ind << ind;
361     if(!function->return_type.is_void()) {
362         function->return_type.write_c_type(out);
363         out << " return_value = ";
364     }
365     if(_class != 0) {
366         if(function->type == Function::CONSTRUCTOR) {
367             out << ns_prefix << _class->name << "* _this = new " << ns_prefix;
368         } else {
369             out << "_this->";
370         }
371     } else {
372         out << ns_prefix;
373     }
374     if(function->type == Function::CONSTRUCTOR) {
375         out << _class->name << "(";
376     } else {
377         out << function->name << "(";
378     }
379     for(size_t i = 0; i < function->parameters.size(); ++i) {
380         if(i != 0)
381             out << ", ";
382         const Parameter param = function->parameters[i];
383         if(param.type.ref == 0 && param.type.pointer == 0) {
384             if(param.type.atomic_type == &BasicType::INT)
385                 out << "static_cast<int> (arg" << i << ")";
386             else if(param.type.atomic_type == &BasicType::FLOAT)
387                 out << "static_cast<float> (arg" << i << ")";
388             else if(param.type.atomic_type == &BasicType::BOOL)
389                 out << "arg" << i << " == SQTrue";
390             else
391                 out << "arg" << i;
392         } else {
393             out << "arg" << i;
394         }
395     }
396     out << ");\n";
397     if(function->type == Function::CONSTRUCTOR) {
398         out << ind << "if(SQ_FAILED(sq_setinstanceup(vm, 1, _this))) {\n";
399         out << ind << ind << "sq_throwerror(vm, _SC(\"Couldn't setup instance of '" << _class->name << "' class\"));\n";
400         out << ind << ind << "return SQ_ERROR;\n";
401         out << ind << "}\n";
402         out << ind << "sq_setreleasehook(vm, 1, "
403             << _class->name << "_release_hook);\n";
404     }
405     out << "\n";
406     // push return value back on stack and return
407     if(function->suspend) {
408         if(!function->return_type.is_void()) {
409             std::stringstream msg;
410             msg << "Function '" << function->name << "' declared as suspend"
411                 << " but has a return value.";
412             throw std::runtime_error(msg.str());
413         }
414         out << ind << ind << "return sq_suspendvm(vm);\n";
415     } else if(function->return_type.is_void()) {
416         out << ind << ind << "return 0;\n";
417     } else {
418         push_to_stack(function->return_type, "return_value");
419         out << ind << ind << "return 1;\n";
420     }
421
422     out << "\n";
423     out << ind << "} catch(std::exception& e) {\n";
424     out << ind << ind << "sq_throwerror(vm, e.what());\n";
425     out << ind << ind << "return SQ_ERROR;\n";
426     out << ind << "} catch(...) {\n";
427     out << ind << ind << "sq_throwerror(vm, _SC(\"Unexpected exception while executing function '" << function->name << "'\"));\n";
428     out << ind << ind << "return SQ_ERROR;\n";
429     out << ind << "}\n";
430     out << "\n";
431
432     out << "}\n";
433     out << "\n";
434 }
435
436 void
437 WrapperCreator::prepare_argument(const Type& type, size_t index,
438         const std::string& var)
439 {
440     if(type.ref > 0 && type.atomic_type != StringType::instance())
441         throw std::runtime_error("References not handled yet");
442     if(type.pointer > 0)
443         throw std::runtime_error("Pointers not handled yet");
444     if(type.atomic_type == &BasicType::INT) {
445         out << ind << "SQInteger " << var << ";\n";
446         out << ind << "if(SQ_FAILED(sq_getinteger(vm, " << index << ", &" << var << "))) {\n";
447         out << ind << ind << "sq_throwerror(vm, _SC(\"Argument " << (index-1) << " not an integer\"));\n";
448         out << ind << ind << "return SQ_ERROR;\n";
449         out << ind << "}\n";
450     } else if(type.atomic_type == &BasicType::FLOAT) {
451         out << ind << "SQFloat " << var << ";\n";
452         out << ind << "if(SQ_FAILED(sq_getfloat(vm, " << index << ", &" << var << "))) {\n";
453         out << ind << ind << "sq_throwerror(vm, _SC(\"Argument " << (index-1) << " not a float\"));\n";
454         out << ind << ind << "return SQ_ERROR;\n";
455         out << ind << "}\n";
456     } else if(type.atomic_type == &BasicType::BOOL) {
457         out << ind << "SQBool " << var << ";\n";
458         out << ind << "if(SQ_FAILED(sq_getbool(vm, " << index << ", &" << var << "))) {\n";
459         out << ind << ind << "sq_throwerror(vm, _SC(\"Argument " << (index-1) << " not a bool\"));\n";
460         out << ind << ind << "return SQ_ERROR;\n";
461         out << ind << "}\n";
462     } else if(type.atomic_type == StringType::instance()) {
463         out << ind << "const SQChar* " << var << ";\n";
464         out << ind << "if(SQ_FAILED(sq_getstring(vm, " << index << ", &" << var << "))) {\n";
465         out << ind << ind << "sq_throwerror(vm, _SC(\"Argument " << (index-1) << " not a string\"));\n";
466         out << ind << ind << "return SQ_ERROR;\n";
467         out << ind << "}\n";
468     } else {
469         std::ostringstream msg;
470         msg << "Type '" << type.atomic_type->name << "' not supported yet.";
471         throw std::runtime_error(msg.str());
472     }
473 }
474
475 void
476 WrapperCreator::push_to_stack(const Type& type, const std::string& var)
477 {
478     if(type.ref > 0 && type.atomic_type != StringType::instance())
479         throw std::runtime_error("References not handled yet");
480     if(type.pointer > 0)
481         throw std::runtime_error("Pointers not handled yet");
482     out << ind << ind;
483     if(type.atomic_type == &BasicType::INT) {
484         out << "sq_pushinteger(vm, " << var << ");\n";
485     } else if(type.atomic_type == &BasicType::FLOAT) {
486         out << "sq_pushfloat(vm, " << var << ");\n";
487     } else if(type.atomic_type == &BasicType::BOOL) {
488         out << "sq_pushbool(vm, " << var << ");\n";
489     } else if(type.atomic_type == StringType::instance()) {
490         out << "sq_pushstring(vm, " << var << ".c_str(), "
491             << var << ".size());\n";
492     } else {
493         std::ostringstream msg;
494         msg << "Type '" << type.atomic_type->name << "' not supported yet.";
495         throw std::runtime_error(msg.str());
496     }
497 }
498
499 void
500 WrapperCreator::create_class_wrapper(Class* _class)
501 {
502     create_class_release_hook(_class);
503     for(std::vector<ClassMember*>::iterator i = _class->members.begin();
504             i != _class->members.end(); ++i) {
505         ClassMember* member = *i;
506         if(member->visibility != ClassMember::PUBLIC)
507             continue;
508         Function* function = dynamic_cast<Function*> (member);
509         if(!function)
510             continue;
511         // don't wrap destructors
512         if(function->type == Function::DESTRUCTOR)
513             continue;
514         create_function_wrapper(_class, function);
515     }
516 }
517
518 void
519 WrapperCreator::create_squirrel_instance(Class* _class)
520 {
521     out << "void create_squirrel_instance(HSQUIRRELVM v, "
522         << ns_prefix << _class->name
523         << "* object, bool setup_releasehook)\n"
524         << "{\n"
525         << ind << "using namespace Wrapper;\n"
526         << "\n"
527         << ind << "sq_pushroottable(v);\n"
528         << ind << "sq_pushstring(v, \"" << _class->name << "\", -1);\n"
529         << ind << "if(SQ_FAILED(sq_get(v, -2))) {\n"
530         << ind << ind << "std::ostringstream msg;\n"
531         << ind << ind << "msg << \"Couldn't resolved squirrel type '"
532         << _class->name << "'\";\n"
533         << ind << ind << "throw SquirrelError(v, msg.str());\n"
534         << ind << "}\n"
535         << "\n"
536         << ind << "if(SQ_FAILED(sq_createinstance(v, -1)) || "
537         << "SQ_FAILED(sq_setinstanceup(v, -1, object))) {\n"
538         << ind << ind << "std::ostringstream msg;\n"
539         << ind << ind << "msg << \"Couldn't setup squirrel instance for "
540         << "object of type '" << _class->name << "'\";\n"
541         << ind << ind << "throw SquirrelError(v, msg.str());\n"
542         << ind << "}\n"
543         << ind << "sq_remove(v, -2); // remove object name\n"
544         << "\n"
545         << ind << "if(setup_releasehook) {\n"
546         << ind << ind << "sq_setreleasehook(v, -1, "
547         << _class->name << "_release_hook);\n"
548         << ind << "}\n"
549         << "\n"
550         << ind << "sq_remove(v, -2); // remove root table\n"
551         << "}\n"
552         << "\n";
553 }
554
555 void
556 WrapperCreator::create_class_release_hook(Class* _class)
557 {
558     out << "static SQInteger " << _class->name << "_release_hook(SQUserPointer ptr, SQInteger )\n"
559         << "{\n"
560         << ind << ns_prefix << _class->name
561         << "* _this = reinterpret_cast<" << ns_prefix << _class->name
562         << "*> (ptr);\n"
563         << ind << "delete _this;\n"
564         << ind << "return 0;\n"
565         << "}\n"
566         << "\n";
567 }