Switch to a simple X11-style license.
[polintos/scott/priv.git] / idlcomp / languages / c++ / interface-caller.cc
1 // idlcomp/languages/c++/interface-caller.cc -- caller-side interface definitions
2 //
3 // This software is copyright (c) 2006 Scott Wood <scott@buserror.net>.
4 // 
5 // Permission is hereby granted, free of charge, to any person obtaining a copy of
6 // this software and associated documentation files (the "Software"), to deal with
7 // the Software without restriction, including without limitation the rights to
8 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9 // of the Software, and to permit persons to whom the Software is furnished to do
10 // so, subject to the following condition:
11 // 
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 // 
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18 // CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
21 // SOFTWARE.
22
23 #include <targets.h>
24 #include "c++.h"
25
26 struct callback_data {
27         CPPFile *file;
28         Interface *iface;
29         bool prototype;
30         int count;
31 };
32
33 void CPPFile::output_iface_tbl_entry(Interface *iface, Interface *super)
34 {
35         file << indent << "::System::RunTime::IFaceTable "
36              << *super->get_fq_name()->flatten("_IDLNS_") << ";\n";
37 }
38
39 void CPPFile::tbl_callback(Interface *super, void *arg)
40 {
41         const callback_data *data = static_cast<const callback_data *>(arg);
42         data->file->output_iface_tbl_entry(data->iface, super);
43 }
44
45 void CPPFile::output_iface_tbl(Interface *iface)
46 {
47         file << indent << "struct iface_table {\n";
48
49         indent.indent_level++;
50
51         output_iface_tbl_entry(iface, iface);
52
53         callback_data data = { this, iface };
54         
55         iface->for_each_super<tbl_callback>(&data);
56         file << indent << "unsigned char *_end;\n";
57
58         indent.indent_level--;
59         
60         file << indent << "} iface_table;\n\n";
61 }
62
63 void cpp_output_one_param(ostream &file, Param *p,
64                           bool is_server, bool is_copy)
65 {
66         bool is_mutable = p->def.flags.field.Shared ||
67                           p->def.flags.field.Push ||
68                           (is_server && 
69                            (p->def.flags.field.Inline ||
70                             is_copy));
71  
72         if (dynamic_cast<Interface *>(*p->type)) {
73                 cpp_output_name(file, p->type);
74                 file << ' ';
75         } else if (p->type) {
76                 cpp_output_type(file, p->type, p->is_array(), is_mutable);
77         } else {
78                 cpp_output_type(file, p->def.basictype, is_mutable);
79         }
80
81         if (p->def.flags.field.Out)
82                 file << '*';
83         
84         if (dynamic_cast<Struct *>(*p->type) && !p->is_inline())
85                 file << '*';
86
87         file << **p->name;
88 }
89
90 void CPPFile::output_one_method(Interface *iface, Method *m, 
91                                 bool prototype, bool retval)
92 {
93         const String &nsname = get_definition_name(iface);
94
95         file << '\n' << indent << "inline ";
96         
97         if (!retval)
98                 file << "void ";
99         
100         if (!prototype)
101                 file << get_definition_name(iface) << "::";
102         
103         file << **m->name << '(';
104
105         if (prototype)
106                 indent.align_spaces = m->name->length() + 8;
107         else
108                 indent.align_spaces = m->name->length() + nsname.length() + 10;
109
110         if (!retval)
111                 indent.align_spaces += 5;
112         
113         bool first = true;
114         for (Method::entries_iterator i = m->entries_begin();
115              i != m->entries_end(); ++i)
116         {
117                 if (!first)
118                         file << ",\n" << indent;
119                 
120                 first = false;
121                 
122                 cpp_output_one_param(file, *i, false);
123         }
124         
125         file << ")";
126         indent.align_spaces = 0;
127
128         if (prototype) {
129                 file << ";\n";
130                 return;
131         }
132         
133         file << '\n' << indent << "{\n";
134         downscope();
135
136         // FIXME: It'd be nice to skip the NULL test in this cast,
137         // as 
138
139         file << indent << "_ptr->info->methods." << **m->name
140              << "(static_cast< ";
141
142         cpp_output_name(file, m->get_ns());
143
144         file << ">(*this)._ptr";
145
146         indent.align_spaces = m->name->length() + 21;
147         
148         for (Method::entries_iterator i = m->entries_begin();
149              i != m->entries_end(); ++i)
150         {
151                 Param *p = *i;
152                 file << ",\n" << indent << **p->name;
153         }
154         
155         file << ");\n";
156         indent.align_spaces = 0;
157
158         upscope();
159         file << indent << "}\n";
160 }
161
162 void CPPFile::output_methods(Interface *iface, Interface *super, bool prototype)
163 {
164         for (Interface::methods_iterator m = super->methods_begin();
165              m != super->methods_end(); ++m)
166         {
167                 output_one_method(iface, *m, prototype, false);
168                 
169                 /*
170                 if (method has at least one out parameter)
171                         output_one_method(iface, *m, prototype, true);
172                 */
173         }
174 }
175
176 void CPPFile::output_one_method_ptr(Method *m, Interface *iface)
177 {
178         Interface *super = static_cast<Interface *>(m->get_ns());
179         extra_newline();
180
181         file << indent << "void (*" << **m->name << ")(void *_this";
182         indent.align_spaces = m->name->length() + 9;
183         
184         for (Method::entries_iterator p = m->entries_begin();
185              p != m->entries_end(); ++p)
186         {
187                 file << ",\n" << indent;
188                 cpp_output_one_param(file, *p, false);
189         }
190         
191         file << ");\n";
192         indent.align_spaces = 0;
193 }
194
195 void CPPFile::output_method_ptrs_callback(Interface *super, void *arg)
196 {
197         const callback_data *data = static_cast<const callback_data *>(arg);
198
199         for (Interface::methods_iterator m = super->methods_begin();
200              m != super->methods_end(); ++m)
201                 data->file->output_one_method_ptr(*m, data->iface);
202 }
203
204 void CPPFile::output_method_ptrs(Interface *iface)
205 {
206         file << indent << "struct methods {\n";
207         downscope();
208         
209         callback_data data = { this, iface };
210
211         output_method_ptrs_callback(iface, &data);
212         iface->for_each_super<output_method_ptrs_callback>(&data);
213
214         upscope();
215         file << indent << "} methods;\n";
216 }
217
218 void CPPFile::output_internal(Interface *iface)
219 {
220         file << indent << "struct info_type {\n";
221
222         indent.indent_level++;
223         int super_count = 0;
224         
225         if (iface->supers_empty()) {
226                 // No supers.  If it's System.Object, output the top-level
227                 // info_type.  Otherwise, inherit from System.Object.
228                 
229                 if (iface != System_Object) {
230                         yyerrorf("Interface \"%s\" has no superinterface and "
231                                  "is not \"System.Object\".", 
232                                  iface->get_fq_name()->flatten()->c_str());
233
234                         throw UserError();
235                 }
236                         
237                 file << indent << "ptrdiff_t concrete;\n"
238                      << indent << "::System::RunTime::IFaceTable "
239                                   "*concrete_IFaceTable;\n";
240         } else {
241                 Interface *super = *iface->supers_begin();
242
243                 file << indent;
244                 cpp_output_name(file, super, "_i_");
245                 file << "::info_type parent;\n";
246         }
247
248         file << '\n';
249
250         output_iface_tbl(iface);
251         output_method_ptrs(iface);
252         
253         indent.indent_level--;
254         
255         file << indent << "};\n\n"
256              << indent << "const info_type *const info;\n";
257
258         file << '\n' << indent << "_i_" << **iface->name << '(';
259         indent.align_spaces = iface->name->length() + 4;
260         cpp_output_name(file, iface, "_i_");
261
262         file << "::info_type *INFO) :\n"
263              << indent << "info(INFO)\n";
264         
265         indent.align_spaces = 0;
266         file << indent << "{}\n";
267 }
268
269 void CPPFile::output_downcast_proto(Interface *iface, Interface *super)
270 {
271         file << '\n' << indent << "static inline " << **iface->name << " downcast(";
272         cpp_output_type(file, super, false, false);
273         file << "oldptr);\n";
274 }
275
276 void CPPFile::output_downcast(Interface *iface, Interface *super)
277 {
278         const String &name = get_definition_name(iface);
279         const String &iname = get_definition_name(iface, "_i_");
280
281         file << '\n' << indent << "inline " << name << ' '
282              << name << "::downcast(";
283         cpp_output_type(file, super, false, false);
284         file << "oldptr)";
285         
286         file << "\n" << indent << "{\n";
287
288         indent.indent_level++;
289         
290         file << indent << "::System::_i_Object *_llptr = \n"
291              << indent << "\treinterpret_cast< ::System::_i_Object *>(oldptr._ptr);\n"
292              << indent << "return " << name << "(reinterpret_cast< "
293              << iname << " *>\n"
294              << indent << "\t(::System::RunTime::downcast(_llptr, " 
295              << name << "_ns::_guid.l)));\n";
296         
297         indent.indent_level--;
298         
299         file << indent << "}\n";
300 }
301
302 void CPPFile::output_upcast_proto(Interface *iface, Interface *super)
303 {
304         const String &supername = *super->get_fq_name("_ns")->flatten("::");
305         file << indent << "inline operator ::" << supername << "();\n";
306 }
307
308 void CPPFile::output_upcast(Interface *iface, Interface *super)
309 {
310         const String &supername = *super->get_fq_name("_ns")->flatten("::");
311
312         file << '\n' << indent << "inline "
313              << get_definition_name(iface)
314              << "::operator ::" << supername << "()\n"
315              << indent << "{\n";
316         
317         indent.indent_level++;
318         
319         // A space is added after the final '<', as GCC gives a parse
320         // error otherwise.  I'm not sure exactly why...
321         
322         file << indent << "uintptr_t ptr = reinterpret_cast<uintptr_t>(_ptr);\n\n"
323              << indent << "if (!_ptr)\n"
324              << indent << "\treturn NULL;\n\n"
325              << indent << "ptr += _ptr->info->iface_table." 
326              << *super->get_fq_name()->flatten("_IDLNS_") 
327              << ".offset;\n"
328              << indent << "return ::" << supername << "(reinterpret_cast< ";
329         
330         cpp_output_name(file, super, "_i_");
331         
332         file << " *>(ptr));\n";
333         
334         indent.indent_level--;
335         
336         file << indent << "}\n";
337 }
338
339 void CPPFile::wrapper_cast_callback(Interface *super, void *arg)
340 {
341         const callback_data *data = static_cast<const callback_data *>(arg);
342         
343         data->file->output_downcast(data->iface, super);
344         data->file->output_upcast(data->iface, super);
345 }
346
347 void CPPFile::wrapper_cast_proto_callback(Interface *super, void *arg)
348 {
349         const callback_data *data = static_cast<const callback_data *>(arg);
350         
351         data->file->output_downcast_proto(data->iface, super);
352         data->file->output_upcast_proto(data->iface, super);
353 }
354
355 void CPPFile::output_wrapper(Interface *iface)
356 {
357         const String &name = **iface->name;
358
359         file << indent << "_i_" << name << " *_ptr;\n\n";
360         
361         file << indent << name << "()\n"
362              << indent << "{\n"
363              << indent << "\t_ptr = NULL;\n"
364              << indent << "}\n\n";
365
366         file << indent << name << "(_i_" << name << " *_other)\n"
367              << indent << "{\n"
368              << indent << "\t_ptr = _other;\n"
369              << indent << "}\n\n";
370
371         file << indent << "operator _i_" << name << " *()\n"
372              << indent << "{\n"
373              << indent << "\treturn _ptr;\n"
374              << indent << "}\n";
375
376         do_extra_newline = true;
377         
378         callback_data data = { this, iface, true };
379
380         iface->for_each_super<wrapper_cast_proto_callback>(&data);
381
382         output_methods(iface, iface, true);
383         iface->for_each_super<wrapper_method_callback>(&data);
384 }
385
386 void CPPFile::output_casts(Interface *iface)
387 {
388         callback_data data = { this, iface };
389
390         iface->for_each_super<wrapper_cast_callback>(&data);
391 }
392
393 void CPPFile::wrapper_method_callback(Interface *super, void *arg)
394 {
395         const callback_data *data = static_cast<const callback_data *>(arg);
396
397         data->file->output_methods(data->iface, super, data->prototype);
398 }
399
400 void CPPFile::output_method_defs(Interface *iface)
401 {
402         output_methods(iface, iface, false);
403
404         callback_data data = { this, iface, false };
405         iface->for_each_super<wrapper_method_callback>(&data);
406 }