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