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