widl: Add support for marshalling and unmarshalling conformant strings.
[wine] / tools / widl / typegen.c
1 /*
2  * Format String Generator for IDL Compiler
3  *
4  * Copyright 2005 Eric Kohl
5  * Copyright 2005 Robert Shearman
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #ifdef HAVE_UNISTD_H
28 # include <unistd.h>
29 #endif
30 #include <string.h>
31 #include <assert.h>
32 #include <ctype.h>
33 #include <signal.h>
34
35 #include "widl.h"
36 #include "utils.h"
37 #include "parser.h"
38 #include "header.h"
39 #include "windef.h"
40
41 #include "widl.h"
42 #include "typegen.h"
43
44 static int print_file(FILE *file, int indent, const char *format, ...)
45 {
46     va_list va;
47     int i, r;
48
49     if (!file) return 0;
50
51     va_start(va, format);
52     for (i = 0; i < indent; i++)
53         fprintf(file, "    ");
54     r = vfprintf(file, format, va);
55     va_end(va);
56     return r;
57 }
58
59 static size_t write_procformatstring_var(FILE *file, int indent, var_t *var, int is_return, unsigned int *type_offset)
60 {
61     size_t size;
62     if (var->ptr_level == 0 && !var->array)
63     {
64         if (is_return)
65             print_file(file, indent, "0x53,    /* FC_RETURN_PARAM_BASETYPE */\n");
66         else
67             print_file(file, indent, "0x4e,    /* FC_IN_PARAM_BASETYPE */\n");
68
69         switch(var->type->type)
70         {
71 #define CASE_BASETYPE(fctype) \
72         case RPC_##fctype: \
73             print_file(file, indent, "0x%02x,    /* " #fctype " */\n", var->type->type); \
74             size = 2; /* includes param type prefix */ \
75             break
76
77         CASE_BASETYPE(FC_BYTE);
78         CASE_BASETYPE(FC_CHAR);
79         CASE_BASETYPE(FC_WCHAR);
80         CASE_BASETYPE(FC_USHORT);
81         CASE_BASETYPE(FC_SHORT);
82         CASE_BASETYPE(FC_ULONG);
83         CASE_BASETYPE(FC_LONG);
84         CASE_BASETYPE(FC_HYPER);
85         CASE_BASETYPE(FC_IGNORE);
86         CASE_BASETYPE(FC_USMALL);
87         CASE_BASETYPE(FC_SMALL);
88         CASE_BASETYPE(FC_FLOAT);
89         CASE_BASETYPE(FC_DOUBLE);
90         CASE_BASETYPE(FC_ERROR_STATUS_T);
91 #undef CASE_BASETYPE
92         default:
93             error("Unknown/unsupported type: %s (0x%02x)\n", var->name, var->type->type);
94             size = 0;
95         }
96     }
97     else
98     {
99         int in_attr = is_attr(var->attrs, ATTR_IN);
100         int out_attr = is_attr(var->attrs, ATTR_OUT);
101
102         if (is_return)
103             print_file(file, indent, "0x52,    /* FC_RETURN_PARAM */\n");
104         else if (in_attr && out_attr)
105             print_file(file, indent, "0x50,    /* FC_IN_OUT_PARAM */\n");
106         else if (out_attr)
107             print_file(file, indent, "0x51,    /* FC_OUT_PARAM */\n");
108         else
109             print_file(file, indent, "0x4d,    /* FC_IN_PARAM */\n");
110
111         print_file(file, indent, "0x01,\n");
112         print_file(file, indent, "NdrFcShort(0x%x),\n", *type_offset);
113         size = 4; /* includes param type prefix */
114     }
115     *type_offset += get_size_typeformatstring_var(var);
116     return size;
117 }
118
119 void write_procformatstring(FILE *file, type_t *iface)
120 {
121     int indent = 0;
122     var_t *var;
123     unsigned int type_offset = 2;
124
125     print_file(file, indent, "static const MIDL_PROC_FORMAT_STRING __MIDL_ProcFormatString =\n");
126     print_file(file, indent, "{\n");
127     indent++;
128     print_file(file, indent, "0,\n");
129     print_file(file, indent, "{\n");
130     indent++;
131
132     if (iface->funcs)
133     {
134         func_t *func = iface->funcs;
135         while (NEXT_LINK(func)) func = NEXT_LINK(func);
136         for (; func; func = PREV_LINK(func))
137         {
138             /* emit argument data */
139             if (func->args)
140             {
141                 var = func->args;
142                 while (NEXT_LINK(var)) var = NEXT_LINK(var);
143                 while (var)
144                 {
145                     write_procformatstring_var(file, indent, var, FALSE, &type_offset);
146                     var = PREV_LINK(var);
147                 }
148             }
149
150             /* emit return value data */
151             var = func->def;
152             if (is_void(var->type, NULL))
153             {
154                 print_file(file, indent, "0x5b,    /* FC_END */\n");
155                 print_file(file, indent, "0x5c,    /* FC_PAD */\n");
156             }
157             else
158                 write_procformatstring_var(file, indent, var, TRUE, &type_offset);
159         }
160     }
161
162     print_file(file, indent, "0x0\n");
163     indent--;
164     print_file(file, indent, "}\n");
165     indent--;
166     print_file(file, indent, "};\n");
167     print_file(file, indent, "\n");
168 }
169
170
171 static size_t write_typeformatstring_var(FILE *file, int indent, var_t *var)
172 {
173     int ptr_level = var->ptr_level;
174
175     /* basic types don't need a type format string */
176     if (ptr_level == 0 && !var->array)
177         return 0;
178
179     if (ptr_level == 1 ||
180         (var->ptr_level == 0 && var->array && !NEXT_LINK(var->array)))
181     {
182         switch (var->type->type)
183         {
184 #define CASE_BASETYPE(fctype) \
185         case RPC_##fctype: \
186             print_file(file, indent, "0x11, 0x08,    /* FC_RP [simple_pointer] */\n"); \
187             print_file(file, indent, "0x%02x,    /* " #fctype " */\n", var->type->type); \
188             print_file(file, indent, "0x5c,          /* FC_PAD */\n"); \
189             return 4
190         CASE_BASETYPE(FC_BYTE);
191         CASE_BASETYPE(FC_CHAR);
192         CASE_BASETYPE(FC_SMALL);
193         CASE_BASETYPE(FC_USMALL);
194         CASE_BASETYPE(FC_WCHAR);
195         CASE_BASETYPE(FC_SHORT);
196         CASE_BASETYPE(FC_USHORT);
197         CASE_BASETYPE(FC_LONG);
198         CASE_BASETYPE(FC_ULONG);
199         CASE_BASETYPE(FC_FLOAT);
200         CASE_BASETYPE(FC_HYPER);
201         CASE_BASETYPE(FC_DOUBLE);
202         CASE_BASETYPE(FC_ENUM16);
203         CASE_BASETYPE(FC_ENUM32);
204         CASE_BASETYPE(FC_IGNORE);
205         CASE_BASETYPE(FC_ERROR_STATUS_T);
206         default:
207             error("write_typeformatstring_var: Unknown/unsupported type: %s (0x%02x)\n", var->name, var->type->type);
208         }
209     }
210     error("write_typeformatstring_var: Pointer level %d not supported for variable %s\n", ptr_level, var->name);
211     return 0;
212 }
213
214
215 void write_typeformatstring(FILE *file, type_t *iface)
216 {
217     int indent = 0;
218     var_t *var;
219
220     print_file(file, indent, "static const MIDL_TYPE_FORMAT_STRING __MIDL_TypeFormatString =\n");
221     print_file(file, indent, "{\n");
222     indent++;
223     print_file(file, indent, "0,\n");
224     print_file(file, indent, "{\n");
225     indent++;
226     print_file(file, indent, "NdrFcShort(0x0),\n");
227
228     if (iface->funcs)
229     {
230         func_t *func = iface->funcs;
231         while (NEXT_LINK(func)) func = NEXT_LINK(func);
232         for (; func; func = PREV_LINK(func))
233         {
234             if (func->args)
235             {
236                 var = func->args;
237                 while (NEXT_LINK(var)) var = NEXT_LINK(var);
238                 while (var)
239                 {
240                     write_typeformatstring_var(file, indent, var);
241                     var = PREV_LINK(var);
242                 }
243             }
244         }
245     }
246
247     print_file(file, indent, "0x0\n");
248     indent--;
249     print_file(file, indent, "}\n");
250     indent--;
251     print_file(file, indent, "};\n");
252     print_file(file, indent, "\n");
253 }
254
255
256 unsigned int get_required_buffer_size(type_t *type)
257 {
258     switch(type->type)
259     {
260         case RPC_FC_BYTE:
261         case RPC_FC_CHAR:
262         case RPC_FC_WCHAR:
263         case RPC_FC_USHORT:
264         case RPC_FC_SHORT:
265         case RPC_FC_USMALL:
266         case RPC_FC_SMALL:
267         case RPC_FC_ULONG:
268         case RPC_FC_LONG:
269         case RPC_FC_FLOAT:
270         case RPC_FC_IGNORE:
271         case RPC_FC_ERROR_STATUS_T:
272             return 4;
273
274         case RPC_FC_HYPER:
275         case RPC_FC_DOUBLE:
276             return 8;
277
278         default:
279             error("Unknown/unsupported type: %s (0x%02x)\n", type->name, type->type);
280             return 0;
281     }
282 }
283
284 void marshall_arguments(FILE *file, int indent, func_t *func, unsigned int *type_offset)
285 {
286     unsigned int last_size = 0;
287     var_t *var;
288
289     if (!func->args)
290         return;
291
292     var = func->args;
293     while (NEXT_LINK(var)) var = NEXT_LINK(var);
294     while (var)
295     {
296         if (var->ptr_level == 0)
297         {
298             unsigned int size;
299             unsigned int alignment = 0;
300             switch (var->type->type)
301             {
302             case RPC_FC_BYTE:
303             case RPC_FC_CHAR:
304             case RPC_FC_SMALL:
305             case RPC_FC_USMALL:
306                 size = 1;
307                 alignment = 0;
308                 break;
309
310             case RPC_FC_WCHAR:
311             case RPC_FC_USHORT:
312             case RPC_FC_SHORT:
313                 size = 2;
314                 if (last_size != 0 && last_size < 2)
315                     alignment = (2 - last_size);
316                 break;
317
318             case RPC_FC_ULONG:
319             case RPC_FC_LONG:
320             case RPC_FC_FLOAT:
321             case RPC_FC_ERROR_STATUS_T:
322                 size = 4;
323                 if (last_size != 0 && last_size < 4)
324                     alignment = (4 - last_size);
325                 break;
326
327             case RPC_FC_HYPER:
328             case RPC_FC_DOUBLE:
329                 size = 8;
330                 if (last_size != 0 && last_size < 4)
331                     alignment = (4 - last_size);
332                 break;
333
334             default:
335                 error("marshall_arguments: Unsupported type: %s (0x%02x, ptr_level: 0)\n", var->name, var->type->type);
336                 size = 0;
337             }
338
339             if (alignment != 0)
340                 print_file(file, indent, "_StubMsg.Buffer += %u;\n", alignment);
341
342             print_file(file, indent, "*(");
343             write_type(file, var->type, var, var->tname);
344             fprintf(file, " *)_StubMsg.Buffer = ");
345             write_name(file, var);
346             fprintf(file, ";\n");
347             fprintf(file, "_StubMsg.Buffer += sizeof(");
348             write_type(file, var->type, var, var->tname);
349             fprintf(file, ");\n");
350             fprintf(file, "\n");
351
352             last_size = size;
353         }
354         else if (var->ptr_level == 1)
355         {
356             if (is_attr(var->attrs, ATTR_STRING))
357             {
358                 switch (var->type->type)
359                 {
360                 case RPC_FC_CHAR:
361                 case RPC_FC_WCHAR:
362                     print_file(file, indent,
363                         "NdrConformantStringMarshall(&_StubMsg, (unsigned char *)%s, &__MIDL_TypeFormatString.Format[%d]);\n",
364                         var->name, *type_offset);
365                     break;
366                 default:
367                     error("marshall_arguments: Unsupported [string] type: %s (0x%02x, ptr_level: 1)\n", var->name, var->type->type);
368                 }
369             }
370             else
371             {
372                 switch (var->type->type)
373                 {
374                 default:
375                     error("marshall_arguments: Unsupported type: %s (0x%02x, ptr_level: 1)\n", var->name, var->type->type);
376                 }
377             }
378             last_size = 1;
379         }
380         else
381         {
382             error("marshall_arguments: Pointer level %d not supported for variable %s\n", var->ptr_level, var->name);
383             last_size = 1;
384         }
385
386         var = PREV_LINK(var);
387     }
388 }
389
390 void unmarshall_arguments(FILE *file, int indent, func_t *func, unsigned int *type_offset)
391 {
392     unsigned int last_size = 0;
393     var_t *var;
394
395     if (!func->args)
396         return;
397
398     var = func->args;
399     while (NEXT_LINK(var)) var = NEXT_LINK(var);
400     while (var)
401     {
402         if (var->ptr_level == 0)
403         {
404             unsigned int size;
405             unsigned int alignment = 0;
406
407             switch (var->type->type)
408             {
409             case RPC_FC_BYTE:
410             case RPC_FC_CHAR:
411             case RPC_FC_SMALL:
412             case RPC_FC_USMALL:
413                 size = 1;
414                 alignment = 0;
415                 break;
416
417             case RPC_FC_WCHAR:
418             case RPC_FC_USHORT:
419             case RPC_FC_SHORT:
420                 size = 2;
421                 if (last_size != 0 && last_size < 2)
422                     alignment = (2 - last_size);
423                 break;
424
425             case RPC_FC_ULONG:
426             case RPC_FC_LONG:
427             case RPC_FC_FLOAT:
428             case RPC_FC_ERROR_STATUS_T:
429                 size = 4;
430                 if (last_size != 0 && last_size < 4)
431                     alignment = (4 - last_size);
432                 break;
433
434             case RPC_FC_HYPER:
435             case RPC_FC_DOUBLE:
436                 size = 8;
437                 if (last_size != 0 && last_size < 4)
438                     alignment = (4 - last_size);
439                 break;
440
441             default:
442                 error("unmarshall_arguments: Unsupported type: %s (0x%02x, ptr_level: 0)\n", var->name, var->type->type);
443                 size = 0;
444             }
445
446             if (alignment != 0)
447                 print_file(file, indent, "_StubMsg.Buffer += %u;\n", alignment);
448
449             print_file(file, indent, "");
450             write_name(file, var);
451             fprintf(file, " = *(");
452             write_type(file, var->type, var, var->tname);
453             fprintf(file, " *)_StubMsg.Buffer;\n");
454             fprintf(file, "_StubMsg.Buffer += sizeof(");
455             write_type(file, var->type, var, var->tname);
456             fprintf(file, ");\n");
457             fprintf(file, "\n");
458
459             last_size = size;
460         }
461         else if (var->ptr_level == 1)
462         {
463             if (is_attr(var->attrs, ATTR_STRING))
464             {
465                 switch (var->type->type)
466                 {
467                 case RPC_FC_CHAR:
468                 case RPC_FC_WCHAR:
469                     print_file(file, indent,
470                         "NdrConformantStringUnmarshall(&_StubMsg, (unsigned char *)%s, &__MIDL_TypeFormatString.Format[%d], 0);\n",
471                         var->name, *type_offset);
472                     break;
473                 default:
474                     error("unmarshall_arguments: Unsupported [string] type: %s (0x%02x, ptr_level: 1)\n", var->name, var->type->type);
475                 }
476             }
477             else
478             {
479                 switch (var->type->type)
480                 {
481                 default:
482                     error("unmarshall_arguments: Unsupported type: %s (0x%02x, ptr_level: 1)\n", var->name, var->type->type);
483                 }
484             }
485             last_size = 1;
486         }
487         else
488         {
489             error("unmarshall_arguments: Pointer level %d not supported for variable %s\n", var->ptr_level, var->name);
490             last_size = 1;
491         }
492
493         var = PREV_LINK(var);
494     }
495 }
496
497
498 size_t get_size_procformatstring_var(var_t *var)
499 {
500     unsigned int type_offset = 2;
501     return write_procformatstring_var(NULL, 0, var, FALSE, &type_offset);
502 }
503
504
505 size_t get_size_typeformatstring_var(var_t *var)
506 {
507     return write_typeformatstring_var(NULL, 0, var);
508 }