Switch to a simple X11-style license.
[polintos/scott/priv.git] / idlcomp / languages / c++ / server.cc
1 // idlcomp/languages/c++/server.cc -- C++ server-side stubs
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 <fstream>
24 #include <errno.h>
25
26 #include <idlc.h>
27 #include <cdl.h>
28 #include <targets.h>
29 #include "c++.h"
30
31 using std::ofstream;
32
33 static void output_ifaceptrs(Class *cla, ostream &f)
34 {
35         f << "// " << *cla->get_fq_name()->flatten("::") << "\n"
36              "// This is a C++ server class mixin generated by idlc.\n"
37              "// Do not modify this file directly.\n"
38              "// This file provides data and methods used by the object system\n"
39              "// to connect this class to its interface(s).  Include it inside\n"
40              "// the class definition as \"public:\", and call the init_iface()\n"
41              "// method from your constructor(s).\n\n";
42
43         
44         Interface *conc = cla->concrete_iface;
45         assert(conc->get_num_chains() > 0);
46         
47         f << "struct IDL_ifaceptr {\n\t";
48         
49         for (int i = 0; i < conc->get_num_chains(); i++) {
50                 Interface *iface = conc->get_chain_head(i);
51
52                 // Skip the "fake" aggregation interface on the first chain.
53                 if (i == 0)
54                         iface = *iface->supers_begin();
55
56                 cpp_output_name(f, iface, "_i_");
57                 f << " p" << i << ";\n\t";
58         }
59                 
60         f << **cla->name << " *priv;\n"
61              "\tIDL_ifaceptr();\n"
62              "};\n\n";
63 }
64
65 static void output_classptr(Class *cla, Interface *iface, ostream &f)
66 {
67         int chain = cla->concrete_iface->super_to_chain(iface);
68
69         f << "static " << **cla->name << " *classptr(";
70         cpp_output_name(f, iface);
71         f << " ref)\n"
72              "{\n"
73              "\tif (ref && static_cast<const void *>(ref._ptr->info) ==\n"
74              "\t           static_cast<const void *>(&IDL_info.p" << chain << "))\n"
75              "\t{\n"
76              "\t\tuintptr_t ptr = reinterpret_cast<uintptr_t>(ref._ptr);\n"
77              "\t\tptr -= " << chain * target->pointer_size << ";\n"
78              "\t\treturn reinterpret_cast<IDL_ifaceptr *>(ref._ptr)->priv;\n"
79              "\t}\n\n"
80              "\treturn NULL;\n"
81              "}\n\n";
82 }
83
84 static void output_convfunc(Class *cla, Interface *iface, ostream &f)
85 {
86         f << "operator ";
87
88         cpp_output_name(f, iface);
89
90         f << "()\n"
91              "{\n"
92              "\treturn ";
93
94         cpp_output_name(f, iface);
95
96         int chain = cla->concrete_iface->super_to_chain(iface);
97
98         f << "(reinterpret_cast< ";
99         cpp_output_name(f, iface, "_i_");
100         f << " *>(&IDL_ref.p" << chain << "));\n"
101              "}\n\n";
102 }
103
104 static void output_info_type(Class *cla, ostream &f)
105 {
106         Interface *conc = cla->concrete_iface;
107         f << "struct IDL_info_type {\n";
108         
109         for (int i = 0; i < conc->get_num_chains(); i++) {
110                 Interface *iface = conc->get_chain_head(i);
111                 
112                 // Skip the "fake" aggregation interface on the first chain.
113                 if (i == 0)
114                         iface = *iface->supers_begin();
115
116                 f << '\t';
117                 cpp_output_name(f, iface, "_i_");
118                 f << "::info_type p" << i << ";\n";
119         }
120
121         f << "};\n\n"
122              "static IDL_info_type IDL_info;\n\n";
123 }
124
125 static void output_mixin(Class *cla, string &filename)
126 {
127         ofstream f;
128         
129         f.open(filename.c_str());
130         if (!f.is_open()) {
131                 fprintf(stderr, "Could not create output file \"%s\": %s.\n",
132                         filename.c_str(), strerror(errno));
133
134                 throw UserError();
135         }
136         
137         output_ifaceptrs(cla, f);
138         output_info_type(cla, f);
139
140         f << "// The classptr method(s) can be used to get a class pointer from\n"
141              "// an interface reference.  If the interface reference does\n"
142              "// not refer to an instance of the class, it returns NULL.\n\n";
143
144         for (Class::ifaces_iterator i = cla->ifaces_begin();
145              i != cla->ifaces_end(); ++i)
146         {
147                 output_classptr(cla, *i, f);
148         }
149         
150         f << "// Used by auto-generated stubs which know that the interface is of\n"
151              "// the proper class.  Do not call this from user code.\n\n"
152              "static " << **cla->name << " *_classptr_internal(void *ptr)\n"
153              "{\n"
154              "\tIDL_ifaceptr *wrap = static_cast<IDL_ifaceptr *>(ptr);\n"
155              "\treturn wrap->priv;\n"
156              "}\n\n";
157
158         f << "IDL_ifaceptr IDL_ref;\n\n";
159
160         f << "// The implicit conversion function(s) below can be used to convert\n"
161              "// a class reference to an interface reference.  Note that if you\n"
162              "// have a pointer, rather than a reference, you will need to dereference\n"
163              "// it prior to casting to the desired interface type.\n\n";
164         
165         for (Class::ifaces_iterator i = cla->ifaces_begin();
166              i != cla->ifaces_end(); ++i)
167         {
168                 output_convfunc(cla, *i, f);
169         }
170         
171         f << "// This method must be called prior to using any interface references\n"
172           << "// to this object.  If a method is called through an interface pointer\n"
173           << "// before this is done, a memory fault is likely.\n\n"
174           << "void init_iface()\n"
175           << "{\n"
176           << "\tIDL_ref.priv = this;\n"
177           << "}\n";
178 }
179
180 struct callback_data {
181         Class *cla;
182         ostream &f;
183 };
184
185 static void output_method_wrapper(Class *cla, Interface *iface,
186                                   Method *meth, Class::MethodInfo *mi,
187                                   bool copy, ostream &f)
188 {
189         const String *implname_idlns, *implname_scope;
190         
191         if (mi) {
192                 implname_idlns = mi->implname->flatten("_IDLNS_");
193                 implname_scope = mi->implname->flatten("::");
194         } else {
195                 implname_idlns = implname_scope = meth->name;
196                 assert(!copy);
197         }
198         
199         Indent indent = { 1, implname_idlns->length() + 6 };
200         
201         f << "\tvoid ";
202         
203         if (copy)
204                 f << "_copy_";
205         
206         f << *implname_idlns << "(void *_this";
207         
208         for (Method::entries_iterator i = meth->entries_begin();
209              i != meth->entries_end(); ++i)
210         {
211                 f << ",\n" << indent;
212                 cpp_output_one_param(f, *i, true, copy);
213         }
214         
215         const String *classname = cla->get_fq_name()->flatten("::");
216         
217         f << ")\n"
218              "\t{\n";
219
220         int chain = cla->concrete_iface->super_to_chain(iface);
221         if (chain) {
222                 f << "\t\t_this = reinterpret_cast<void *>"
223                      "(reinterpret_cast<uintptr_t>(_this) - " 
224                   << chain * target->pointer_size << ");\n";
225         }
226
227         f << "\t\t::" << *classname << " *_ptr = ::" << *classname
228           << "::_classptr_internal(_this);\n"
229              "\t\t_ptr->" << *implname_scope << '(';
230         
231         indent = (Indent){ 2, implname_scope->length() + 7 };
232
233         for (Method::entries_iterator i = meth->entries_begin();
234              i != meth->entries_end(); ++i)
235         {
236                 if (i != meth->entries_begin())
237                         f << ",\n" << indent;
238
239                 Param *p = *i;
240                 bool copy_this_param = false;
241                 if (copy) {
242                         Class::ParamInfo *pi = mi->get_param(p);
243                         if (pi && pi->copy)
244                                 copy_this_param = true;
245                 }
246
247                 // FIXME: copy non-arrays
248
249                 f << **p->name;
250                 
251                 if (copy_this_param && p->is_array())
252                         f << ".copy()";
253         }
254         
255         f << ");\n"
256              "\t}\n\n";
257 }
258
259 // Should be static, but GCC won't accept it as a template parameter
260 // even though the template is only used from this file (and thus
261 // won't be instantiated elsewhere (unless some template consolidation
262 // mechanism puts them all in one file or something, but then an
263 // exception could be made for templates with static parameters,
264 // as they can be placed in their original files with no chance
265 // of a duplicate)).
266
267 void cpp_output_iface_method_wrappers(Interface *super, void *arg)
268 {
269         callback_data *data = static_cast<callback_data *>(arg);
270         
271         for (Interface::methods_iterator i = super->methods_begin();
272              i != super->methods_end(); ++i)
273         {
274                 Method *m = *i;
275                 Class::MethodInfo *mi = data->cla->get_method(m);
276
277                 output_method_wrapper(data->cla, super, m, mi, false, data->f);
278                 
279                 if (mi && mi->copy_params)
280                         output_method_wrapper(data->cla, super, m, mi, true, data->f);
281         }
282 }
283
284 struct Context {
285         Class *cla;
286         ostream &f;
287         Indent indent;
288         int chain;
289         Interface *cur;
290 };
291
292 void cpp_output_iface_table_entry(Interface *super, void *arg)
293 {
294         Context &ctx(*static_cast<Context *>(arg));
295         
296         ctx.f << ctx.indent << "{\n";
297         ctx.indent.indent_level++;
298
299         ctx.f << ctx.indent;
300         cpp_output_name(ctx.f, super);
301         
302         ctx.f << "_ns::_guid.l,\n"
303               << ctx.indent 
304               << (ctx.cla->concrete_iface->super_to_chain(super) - ctx.chain) *
305                  target->pointer_size << '\n';
306
307         ctx.indent.indent_level--;
308         ctx.f << ctx.indent << "},\n";
309 }
310
311 void cpp_output_method_table_entries(Interface *super, void *arg)
312 {
313         Context &ctx(*static_cast<Context *>(arg));
314         
315         for (Interface::methods_iterator i = super->methods_begin();
316              i != super->methods_end(); ++i)
317         {
318                 Method *meth = *i;
319                 Class::MethodInfo *mi = ctx.cla->get_method(meth);
320                 const String *implname;
321         
322                 if (mi)
323                         implname = mi->implname->flatten("::");
324                 else
325                         implname = meth->name;
326
327                 ctx.f << ctx.indent << "IDL_Server_"
328                       <<  *ctx.cla->get_fq_name()->flatten("_IDLNS_")
329                       << "::" << *implname << ",\n";
330         }
331 }
332
333 static void output_info_type_init_rec(Context &ctx)
334 {
335         Interface *iface = ctx.cur;
336         
337         ctx.f << ctx.indent << "{ // " << *iface->get_fq_name()->flatten() << '\n';
338         ctx.indent.indent_level++;
339         
340         if (!iface->supers_empty()) {
341                 ctx.cur = *iface->supers_begin();
342                 output_info_type_init_rec(ctx);
343         } else {
344                 // Concrete pointer adjustment
345                 ctx.f << ctx.indent << '-' 
346                       << ctx.chain * target->pointer_size << ",\n";
347                 
348                 // Pointer to concrete downcast table
349                 ctx.f << ctx.indent << "&IDL_info.p0.iface_table.";
350                 
351                 Interface *iface = *ctx.cla->concrete_iface->supers_begin();
352                 ctx.f << *iface->get_fq_name()->flatten("_IDLNS_") << ",\n";
353         }
354
355         // Interface table for downcasts
356         ctx.f << ctx.indent << "{\n";
357         ctx.indent.indent_level++;
358         
359         cpp_output_iface_table_entry(iface, &ctx);
360         iface->for_each_super<cpp_output_iface_table_entry>(&ctx);
361         
362         ctx.f << ctx.indent << "NULL\n";
363         
364         ctx.indent.indent_level--;
365         ctx.f << ctx.indent << "},\n";
366
367         // Method table
368         
369         ctx.f << ctx.indent << "{\n";
370         ctx.indent.indent_level++;
371         
372         cpp_output_method_table_entries(iface, &ctx);
373         iface->for_each_super<cpp_output_method_table_entries>(&ctx);
374         
375         ctx.indent.indent_level--;
376         ctx.f << ctx.indent << "},\n";
377
378         ctx.indent.indent_level--;
379         ctx.f << ctx.indent << "},\n";
380 }
381
382 static void output_info_type_init(Class *cla, ostream &f)
383 {
384         Indent indent = (Indent){1, 0};
385         
386         // The struct name is enclosed in parentheses to disambiguate the
387         // leading :: from being a continuation of the type name.
388         
389         cpp_output_name(f, cla);
390         f << "::IDL_info_type (";
391         cpp_output_name(f, cla);
392         f << "::IDL_info) =\n"
393              "{\n";
394         
395         Context ctx = { cla, f };
396         ctx.indent.indent_level = 1;
397         
398         for (ctx.chain = 0; ctx.chain < cla->concrete_iface->get_num_chains();
399              ctx.chain++)
400         {
401                 ctx.cur = cla->concrete_iface->get_chain_head(ctx.chain);
402                 
403                 // Skip the "fake" aggregation interface on the first chain.
404                 if (ctx.chain == 0)
405                         ctx.cur = *ctx.cur->supers_begin();
406                 
407                 output_info_type_init_rec(ctx);
408         }
409
410         assert(ctx.indent.indent_level == 1);
411         f << "};\n\n";
412 }
413
414 static void output_footer(Class *cla, ostream &f)
415 {
416         // Create an arbitrary, unique name to stick the method wrappers in.
417         f << "\n\nnamespace IDL_Server_" << *cla->get_fq_name()->flatten("_IDLNS_")
418           << " {\n";
419
420         callback_data data = { cla, f };
421
422         cla->concrete_iface->for_each_super
423                 <cpp_output_iface_method_wrappers>(&data);
424
425         f << "};\n\n";
426         
427         output_info_type_init(cla, f);
428
429         cpp_output_name(f, cla);
430         f << "::IDL_ifaceptr::IDL_ifaceptr() :";
431         
432         for (int i = 0; i < cla->concrete_iface->get_num_chains(); i++) {
433                 if (i != 0)
434                         f << ',';
435         
436                 f << "\np" << i << "(&IDL_info.p" << i << ")";
437         }
438         
439         f << "\n{\n"
440              "}\n";
441 }
442
443 void CPPBinding::output_server(UserNameSpace *ns, const char *dir)
444 {
445         ofstream footer;
446
447         string footer_name(dir);
448         footer_name += "/footer.cc";
449
450         if (makedep) {
451                 printf("%s: %s\n", footer_name.c_str(), dir);
452         } else {        
453                 footer.open(footer_name.c_str());
454                 if (!footer.is_open()) {
455                         fprintf(stderr, "Could not create output file \"%s\": %s.\n",
456                                 footer_name.c_str(), strerror(errno));
457         
458                         throw UserError();
459                 }
460         
461                 footer << "// This is a C++ server binding generated by idlc.\n"
462                           "// Do not modify this file directly.\n"
463                           "// Include this file from exactly one file that has access to\n"
464                           "// the class and interface declarations (and preferably, with\n"
465                           "// access to to inlineable methods as well).";
466         }
467         
468         for (list<ClassRef>::iterator i = classes.begin(); i != classes.end(); ++i) {
469                 Class *cla = *i;
470         
471                 string filename(dir);
472                 filename += '/';
473                 filename += *cla->get_ns()->get_fq_name()->flatten("/");
474                 
475                 makepath(filename.c_str(), false);
476                 
477                 filename += '/';
478                 filename += **cla->name;
479                 filename += ".h";
480
481                 if (makedep) {
482                         printf("%s: %s\n", filename.c_str(), dir);
483                 } else {
484                         output_mixin(cla, filename);
485                         output_footer(cla, footer);
486                 }
487         }
488 }