On x86 compilers without fastcall, simulate it when invoking traces and un-simulate...
[wine-gecko.git] / xpcom / typelib / xpidl / xpidl_idl.c
blob23e8df0ba661f8c67f39464b603ac1adbb6a6545
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Michael Ang <mang@subcarrier.org>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
40 * Common IDL-processing code.
43 #include "xpidl.h"
44 #include <limits.h>
46 static gboolean parsed_empty_file;
49 * The bulk of the generation happens here.
51 gboolean
52 xpidl_process_node(TreeState *state)
54 gint type;
55 nodeHandler *dispatch, handler;
57 XPT_ASSERT(state->tree);
58 type = IDL_NODE_TYPE(state->tree);
60 if ((dispatch = state->dispatch) && (handler = dispatch[type]))
61 return handler(state);
62 return TRUE;
65 static int
66 msg_callback(int level, int num, int line, const char *file,
67 const char *message)
69 char *warning_message;
72 * Egregious hack to permit empty files.
73 * XXX libIDL needs an API to detect this case robustly.
75 if (0 == strcmp(message, "File empty after optimization")) {
76 parsed_empty_file = TRUE;
77 return 1;
80 if (!file)
81 file = "<unknown file>";
82 warning_message = g_strdup_printf("%s:%d: %s\n", file, line, message);
84 fputs(warning_message, stderr);
86 g_free(warning_message);
87 return 1;
91 * To keep track of the state associated with a given input file. The 'next'
92 * field lets us maintain a stack of input files.
94 typedef struct input_data {
95 char *filename; /* where did I come from? */
96 unsigned int lineno; /* last lineno processed */
97 char *buf; /* contents of file */
98 char *point; /* next char to feed to libIDL */
99 char *max; /* 1 past last char in buf */
100 struct input_data *next; /* file from which we were included */
101 } input_data;
104 * Passed to us by libIDL. Holds global information and the current stack of
105 * include files.
107 typedef struct input_callback_state {
108 struct input_data *input_stack; /* linked list of input_data */
109 GHashTable *already_included; /* to prevent redundant includes */
110 IncludePathEntry *include_path; /* search path for included files */
111 GSList *base_includes; /* to accumulate #includes from *first* file;
112 * for passing thru TreeState to
113 * xpidl_header backend. */
114 } input_callback_state;
116 static FILE *
117 fopen_from_includes(char **filename, const char *mode,
118 IncludePathEntry *include_path)
120 IncludePathEntry *current_path = include_path;
121 char *pathname;
122 FILE *inputfile;
123 if (!strcmp(*filename, "-"))
124 return stdin;
126 if ((*filename)[0] != '/') {
127 while (current_path) {
128 pathname = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s",
129 current_path->directory, *filename);
130 if (!pathname)
131 return NULL;
132 inputfile = fopen(pathname, mode);
133 if (inputfile) {
134 free(*filename);
135 *filename = xpidl_strdup(pathname);
136 g_free(pathname);
137 return inputfile;
139 g_free(pathname);
140 current_path = current_path->next;
142 } else {
143 inputfile = fopen(*filename, mode);
144 if (inputfile)
145 return inputfile;
147 return NULL;
150 static input_data *
151 new_input_data(char **filename, IncludePathEntry *include_path)
153 input_data *new_data;
154 FILE *inputfile;
155 char *buffer = NULL;
156 size_t offset = 0;
157 size_t buffer_size;
159 #if defined(XP_OS2) || defined(XP_WIN32)
161 * if filename is fully qualified (starts with driver letter), then
162 * just call fopen(); else, go with fopen_from_includes()
164 if( (*filename)[1] == ':' )
165 inputfile = fopen(*filename, "r");
166 else
167 inputfile = fopen_from_includes(filename, "r", include_path);
168 #else
169 inputfile = fopen_from_includes(filename, "r", include_path);
170 #endif
172 if (!inputfile)
173 return NULL;
175 * Rather than try to keep track of many different varieties of state
176 * around the boundaries of a circular buffer, we just read in the entire
177 * file.
179 * We iteratively grow the buffer here; an alternative would be to use
180 * stat to find the exact buffer size we need, as xpt_dump does.
182 for (buffer_size = 8191; ; buffer_size *= 2) {
183 size_t just_read;
184 buffer = realloc(buffer, buffer_size + 1); /* +1 for trailing nul */
185 just_read = fread(buffer + offset, 1, buffer_size - offset, inputfile);
186 if (ferror(inputfile))
187 return NULL;
189 if (just_read < buffer_size - offset || just_read == 0) {
190 /* Done reading. */
191 offset += just_read;
192 break;
194 offset += just_read;
197 fclose(inputfile);
199 new_data = xpidl_malloc(sizeof (struct input_data));
200 new_data->point = new_data->buf = buffer;
201 new_data->max = buffer + offset;
202 *new_data->max = '\0';
203 new_data->filename = *filename;
204 /* libIDL expects the line number to be that of the *next* line */
205 new_data->lineno = 2;
206 new_data->next = NULL;
208 if (deps)
209 fprintf(deps, " \\\n\t%s", *filename);
211 return new_data;
214 /* process pending raw section */
215 static int
216 NextIsRaw(input_data *data, char **startp, int *lenp)
218 char *end, *start;
221 * XXXmccabe still needed: an in_raw flag to handle the case where we're in
222 * a raw block, but haven't managed to copy it all to xpidl. This will
223 * happen when we have a raw block larger than
224 * IDL_input_data->fill.max_size (currently 8192.)
226 if (!(data->point[0] == '%' && data->point[1] == '{'))
227 return 0;
229 start = *startp = data->point;
231 end = NULL;
232 while (start < data->max && (end = strstr(start, "%}"))) {
233 if (end[-1] == '\r' ||
234 end[-1] == '\n')
235 break;
236 start = end + 1;
239 if (end && start < data->max) {
240 *lenp = end - data->point + 2;
241 return 1;
242 } else {
243 const char *filename;
244 int lineno;
246 IDL_file_get(&filename, &lineno);
247 msg_callback(IDL_ERROR, 0, lineno, filename,
248 "unterminated %{ block");
249 return -1;
253 /* process pending comment */
254 static int
255 NextIsComment(input_data *data, char **startp, int *lenp)
257 char *end;
259 if (!(data->point[0] == '/' && data->point[1] == '*'))
260 return 0;
262 end = strstr(data->point, "*/");
263 *lenp = 0;
264 if (end) {
265 int skippedLines = 0;
266 char *tempPoint;
268 /* get current lineno */
269 IDL_file_get(NULL,(int *)&data->lineno);
271 /* get line count */
272 for (tempPoint = data->point; tempPoint < end; tempPoint++) {
273 if (*tempPoint == '\n')
274 skippedLines++;
277 data->lineno += skippedLines;
278 IDL_file_set(data->filename, (int)data->lineno);
280 *startp = end + 2;
282 /* If it's a ** comment, tell libIDL about it. */
283 if (data->point[2] == '*') {
284 /* hack termination. +2 to get past '*' '/' */
285 char t = *(end + 2);
286 *(end + 2) = '\0';
287 IDL_queue_new_ident_comment(data->point);
288 *(end + 2) = t;
291 data->point = *startp; /* XXXmccabe move this out of function? */
292 return 1;
293 } else {
294 const char *filename;
295 int lineno;
297 IDL_file_get(&filename, &lineno);
298 msg_callback(IDL_ERROR, 0, lineno, filename,
299 "unterminated comment");
300 return -1;
304 static int
305 NextIsInclude(input_callback_state *callback_state, char **startp,
306 int *lenp)
308 input_data *data = callback_state->input_stack;
309 input_data *new_data;
310 char *filename, *end;
311 const char *scratch;
313 /* process the #include that we're in now */
314 if (strncmp(data->point, "#include \"", 10)) {
315 return 0;
318 filename = data->point + 10; /* skip #include " */
319 XPT_ASSERT(filename < data->max);
320 end = filename;
321 while (end < data->max) {
322 if (*end == '\"' || *end == '\n' || *end == '\r')
323 break;
324 end++;
327 if (*end != '\"') {
329 * Didn't find end of include file. Scan 'til next whitespace to find
330 * some reasonable approximation of the filename, and use it to report
331 * an error.
334 end = filename;
335 while (end < data->max) {
336 if (*end == ' ' || *end == '\n' || *end == '\r' || *end == '\t')
337 break;
338 end++;
340 *end = '\0';
342 /* make sure we have accurate line info */
343 IDL_file_get(&scratch, (int *)&data->lineno);
344 fprintf(stderr,
345 "%s:%d: didn't find end of quoted include name \"%s\n",
346 scratch, data->lineno, filename);
347 return -1;
350 *end = '\0';
351 *startp = end + 1;
353 if (data->next == NULL) {
355 * If we're in the initial file, add this filename to the list
356 * of filenames to be turned into #include "filename.h"
357 * directives in xpidl_header.c. We do it here rather than in the
358 * block below so it still gets added to the list even if it's
359 * already been recursively included from some other file.
361 char *filename_cp = xpidl_strdup(filename);
363 /* note that g_slist_append accepts and likes null as list-start. */
364 callback_state->base_includes =
365 g_slist_append(callback_state->base_includes, filename_cp);
368 /* store offset for when we pop, or if we skip this one */
369 data->point = *startp;
371 if (!g_hash_table_lookup(callback_state->already_included, filename)) {
372 filename = xpidl_strdup(filename);
373 g_hash_table_insert(callback_state->already_included,
374 filename, (void *)TRUE);
375 filename = xpidl_strdup(filename);
376 new_data = new_input_data(&filename, callback_state->include_path);
377 if (!new_data) {
378 char *error_message;
379 IDL_file_get(&scratch, (int *)&data->lineno);
380 error_message =
381 g_strdup_printf("can't open included file %s for reading\n",
382 filename);
383 msg_callback(IDL_ERROR, 0,
384 data->lineno, scratch, error_message);
385 g_free(error_message);
386 free(filename);
387 return -1;
390 new_data->next = data;
391 /* tell libIDL to exclude this IDL from the toplevel tree */
392 IDL_inhibit_push();
393 IDL_file_get(&scratch, (int *)&data->lineno);
394 callback_state->input_stack = new_data;
395 IDL_file_set(new_data->filename, (int)new_data->lineno);
398 *lenp = 0; /* this is magic, see the comment below */
399 return 1;
402 static void
403 FindSpecial(input_data *data, char **startp, int *lenp)
405 char *point = data->point;
407 /* magic sequences are:
408 * "%{" raw block
409 * "/\*" comment
410 * "#include \"" include
411 * The first and last want a newline [\r\n] before, or the start of the
412 * file.
415 #define LINE_START(data, point) (point == data->buf || \
416 (point > data->point && \
417 (point[-1] == '\r' || point[-1] == '\n')))
419 while (point < data->max) {
420 if (point[0] == '/' && point[1] == '*')
421 break;
422 if (LINE_START(data, point)) {
423 if (point[0] == '%' && point[1] == '{')
424 break;
425 if (point[0] == '#' && !strncmp(point + 1, "include \"", 9))
426 break;
428 point++;
431 #undef LINE_START
433 *startp = data->point;
434 *lenp = point - data->point;
437 /* set this with a debugger to see exactly what libIDL sees */
438 static FILE *tracefile;
440 static int
441 input_callback(IDL_input_reason reason, union IDL_input_data *cb_data,
442 gpointer user_data)
444 input_callback_state *callback_state = user_data;
445 input_data *data = callback_state->input_stack;
446 input_data *new_data = NULL;
447 unsigned int len, copy;
448 int rv;
449 char *start, *filename;
451 switch(reason) {
452 case IDL_INPUT_REASON_INIT:
453 filename = xpidl_strdup(cb_data->init.filename);
454 if (data == NULL || data->next == NULL) {
456 * This is the first file being processed. As it's the target
457 * file, we only look for it in the first entry in the include
458 * path, which we assume to be the current directory.
461 /* XXXmccabe proper assumption? Do we handle files in other
462 directories? */
464 IncludePathEntry first_entry;
466 first_entry.directory = callback_state->include_path->directory;
467 first_entry.next = NULL;
469 new_data = new_input_data(&filename, &first_entry);
470 } else {
471 new_data = new_input_data(&filename, callback_state->include_path);
474 if (!new_data) {
475 free(filename);
476 return -1;
479 IDL_file_set(new_data->filename, (int)new_data->lineno);
480 callback_state->input_stack = new_data;
481 return 0;
483 case IDL_INPUT_REASON_FILL:
484 start = NULL;
485 len = 0;
487 while (data->point >= data->max) {
488 if (!data->next)
489 return 0;
491 /* Current file is done; revert to including file */
492 callback_state->input_stack = data->next;
493 free(data->filename);
494 free(data->buf);
495 free(data);
496 data = callback_state->input_stack;
498 IDL_file_set(data->filename, (int)data->lineno);
499 IDL_inhibit_pop();
503 * Now we scan for sequences which require special attention:
504 * \n#include begins an include statement
505 * \n%{ begins a raw-source block
506 * /\* begins a comment
508 * We used to be fancier here, so make sure that we sent the most
509 * data possible at any given time. To that end, we skipped over
510 * \n%{ raw \n%} blocks and then _continued_ the search for special
511 * sequences like \n#include or /\* comments .
513 * It was really ugly, though -- liberal use of goto! lots of implicit
514 * state! what fun! -- so now we just do this:
516 * if (special at start) {
517 * process that special -
518 * - raw: send it to libIDL, and don't look inside for specials
519 * - comments: adjust point and start over
520 * - includes: push new input_data struct for included file, and
521 * start over
522 * } else {
523 * scan for next special
524 * send data up to that special to libIDL
527 * If len is set to zero, it is a sentinel value indicating we a comment
528 * or include was found, and parsing should start over.
530 * XXX const string foo = "/\*" will just screw us horribly.
531 * Hm but. We could treat strings as we treat raw blocks, eh?
535 * Order is important, so that you can have /\* comments and
536 * #includes within raw sections, and so that you can comment out
537 * #includes.
539 rv = NextIsRaw(data, &start, (int *)&len);
540 if (rv == -1) return -1;
541 if (!rv) {
543 * When NextIsComment succeeds, it returns a 0 len (requesting a
544 * restart) and adjusts data->point to pick up after the comment.
546 rv = NextIsComment(data, &start, (int *)&len);
547 if (rv == -1) return -1;
548 if (!rv) {
550 * NextIsInclude might push a new input_data struct; if so, it
551 * will return a 0 len, letting the callback pick up the new
552 * file the next time around.
554 rv = NextIsInclude(callback_state, &start, (int *)&len);
555 if (rv == -1) return -1;
556 if (!rv)
557 FindSpecial(data, &start, (int *)&len);
561 if (len == 0) {
563 * len == 0 is a sentinel value that means we found a comment or
564 * include. If we found a comment, point has been adjusted to
565 * point past the comment. If we found an include, a new input_data
566 * has been pushed. In both cases, calling the input_callback again
567 * will pick up the new state.
569 return input_callback(reason, cb_data, user_data);
572 copy = MIN(len, (unsigned int) cb_data->fill.max_size);
573 memcpy(cb_data->fill.buffer, start, copy);
574 data->point = start + copy;
576 if (tracefile)
577 fwrite(cb_data->fill.buffer, copy, 1, tracefile);
579 return copy;
581 case IDL_INPUT_REASON_ABORT:
582 case IDL_INPUT_REASON_FINISH:
583 while (data != NULL) {
584 input_data *next;
586 next = data->next;
587 free(data->filename);
588 free(data->buf);
589 free(data);
590 data = next;
592 return 0;
594 default:
595 g_error("unknown input reason %d!", reason);
596 return -1;
600 static void
601 free_ghash_key(gpointer key, gpointer value, gpointer user_data)
603 /* We're only storing TRUE in the value... */
604 free(key);
607 static void
608 free_gslist_data(gpointer data, gpointer user_data)
610 free(data);
613 /* Pick up unlink. */
614 #ifdef XP_UNIX
615 #include <unistd.h>
616 #elif XP_WIN
617 /* We get it from stdio.h. */
618 #define unlink _unlink
619 #endif
622 xpidl_process_idl(char *filename, IncludePathEntry *include_path,
623 char *file_basename, ModeData *mode)
625 char *tmp, *outname, *real_outname = NULL;
626 IDL_tree top;
627 TreeState state;
628 int rv;
629 input_callback_state callback_state;
630 gboolean ok = TRUE;
631 backend *emitter;
633 callback_state.input_stack = NULL;
634 callback_state.base_includes = NULL;
635 callback_state.include_path = include_path;
636 callback_state.already_included = g_hash_table_new(g_str_hash, g_str_equal);
638 if (!callback_state.already_included) {
639 fprintf(stderr, "failed to create hashtable. out of memory?\n");
640 return 0;
643 state.basename = xpidl_strdup(filename);
645 /* if basename has an .extension, truncate it. */
646 tmp = strrchr(state.basename, '.');
647 if (tmp)
648 *tmp = '\0';
650 if (!file_basename)
651 outname = xpidl_strdup(state.basename);
652 else
653 outname = xpidl_strdup(file_basename);
655 /* so we don't include it again! */
656 g_hash_table_insert(callback_state.already_included,
657 xpidl_strdup(filename), (void *)TRUE);
659 parsed_empty_file = FALSE;
661 rv = IDL_parse_filename_with_input(filename, input_callback, &callback_state,
662 msg_callback, &top,
663 &state.ns,
664 IDLF_IGNORE_FORWARDS |
665 IDLF_XPIDL,
666 enable_warnings ? IDL_WARNING1 :
667 IDL_ERROR);
668 if (parsed_empty_file) {
670 * If we've detected (via hack in msg_callback) that libIDL returned
671 * failure because it found a file with no IDL, set the parse tree to
672 * null and proceed. Allowing this is useful to permit .idl files that
673 * collect #includes.
675 top = NULL;
676 state.ns = NULL;
677 } else if (rv != IDL_SUCCESS) {
678 if (rv == -1) {
679 g_warning("Parse of %s failed: %s", filename, g_strerror(errno));
680 } else {
681 g_warning("Parse of %s failed", filename);
683 return 0;
686 state.basename = xpidl_strdup(filename);
687 tmp = strrchr(state.basename, '.');
688 if (tmp)
689 *tmp = '\0';
691 /* so xpidl_header.c can use it to generate a list of #include directives */
692 state.base_includes = callback_state.base_includes;
694 emitter = mode->factory();
695 state.dispatch = emitter->dispatch_table;
697 if (strcmp(outname, "-")) {
698 const char *fopen_mode;
699 const char *out_basename;
701 /* explicit_output_filename can't be true without a filename */
702 if (explicit_output_filename) {
703 real_outname = g_strdup(outname);
704 } else {
706 *This combination seems a little strange, what about OS/2?
707 * Assume it's some build issue
709 #if defined(XP_UNIX) || defined(XP_WIN)
710 if (!file_basename) {
711 out_basename = xpidl_basename(outname);
712 } else {
713 out_basename = outname;
715 #else
716 out_basename = outname;
717 #endif
718 real_outname = g_strdup_printf("%s.%s", out_basename, mode->suffix);
721 /* don't create/open file here for Java */
722 if (strcmp(mode->mode, "java") == 0) {
723 state.filename = real_outname;
724 } else {
725 /* Use binary write for typelib mode */
726 fopen_mode = (strcmp(mode->mode, "typelib")) ? "w" : "wb";
727 state.file = fopen(real_outname, fopen_mode);
728 if (!state.file) {
729 perror("error opening output file");
730 return 0;
733 } else {
734 state.file = stdout;
736 state.tree = top;
738 if (emitter->emit_prolog)
739 emitter->emit_prolog(&state);
740 if (state.tree) /* Only if we have a tree to process. */
741 ok = xpidl_process_node(&state);
742 if (emitter->emit_epilog)
743 emitter->emit_epilog(&state);
745 if (strcmp(mode->mode, "java") != 0) {
746 if (state.file != stdout)
747 fclose(state.file);
750 free(state.basename);
751 free(outname);
752 g_hash_table_foreach(callback_state.already_included, free_ghash_key, NULL);
753 g_hash_table_destroy(callback_state.already_included);
754 g_slist_foreach(callback_state.base_includes, free_gslist_data, NULL);
756 if (state.ns)
757 IDL_ns_free(state.ns);
758 if (top)
759 IDL_tree_free(top);
761 if (strcmp(mode->mode, "java") != 0) {
762 if (real_outname != NULL) {
764 * Delete partial output file on failure. (Mac does this in the
765 * plugin driver code, if the compiler returns failure.)
767 #if defined(XP_UNIX) || defined(XP_WIN)
768 if (!ok)
769 unlink(real_outname);
770 #endif
771 g_free(real_outname);
775 return ok;
779 * Our own version of IDL_tree_warning, which we use when IDL_tree_warning
780 * would crash on us.
782 void
783 xpidl_tree_warning(IDL_tree p, int level, const char *fmt, ...)
785 va_list ap;
786 char *msg, *file;
787 int lineno;
789 /* XXX need to check against __IDL_max_msg_level, no accessor */
790 va_start(ap, fmt);
791 msg = g_strdup_vprintf(fmt, ap);
793 if (p) {
794 file = p->_file;
795 lineno = p->_line;
796 } else {
797 file = NULL;
798 lineno = 0;
801 /* call our message callback, like IDL_tree_warning would */
802 msg_callback(level, 0, lineno, file, msg);
803 g_free(msg);
804 va_end(ap);