Back to index

lightning-sunbird  0.9+nobinonly
xpidl_header.c
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 /*
00039  * Generate XPCOM headers from XPIDL.
00040  */
00041 
00042 #include "xpidl.h"
00043 #include <ctype.h>
00044 
00045 #define AS_DECL 0
00046 #define AS_CALL 1
00047 #define AS_IMPL 2
00048 
00049 static gboolean write_method_signature(IDL_tree method_tree, FILE *outfile,
00050                                        int mode, const char *className);
00051 static gboolean write_attr_accessor(IDL_tree attr_tree, FILE * outfile,
00052                                     gboolean getter, 
00053                                     int mode, const char *className);
00054 
00055 static void
00056 write_indent(FILE *outfile) {
00057     fputs("  ", outfile);
00058 }
00059 
00060 static gboolean
00061 header_prolog(TreeState *state)
00062 {
00063     const char *define = xpidl_basename(state->basename);
00064     fprintf(state->file, "/*\n * DO NOT EDIT.  THIS FILE IS GENERATED FROM"
00065             " %s.idl\n */\n", state->basename);
00066     fprintf(state->file,
00067             "\n#ifndef __gen_%s_h__\n"
00068             "#define __gen_%s_h__\n",
00069             define, define);
00070     if (state->base_includes != NULL) {
00071         guint len = g_slist_length(state->base_includes);
00072         guint i;
00073 
00074         fputc('\n', state->file);
00075         for (i = 0; i < len; i++) {
00076             char *ident, *dot;
00077             
00078             ident = (char *)g_slist_nth_data(state->base_includes, i);
00079             
00080             /* suppress any trailing .extension */
00081             
00082             /* XXX use g_basename instead ? ? */
00083             
00084             dot = strrchr(ident, '.');
00085             if (dot != NULL)
00086                 *dot = '\0';
00087             
00088 
00089             /* begin include guard */            
00090             fprintf(state->file,
00091                     "\n#ifndef __gen_%s_h__\n",
00092                      ident);
00093 
00094             fprintf(state->file, "#include \"%s.h\"\n",
00095                     (char *)g_slist_nth_data(state->base_includes, i));
00096 
00097             fprintf(state->file, "#endif\n");
00098             
00099         }
00100         if (i > 0)
00101             fputc('\n', state->file);
00102     }
00103     /*
00104      * Support IDL files that don't include a root IDL file that defines
00105      * NS_NO_VTABLE.
00106      */
00107     fprintf(state->file,
00108             "/* For IDL files that don't want to include root IDL files. */\n"
00109             "#ifndef NS_NO_VTABLE\n"
00110             "#define NS_NO_VTABLE\n"
00111             "#endif\n");
00112     
00113     return TRUE;
00114 }
00115 
00116 static gboolean
00117 header_epilog(TreeState *state)
00118 {
00119     const char *define = xpidl_basename(state->basename);
00120     fprintf(state->file, "\n#endif /* __gen_%s_h__ */\n", define);
00121     return TRUE;
00122 }
00123 
00124 static void
00125 write_classname_iid_define(FILE *file, const char *className)
00126 {
00127     const char *iidName;
00128     if (className[0] == 'n' && className[1] == 's') {
00129         /* backcompat naming styles */
00130         fputs("NS_", file);
00131         iidName = className + 2;
00132     } else {
00133         iidName = className;
00134     }
00135     while (*iidName)
00136         fputc(toupper(*iidName++), file);
00137     fputs("_IID", file);
00138 }
00139 
00140 static gboolean
00141 interface(TreeState *state)
00142 {
00143     IDL_tree iface = state->tree, iter, orig;
00144     char *className = IDL_IDENT(IDL_INTERFACE(iface).ident).str;
00145     char *classNameUpper = NULL;
00146     char *classNameImpl = NULL;
00147     char *cp;
00148     gboolean ok = TRUE;
00149     gboolean keepvtable;
00150     const char *iid;
00151     const char *name_space;
00152     struct nsID id;
00153     char iid_parsed[UUID_LENGTH];
00154     GSList *doc_comments = IDL_IDENT(IDL_INTERFACE(iface).ident).comments;
00155 
00156     if (!verify_interface_declaration(iface))
00157         return FALSE;
00158 
00159 #define FAIL    do {ok = FALSE; goto out;} while(0)
00160 
00161     fprintf(state->file,   "\n/* starting interface:    %s */\n",
00162             className);
00163 
00164     name_space = IDL_tree_property_get(IDL_INTERFACE(iface).ident, "namespace");
00165     if (name_space) {
00166         fprintf(state->file, "/* namespace:             %s */\n",
00167                 name_space);
00168         fprintf(state->file, "/* fully qualified name:  %s.%s */\n",
00169                 name_space,className);
00170     }
00171 
00172     iid = IDL_tree_property_get(IDL_INTERFACE(iface).ident, "uuid");
00173     if (iid) {
00174         /* Redundant, but a better error than 'cannot parse.' */
00175         if (strlen(iid) != 36) {
00176             IDL_tree_error(state->tree, "IID %s is the wrong length\n", iid);
00177             FAIL;
00178         }
00179 
00180         /*
00181          * Parse uuid and then output resulting nsID to string, to validate
00182          * uuid and normalize resulting .h files.
00183          */
00184         if (!xpidl_parse_iid(&id, iid)) {
00185             IDL_tree_error(state->tree, "cannot parse IID %s\n", iid);
00186             FAIL;
00187         }
00188         if (!xpidl_sprint_iid(&id, iid_parsed)) {
00189             IDL_tree_error(state->tree, "error formatting IID %s\n", iid);
00190             FAIL;
00191         }
00192 
00193         /* #define NS_ISUPPORTS_IID_STR "00000000-0000-0000-c000-000000000046" */
00194         fputs("#define ", state->file);
00195         write_classname_iid_define(state->file, className);
00196         fprintf(state->file, "_STR \"%s\"\n", iid_parsed);
00197         fputc('\n', state->file);
00198 
00199         /* #define NS_ISUPPORTS_IID { {0x00000000 .... 0x46 }} */
00200         fprintf(state->file, "#define ");
00201         write_classname_iid_define(state->file, className);
00202         fprintf(state->file, " \\\n"
00203                 "  {0x%.8x, 0x%.4x, 0x%.4x, \\\n"
00204                 "    { 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x, "
00205                 "0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x }}\n",
00206                 id.m0, id.m1, id.m2,
00207                 id.m3[0], id.m3[1], id.m3[2], id.m3[3],
00208                 id.m3[4], id.m3[5], id.m3[6], id.m3[7]);
00209         fputc('\n', state->file);
00210     } else {
00211         IDL_tree_error(state->tree, "interface %s lacks a uuid attribute\n", 
00212             className);
00213         FAIL;
00214     }
00215 
00216     if (doc_comments != NULL)
00217         printlist(state->file, doc_comments);
00218 
00219     /*
00220      * NS_NO_VTABLE is defined in nsISupportsUtils.h, and defined on windows
00221      * to __declspec(novtable) on windows.  This optimization is safe
00222      * whenever the constructor calls no virtual methods.  Writing in IDL
00223      * almost guarantees this, except for the case when a %{C++ block occurs in
00224      * the interface.  We detect that case, and emit a macro call that disables
00225      * the optimization.
00226      */
00227     keepvtable = FALSE;
00228     for (iter = IDL_INTERFACE(state->tree).body;
00229          iter != NULL;
00230          iter = IDL_LIST(iter).next)
00231     {
00232         IDL_tree data = IDL_LIST(iter).data;
00233         if (IDL_NODE_TYPE(data) == IDLN_CODEFRAG)
00234             keepvtable = TRUE;
00235     }
00236     
00237     /* The interface declaration itself. */
00238     fprintf(state->file,
00239             "class %s%s",
00240             (keepvtable ? "" : "NS_NO_VTABLE "), className);
00241     
00242     if ((iter = IDL_INTERFACE(iface).inheritance_spec)) {
00243         fputs(" : ", state->file);
00244         if (IDL_LIST(iter).next != NULL) {
00245             IDL_tree_error(iter,
00246                            "multiple inheritance is not supported by xpidl");
00247             FAIL;
00248         }
00249         fprintf(state->file, "public %s", IDL_IDENT(IDL_LIST(iter).data).str);
00250     }
00251     fputs(" {\n"
00252           " public: \n\n", state->file);
00253     if (iid) {
00254         fputs("  NS_DEFINE_STATIC_IID_ACCESSOR(", state->file);
00255         write_classname_iid_define(state->file, className);
00256         fputs(")\n\n", state->file);
00257     }
00258 
00259     orig = state->tree; /* It would be nice to remove this state-twiddling. */
00260 
00261     state->tree = IDL_INTERFACE(iface).body;
00262 
00263     if (state->tree && !xpidl_process_node(state))
00264         FAIL;
00265 
00266     fputs("};\n", state->file);
00267     fputc('\n', state->file);
00268 
00269     /*
00270      * #define NS_DECL_NSIFOO - create method prototypes that can be used in
00271      * class definitions that support this interface.
00272      *
00273      * Walk the tree explicitly to prototype a reworking of xpidl to get rid of
00274      * the callback mechanism.
00275      */
00276     state->tree = orig;
00277     fputs("/* Use this macro when declaring classes that implement this "
00278           "interface. */\n", state->file);
00279     fputs("#define NS_DECL_", state->file);
00280     classNameUpper = xpidl_strdup(className);
00281     for (cp = classNameUpper; *cp != '\0'; cp++)
00282         *cp = toupper(*cp);
00283     fprintf(state->file, "%s \\\n", classNameUpper);
00284     if (IDL_INTERFACE(state->tree).body == NULL) {
00285         write_indent(state->file);
00286         fputs("/* no methods! */\n", state->file);
00287     }
00288 
00289     for (iter = IDL_INTERFACE(state->tree).body;
00290          iter != NULL;
00291          iter = IDL_LIST(iter).next)
00292     {
00293         IDL_tree data = IDL_LIST(iter).data;
00294 
00295         switch(IDL_NODE_TYPE(data)) {
00296           case IDLN_OP_DCL:
00297             write_indent(state->file);
00298             write_method_signature(data, state->file, AS_DECL, NULL);
00299             break;
00300 
00301           case IDLN_ATTR_DCL:
00302             write_indent(state->file);
00303             if (!write_attr_accessor(data, state->file, TRUE, AS_DECL, NULL))
00304                 FAIL;
00305             if (!IDL_ATTR_DCL(data).f_readonly) {
00306                 fputs("; \\\n", state->file); /* Terminate the previous one. */
00307                 write_indent(state->file);
00308                 if (!write_attr_accessor(data, state->file,
00309                                          FALSE, AS_DECL, NULL))
00310                     FAIL;
00311                 /* '; \n' at end will clean up. */
00312             }
00313             break;
00314 
00315           case IDLN_CONST_DCL:
00316             /* ignore it here; it doesn't contribute to the macro. */
00317             continue;
00318 
00319           case IDLN_CODEFRAG:
00320             XPIDL_WARNING((iter, IDL_WARNING1,
00321                            "%%{ .. %%} code fragment within interface "
00322                            "ignored when generating NS_DECL_%s macro; "
00323                            "if the code fragment contains method "
00324                            "declarations, the macro probably isn't "
00325                            "complete.", classNameUpper));
00326             continue;
00327 
00328           default:
00329             IDL_tree_error(iter,
00330                            "unexpected node type %d! "
00331                            "Please file a bug against the xpidl component.",
00332                            IDL_NODE_TYPE(data));
00333             FAIL;
00334         }
00335 
00336         if (IDL_LIST(iter).next != NULL) {
00337             fprintf(state->file, "; \\\n");
00338         } else {
00339             fprintf(state->file, "; \n");
00340         }
00341     }
00342     fputc('\n', state->file);
00343 
00344     /* XXX abstract above and below into one function? */
00345     /*
00346      * #define NS_FORWARD_NSIFOO - create forwarding methods that can delegate
00347      * behavior from in implementation to another object.  As generated by
00348      * idlc.
00349      */
00350     fprintf(state->file,
00351             "/* Use this macro to declare functions that forward the "
00352             "behavior of this interface to another object. */\n"
00353             "#define NS_FORWARD_%s(_to) \\\n",
00354             classNameUpper);
00355     if (IDL_INTERFACE(state->tree).body == NULL) {
00356         write_indent(state->file);
00357         fputs("/* no methods! */\n", state->file);
00358     }
00359 
00360     for (iter = IDL_INTERFACE(state->tree).body;
00361          iter != NULL;
00362          iter = IDL_LIST(iter).next)
00363     {
00364         IDL_tree data = IDL_LIST(iter).data;
00365 
00366         switch(IDL_NODE_TYPE(data)) {
00367           case IDLN_OP_DCL:
00368             write_indent(state->file);
00369             write_method_signature(data, state->file, AS_DECL, NULL);
00370             fputs(" { return _to ", state->file);
00371             write_method_signature(data, state->file, AS_CALL, NULL);
00372             break;
00373 
00374           case IDLN_ATTR_DCL:
00375             write_indent(state->file);
00376             if (!write_attr_accessor(data, state->file, TRUE, AS_DECL, NULL))
00377                 FAIL;
00378             fputs(" { return _to ", state->file);
00379             if (!write_attr_accessor(data, state->file, TRUE, AS_CALL, NULL))
00380                 FAIL;
00381             if (!IDL_ATTR_DCL(data).f_readonly) {
00382                 fputs("; } \\\n", state->file); /* Terminate the previous one. */
00383                 write_indent(state->file);
00384                 if (!write_attr_accessor(data, state->file,
00385                                          FALSE, AS_DECL, NULL))
00386                     FAIL;
00387                 fputs(" { return _to ", state->file);
00388                 if (!write_attr_accessor(data, state->file,
00389                                          FALSE, AS_CALL, NULL))
00390                     FAIL;
00391                 /* '; } \n' at end will clean up. */
00392             }
00393             break;
00394 
00395           case IDLN_CONST_DCL:
00396           case IDLN_CODEFRAG:
00397               continue;
00398 
00399           default:
00400             FAIL;
00401         }
00402 
00403         if (IDL_LIST(iter).next != NULL) {
00404             fprintf(state->file, "; } \\\n");
00405         } else {
00406             fprintf(state->file, "; } \n");
00407         }
00408     }
00409     fputc('\n', state->file);
00410 
00411 
00412     /* XXX abstract above and below into one function? */
00413     /*
00414      * #define NS_FORWARD_SAFE_NSIFOO - create forwarding methods that can delegate
00415      * behavior from in implementation to another object.  As generated by
00416      * idlc.
00417      */
00418     fprintf(state->file,
00419             "/* Use this macro to declare functions that forward the "
00420             "behavior of this interface to another object in a safe way. */\n"
00421             "#define NS_FORWARD_SAFE_%s(_to) \\\n",
00422             classNameUpper);
00423     if (IDL_INTERFACE(state->tree).body == NULL) {
00424         write_indent(state->file);
00425         fputs("/* no methods! */\n", state->file);
00426     }
00427 
00428     for (iter = IDL_INTERFACE(state->tree).body;
00429          iter != NULL;
00430          iter = IDL_LIST(iter).next)
00431     {
00432         IDL_tree data = IDL_LIST(iter).data;
00433 
00434         switch(IDL_NODE_TYPE(data)) {
00435           case IDLN_OP_DCL:
00436             write_indent(state->file);
00437             write_method_signature(data, state->file, AS_DECL, NULL);
00438             fputs(" { return !_to ? NS_ERROR_NULL_POINTER : _to->", state->file);
00439             write_method_signature(data, state->file, AS_CALL, NULL);
00440             break;
00441 
00442           case IDLN_ATTR_DCL:
00443             write_indent(state->file);
00444             if (!write_attr_accessor(data, state->file, TRUE, AS_DECL, NULL))
00445                 FAIL;
00446             fputs(" { return !_to ? NS_ERROR_NULL_POINTER : _to->", state->file);
00447             if (!write_attr_accessor(data, state->file, TRUE, AS_CALL, NULL))
00448                 FAIL;
00449             if (!IDL_ATTR_DCL(data).f_readonly) {
00450                 fputs("; } \\\n", state->file); /* Terminate the previous one. */
00451                 write_indent(state->file);
00452                 if (!write_attr_accessor(data, state->file,
00453                                          FALSE, AS_DECL, NULL))
00454                     FAIL;
00455                 fputs(" { return !_to ? NS_ERROR_NULL_POINTER : _to->", state->file);
00456                 if (!write_attr_accessor(data, state->file,
00457                                          FALSE, AS_CALL, NULL))
00458                     FAIL;
00459                 /* '; } \n' at end will clean up. */
00460             }
00461             break;
00462 
00463           case IDLN_CONST_DCL:
00464           case IDLN_CODEFRAG:
00465               continue;
00466 
00467           default:
00468             FAIL;
00469         }
00470 
00471         if (IDL_LIST(iter).next != NULL) {
00472             fprintf(state->file, "; } \\\n");
00473         } else {
00474             fprintf(state->file, "; } \n");
00475         }
00476     }
00477     fputc('\n', state->file);
00478 
00479     /*
00480      * Build a sample implementation template.
00481      */
00482     if (strlen(className) >= 3 && className[2] == 'I') {
00483         classNameImpl = xpidl_strdup(className);
00484         if (!classNameImpl)
00485             FAIL;
00486         memmove(&classNameImpl[2], &classNameImpl[3], strlen(classNameImpl) - 2);
00487     } else {
00488         classNameImpl = xpidl_strdup("_MYCLASS_");
00489         if (!classNameImpl)
00490             FAIL;
00491     }
00492 
00493     fputs("#if 0\n"
00494           "/* Use the code below as a template for the "
00495           "implementation class for this interface. */\n"
00496           "\n"
00497           "/* Header file */"
00498           "\n",
00499           state->file);
00500     fprintf(state->file, "class %s : public %s\n", classNameImpl, className);
00501     fputs("{\n"
00502           "public:\n", state->file);
00503     write_indent(state->file);
00504     fputs("NS_DECL_ISUPPORTS\n", state->file);
00505     write_indent(state->file);
00506     fprintf(state->file, "NS_DECL_%s\n", classNameUpper);
00507     fputs("\n", state->file);
00508     write_indent(state->file);
00509     fprintf(state->file, "%s();\n", classNameImpl);
00510     fputs("\n"
00511           "private:\n", state->file);
00512     write_indent(state->file);
00513     fprintf(state->file, "~%s();\n", classNameImpl);
00514     fputs("\n"
00515           "protected:\n", state->file);
00516     write_indent(state->file);
00517     fputs("/* additional members */\n", state->file);
00518     fputs("};\n\n", state->file);
00519 
00520     fputs("/* Implementation file */\n", state->file);
00521 
00522     fprintf(state->file, 
00523             "NS_IMPL_ISUPPORTS1(%s, %s)\n", classNameImpl, className);
00524     fputs("\n", state->file);
00525     
00526     fprintf(state->file, "%s::%s()\n", classNameImpl, classNameImpl);
00527     fputs("{\n", state->file);
00528     write_indent(state->file);
00529     fputs("/* member initializers and constructor code */\n", state->file);
00530     fputs("}\n\n", state->file);
00531     
00532     fprintf(state->file, "%s::~%s()\n", classNameImpl, classNameImpl);
00533     fputs("{\n", state->file);
00534     write_indent(state->file);
00535     fputs("/* destructor code */\n", state->file);
00536     fputs("}\n\n", state->file);
00537 
00538     for (iter = IDL_INTERFACE(state->tree).body;
00539          iter != NULL;
00540          iter = IDL_LIST(iter).next)
00541     {
00542         IDL_tree data = IDL_LIST(iter).data;
00543 
00544         switch(IDL_NODE_TYPE(data)) {
00545           case IDLN_OP_DCL:
00546             /* It would be nice to remove this state-twiddling. */
00547             orig = state->tree; 
00548             state->tree = data;
00549             xpidl_write_comment(state, 0);
00550             state->tree = orig;
00551 
00552             write_method_signature(data, state->file, AS_IMPL, classNameImpl);
00553             fputs("\n{\n", state->file);
00554             write_indent(state->file);
00555             write_indent(state->file);
00556             fputs("return NS_ERROR_NOT_IMPLEMENTED;\n"
00557                   "}\n"
00558                   "\n", state->file);
00559             break;
00560 
00561           case IDLN_ATTR_DCL:
00562             /* It would be nice to remove this state-twiddling. */
00563             orig = state->tree; 
00564             state->tree = data;
00565             xpidl_write_comment(state, 0);
00566             state->tree = orig;
00567             
00568             if (!write_attr_accessor(data, state->file, TRUE, 
00569                                      AS_IMPL, classNameImpl))
00570                 FAIL;
00571             fputs("\n{\n", state->file);
00572             write_indent(state->file);
00573             write_indent(state->file);
00574             fputs("return NS_ERROR_NOT_IMPLEMENTED;\n"
00575                   "}\n", state->file);
00576 
00577             if (!IDL_ATTR_DCL(data).f_readonly) {
00578                 if (!write_attr_accessor(data, state->file, FALSE, 
00579                                          AS_IMPL, classNameImpl))
00580                     FAIL;
00581                 fputs("\n{\n", state->file);
00582                 write_indent(state->file);
00583                 write_indent(state->file);
00584                 fputs("return NS_ERROR_NOT_IMPLEMENTED;\n"
00585                       "}\n", state->file);
00586             }
00587             fputs("\n", state->file);
00588             break;
00589 
00590           case IDLN_CONST_DCL:
00591           case IDLN_CODEFRAG:
00592               continue;
00593 
00594           default:
00595             FAIL;
00596         }
00597     }
00598 
00599     fputs("/* End of implementation class template. */\n"
00600           "#endif\n"
00601           "\n", state->file);
00602 
00603 #undef FAIL
00604 
00605 out:
00606     if (classNameUpper)
00607         free(classNameUpper);
00608     if (classNameImpl)
00609         free(classNameImpl);
00610     return ok;
00611 }
00612 
00613 static gboolean
00614 list(TreeState *state)
00615 {
00616     IDL_tree iter;
00617     for (iter = state->tree; iter; iter = IDL_LIST(iter).next) {
00618         state->tree = IDL_LIST(iter).data;
00619         if (!xpidl_process_node(state))
00620             return FALSE;
00621     }
00622     return TRUE;
00623 }
00624 
00625 static gboolean
00626 write_type(IDL_tree type_tree, gboolean is_out, FILE *outfile)
00627 {
00628     if (!type_tree) {
00629         fputs("void", outfile);
00630         return TRUE;
00631     }
00632 
00633     switch (IDL_NODE_TYPE(type_tree)) {
00634       case IDLN_TYPE_INTEGER: {
00635         gboolean sign = IDL_TYPE_INTEGER(type_tree).f_signed;
00636         switch (IDL_TYPE_INTEGER(type_tree).f_type) {
00637           case IDL_INTEGER_TYPE_SHORT:
00638             fputs(sign ? "PRInt16" : "PRUint16", outfile);
00639             break;
00640           case IDL_INTEGER_TYPE_LONG:
00641             fputs(sign ? "PRInt32" : "PRUint32", outfile);
00642             break;
00643           case IDL_INTEGER_TYPE_LONGLONG:
00644             fputs(sign ? "PRInt64" : "PRUint64", outfile);
00645             break;
00646           default:
00647             g_error("Unknown integer type %d\n",
00648                     IDL_TYPE_INTEGER(type_tree).f_type);
00649             return FALSE;
00650         }
00651         break;
00652       }
00653       case IDLN_TYPE_CHAR:
00654         fputs("char", outfile);
00655         break;
00656       case IDLN_TYPE_WIDE_CHAR:
00657         fputs("PRUnichar", outfile); /* wchar_t? */
00658         break;
00659       case IDLN_TYPE_WIDE_STRING:
00660         fputs("PRUnichar *", outfile);
00661         break;
00662       case IDLN_TYPE_STRING:
00663         fputs("char *", outfile);
00664         break;
00665       case IDLN_TYPE_BOOLEAN:
00666         fputs("PRBool", outfile);
00667         break;
00668       case IDLN_TYPE_OCTET:
00669         fputs("PRUint8", outfile);
00670         break;
00671       case IDLN_TYPE_FLOAT:
00672         switch (IDL_TYPE_FLOAT(type_tree).f_type) {
00673           case IDL_FLOAT_TYPE_FLOAT:
00674             fputs("float", outfile);
00675             break;
00676           case IDL_FLOAT_TYPE_DOUBLE:
00677             fputs("double", outfile);
00678             break;
00679           /* XXX 'long double' just ignored, or what? */
00680           default:
00681             fprintf(outfile, "unknown_type_%d", IDL_NODE_TYPE(type_tree));
00682             break;
00683         }
00684         break;
00685       case IDLN_IDENT:
00686         if (UP_IS_NATIVE(type_tree)) {
00687             if (IDL_tree_property_get(type_tree, "domstring") ||
00688                 IDL_tree_property_get(type_tree, "astring")) {
00689                 fputs("nsAString", outfile);
00690             } else if (IDL_tree_property_get(type_tree, "utf8string")) {
00691                 fputs("nsACString", outfile);
00692             } else if (IDL_tree_property_get(type_tree, "cstring")) {
00693                 fputs("nsACString", outfile);
00694             } else {
00695                 fputs(IDL_NATIVE(IDL_NODE_UP(type_tree)).user_type, outfile);
00696             }
00697             if (IDL_tree_property_get(type_tree, "ptr")) {
00698                 fputs(" *", outfile);
00699             } else if (IDL_tree_property_get(type_tree, "ref")) {
00700                 fputs(" &", outfile);
00701             }
00702         } else {
00703             fputs(IDL_IDENT(type_tree).str, outfile);
00704         }
00705         if (UP_IS_AGGREGATE(type_tree))
00706             fputs(" *", outfile);
00707         break;
00708       default:
00709         fprintf(outfile, "unknown_type_%d", IDL_NODE_TYPE(type_tree));
00710         break;
00711     }
00712     return TRUE;
00713 }
00714 
00715 /*
00716  * An attribute declaration looks like:
00717  *
00718  * [ IDL_ATTR_DCL]
00719  *   - param_type_spec [IDL_TYPE_* or NULL for void]
00720  *   - simple_declarations [IDL_LIST]
00721  *     - data [IDL_IDENT]
00722  *     - next [IDL_LIST or NULL if no more idents]
00723  *       - data [IDL_IDENT]
00724  */
00725 
00726 #define ATTR_IDENT(tree) (IDL_IDENT(IDL_LIST(IDL_ATTR_DCL(tree).simple_declarations).data))
00727 #define ATTR_TYPE_DECL(tree) (IDL_ATTR_DCL(tree).param_type_spec)
00728 #define ATTR_TYPE(tree) (IDL_NODE_TYPE(ATTR_TYPE_DECL(tree)))
00729 
00730 /*
00731  *  AS_DECL writes 'NS_IMETHOD foo(string bar, long sil)'
00732  *  AS_IMPL writes 'NS_IMETHODIMP className::foo(string bar, long sil)'
00733  *  AS_CALL writes 'foo(bar, sil)'
00734  */
00735 static gboolean
00736 write_attr_accessor(IDL_tree attr_tree, FILE * outfile,
00737                     gboolean getter, int mode, const char *className)
00738 {
00739     char *attrname = ATTR_IDENT(attr_tree).str;
00740 
00741     if (mode == AS_DECL) {
00742         fputs("NS_IMETHOD ", outfile);
00743     } else if (mode == AS_IMPL) {
00744         fprintf(outfile, "NS_IMETHODIMP %s::", className);
00745     }
00746     fprintf(outfile, "%cet%c%s(",
00747             getter ? 'G' : 'S',
00748             toupper(*attrname), attrname + 1);
00749     if (mode == AS_DECL || mode == AS_IMPL) {
00750         /* Setters for string, wstring, nsid, domstring, utf8string, 
00751          * cstring and astring get const. 
00752          */
00753         if (!getter &&
00754             (IDL_NODE_TYPE(ATTR_TYPE_DECL(attr_tree)) == IDLN_TYPE_STRING ||
00755              IDL_NODE_TYPE(ATTR_TYPE_DECL(attr_tree)) == IDLN_TYPE_WIDE_STRING ||
00756              IDL_tree_property_get(ATTR_TYPE_DECL(attr_tree), "nsid") ||
00757              IDL_tree_property_get(ATTR_TYPE_DECL(attr_tree), "domstring")  ||
00758              IDL_tree_property_get(ATTR_TYPE_DECL(attr_tree), "utf8string") ||
00759              IDL_tree_property_get(ATTR_TYPE_DECL(attr_tree), "cstring")    ||
00760              IDL_tree_property_get(ATTR_TYPE_DECL(attr_tree), "astring")))
00761         {
00762             fputs("const ", outfile);
00763         }
00764 
00765         if (!write_type(ATTR_TYPE_DECL(attr_tree), getter, outfile))
00766             return FALSE;
00767         fprintf(outfile, "%s%s",
00768                 (STARRED_TYPE(attr_tree) ? "" : " "),
00769                 (getter && !DIPPER_TYPE(ATTR_TYPE_DECL(attr_tree)))? "*" : "");
00770     }
00771     fprintf(outfile, "a%c%s)", toupper(attrname[0]), attrname + 1);
00772     return TRUE;
00773 }
00774 
00775 static gboolean
00776 attr_dcl(TreeState *state)
00777 {
00778     GSList *doc_comments;
00779 
00780     if (!verify_attribute_declaration(state->tree))
00781         return FALSE;
00782 
00783     doc_comments =
00784         IDL_IDENT(IDL_LIST(IDL_ATTR_DCL
00785                            (state->tree).simple_declarations).data).comments;
00786 
00787     if (doc_comments != NULL) {
00788         write_indent(state->file);
00789         printlist(state->file, doc_comments);
00790     }
00791 
00792     /*
00793      * XXX lists of attributes with the same type, e.g.
00794      * attribute string foo, bar sil;
00795      * are legal IDL... but we don't do anything with 'em.
00796      */
00797     if (IDL_LIST(IDL_ATTR_DCL(state->tree).simple_declarations).next != NULL) {
00798         XPIDL_WARNING((state->tree, IDL_WARNING1,
00799                        "multiple attributes in a single declaration aren't "
00800                        "currently supported by xpidl"));
00801     }
00802 
00803     xpidl_write_comment(state, 2);
00804 
00805     write_indent(state->file);
00806     if (!write_attr_accessor(state->tree, state->file, TRUE, AS_DECL, NULL))
00807         return FALSE;
00808     fputs(" = 0;\n", state->file);
00809 
00810     if (!IDL_ATTR_DCL(state->tree).f_readonly) {
00811         write_indent(state->file);
00812         if (!write_attr_accessor(state->tree, state->file, FALSE, AS_DECL, NULL))
00813             return FALSE;
00814         fputs(" = 0;\n", state->file);
00815     }
00816     fputc('\n', state->file);
00817 
00818     return TRUE;
00819 }
00820 
00821 static gboolean
00822 do_enum(TreeState *state)
00823 {
00824     IDL_tree_error(state->tree, "enums not supported, "
00825                    "see http://bugzilla.mozilla.org/show_bug.cgi?id=8781");
00826     return FALSE;
00827 }
00828 
00829 static gboolean
00830 do_const_dcl(TreeState *state)
00831 {
00832     struct _IDL_CONST_DCL *dcl = &IDL_CONST_DCL(state->tree);
00833     const char *name = IDL_IDENT(dcl->ident).str;
00834     gboolean is_signed;
00835     GSList *doc_comments = IDL_IDENT(dcl->ident).comments;
00836     IDL_tree real_type;
00837     const char *const_format;
00838 
00839     if (!verify_const_declaration(state->tree))
00840         return FALSE;
00841 
00842     if (doc_comments != NULL) {
00843         write_indent(state->file);
00844         printlist(state->file, doc_comments);
00845     }
00846 
00847     /* Could be a typedef; try to map it to the real type. */
00848     real_type = find_underlying_type(dcl->const_type);
00849     real_type = real_type ? real_type : dcl->const_type;
00850     is_signed = IDL_TYPE_INTEGER(real_type).f_signed;
00851 
00852     const_format = is_signed ? "%" IDL_LL "d" : "%" IDL_LL "uU";
00853     write_indent(state->file);
00854     fprintf(state->file, "enum { %s = ", name);
00855     fprintf(state->file, const_format, IDL_INTEGER(dcl->const_exp).value);
00856     fprintf(state->file, " };\n\n");
00857 
00858     return TRUE;
00859 }
00860 
00861 static gboolean
00862 do_typedef(TreeState *state)
00863 {
00864     IDL_tree type = IDL_TYPE_DCL(state->tree).type_spec;
00865     IDL_tree dcls = IDL_TYPE_DCL(state->tree).dcls;
00866     IDL_tree complex;
00867     GSList *doc_comments;
00868 
00869     if (IDL_NODE_TYPE(type) == IDLN_TYPE_SEQUENCE) {
00870         XPIDL_WARNING((state->tree, IDL_WARNING1,
00871                        "sequences not supported, ignored"));
00872     } else {
00873         if (IDL_NODE_TYPE(complex = IDL_LIST(dcls).data) == IDLN_TYPE_ARRAY) {
00874             IDL_tree dim = IDL_TYPE_ARRAY(complex).size_list;
00875             doc_comments = IDL_IDENT(IDL_TYPE_ARRAY(complex).ident).comments;
00876 
00877             if (doc_comments != NULL)
00878                 printlist(state->file, doc_comments);
00879 
00880             fputs("typedef ", state->file);
00881             if (!write_type(type, FALSE, state->file))
00882                 return FALSE;
00883             fputs(" ", state->file);
00884 
00885             fprintf(state->file, "%s",
00886                     IDL_IDENT(IDL_TYPE_ARRAY(complex).ident).str);
00887             do {
00888                 fputc('[', state->file);
00889                 if (IDL_LIST(dim).data) {
00890                     fprintf(state->file, "%ld",
00891                             (long)IDL_INTEGER(IDL_LIST(dim).data).value);
00892                 }
00893                 fputc(']', state->file);
00894             } while ((dim = IDL_LIST(dim).next) != NULL);
00895         } else {
00896             doc_comments = IDL_IDENT(IDL_LIST(dcls).data).comments;
00897 
00898             if (doc_comments != NULL)
00899                 printlist(state->file, doc_comments);
00900 
00901             fputs("typedef ", state->file);
00902             if (!write_type(type, FALSE, state->file))
00903                 return FALSE;
00904             fputs(" ", state->file);
00905             fputs(IDL_IDENT(IDL_LIST(dcls).data).str, state->file);
00906         }
00907         fputs(";\n\n", state->file);
00908     }
00909     return TRUE;
00910 }
00911 
00912 /*
00913  * param generation:
00914  * in string foo        -->     nsString *foo
00915  * out string foo       -->     nsString **foo;
00916  * inout string foo     -->     nsString **foo;
00917  */
00918 
00919 /* If notype is true, just write the param name. */
00920 static gboolean
00921 write_param(IDL_tree param_tree, FILE *outfile)
00922 {
00923     IDL_tree param_type_spec = IDL_PARAM_DCL(param_tree).param_type_spec;
00924     gboolean is_in = IDL_PARAM_DCL(param_tree).attr == IDL_PARAM_IN;
00925     /* in string, wstring, nsid, domstring, utf8string, cstring and 
00926      * astring any explicitly marked [const] are const 
00927      */
00928 
00929     if (is_in &&
00930         (IDL_NODE_TYPE(param_type_spec) == IDLN_TYPE_STRING ||
00931          IDL_NODE_TYPE(param_type_spec) == IDLN_TYPE_WIDE_STRING ||
00932          IDL_tree_property_get(IDL_PARAM_DCL(param_tree).simple_declarator,
00933                                "const") ||
00934          IDL_tree_property_get(param_type_spec, "nsid") ||
00935          IDL_tree_property_get(param_type_spec, "domstring")  ||
00936          IDL_tree_property_get(param_type_spec, "utf8string") ||
00937          IDL_tree_property_get(param_type_spec, "cstring")    ||
00938          IDL_tree_property_get(param_type_spec, "astring"))) {
00939         fputs("const ", outfile);
00940     }
00941     else if (IDL_PARAM_DCL(param_tree).attr == IDL_PARAM_OUT &&
00942              IDL_tree_property_get(IDL_PARAM_DCL(param_tree).simple_declarator, 
00943                                    "shared")) {
00944         fputs("const ", outfile);
00945     }
00946 
00947     if (!write_type(param_type_spec, !is_in, outfile))
00948         return FALSE;
00949 
00950     /* unless the type ended in a *, add a space */
00951     if (!STARRED_TYPE(param_type_spec))
00952         fputc(' ', outfile);
00953 
00954     /* out and inout params get a bonus '*' (unless this is type that has a 
00955      * 'dipper' class that is passed in to receive 'out' data) 
00956      */
00957     if (IDL_PARAM_DCL(param_tree).attr != IDL_PARAM_IN &&
00958         !DIPPER_TYPE(param_type_spec)) {
00959         fputc('*', outfile);
00960     }
00961     /* arrays get a bonus * too */
00962     /* XXX Should this be a leading '*' or a trailing "[]" ?*/
00963     if (IDL_tree_property_get(IDL_PARAM_DCL(param_tree).simple_declarator,
00964                               "array"))
00965         fputc('*', outfile);
00966 
00967     fputs(IDL_IDENT(IDL_PARAM_DCL(param_tree).simple_declarator).str, outfile);
00968 
00969     return TRUE;
00970 }
00971 
00972 /*
00973  * A forward declaration, usually an interface.
00974  */
00975 static gboolean
00976 forward_dcl(TreeState *state)
00977 {
00978     IDL_tree iface = state->tree;
00979     const char *className = IDL_IDENT(IDL_FORWARD_DCL(iface).ident).str;
00980 
00981     if (!className)
00982         return FALSE;
00983 
00984     fprintf(state->file, "class %s; /* forward declaration */\n\n", className);
00985     return TRUE;
00986 }
00987 
00988 /*
00989  * Shared between the interface class declaration and the NS_DECL_IFOO macro
00990  * provided to aid declaration of implementation classes.  
00991  * mode...
00992  *  AS_DECL writes 'NS_IMETHOD foo(string bar, long sil)'
00993  *  AS_IMPL writes 'NS_IMETHODIMP className::foo(string bar, long sil)'
00994  *  AS_CALL writes 'foo(bar, sil)'
00995  */
00996 static gboolean
00997 write_method_signature(IDL_tree method_tree, FILE *outfile, int mode,
00998                        const char *className)
00999 {
01000     struct _IDL_OP_DCL *op = &IDL_OP_DCL(method_tree);
01001     gboolean no_generated_args = TRUE;
01002     gboolean op_notxpcom =
01003         (IDL_tree_property_get(op->ident, "notxpcom") != NULL);
01004     const char *name;
01005     IDL_tree iter;
01006 
01007     if (mode == AS_DECL) {
01008         if (op_notxpcom) {
01009             fputs("NS_IMETHOD_(", outfile);
01010             if (!write_type(op->op_type_spec, FALSE, outfile))
01011                 return FALSE;
01012             fputc(')', outfile);
01013         } else {
01014             fputs("NS_IMETHOD", outfile);
01015         }
01016         fputc(' ', outfile);
01017     }
01018     else if (mode == AS_IMPL) {
01019         if (op_notxpcom) {
01020             fputs("NS_IMETHODIMP_(", outfile);
01021             if (!write_type(op->op_type_spec, FALSE, outfile))
01022                 return FALSE;
01023             fputc(')', outfile);
01024         } else {
01025             fputs("NS_IMETHODIMP", outfile);
01026         }
01027         fputc(' ', outfile);
01028     }
01029     name = IDL_IDENT(op->ident).str;
01030     if (mode == AS_IMPL) {
01031         fprintf(outfile, "%s::%c%s(", className, toupper(*name), name + 1);
01032     } else {
01033         fprintf(outfile, "%c%s(", toupper(*name), name + 1);
01034     }
01035     for (iter = op->parameter_dcls; iter; iter = IDL_LIST(iter).next) {
01036         if (mode == AS_DECL || mode == AS_IMPL) {
01037             if (!write_param(IDL_LIST(iter).data, outfile))
01038                 return FALSE;
01039         } else {
01040             fputs(IDL_IDENT(IDL_PARAM_DCL(IDL_LIST(iter).data)
01041                             .simple_declarator).str,
01042                   outfile);
01043         }
01044         if ((IDL_LIST(iter).next ||
01045              (!op_notxpcom && op->op_type_spec) || op->f_varargs))
01046             fputs(", ", outfile);
01047         no_generated_args = FALSE;
01048     }
01049 
01050     /* make IDL return value into trailing out argument */
01051     if (op->op_type_spec && !op_notxpcom) {
01052         IDL_tree fake_param = IDL_param_dcl_new(IDL_PARAM_OUT,
01053                                                 op->op_type_spec,
01054                                                 IDL_ident_new("_retval"));
01055         if (!fake_param)
01056             return FALSE;
01057         if (mode == AS_DECL || mode == AS_IMPL) {
01058             if (!write_param(fake_param, outfile))
01059                 return FALSE;
01060         } else {
01061             fputs("_retval", outfile);
01062         }
01063         if (op->f_varargs)
01064             fputs(", ", outfile);
01065         no_generated_args = FALSE;
01066     }
01067 
01068     /* varargs go last */
01069     if (op->f_varargs) {
01070         if (mode == AS_DECL || mode == AS_IMPL) {
01071             fputs("nsVarArgs *", outfile);
01072         }
01073         fputs("_varargs", outfile);
01074         no_generated_args = FALSE;
01075     }
01076 
01077     /*
01078      * If generated method has no arguments, output 'void' to avoid C legacy
01079      * behavior of disabling type checking.
01080      */
01081     if (no_generated_args && mode == AS_DECL) {
01082         fputs("void", outfile);
01083     }
01084 
01085     fputc(')', outfile);
01086 
01087     return TRUE;
01088 }
01089 
01090 /*
01091  * A method is an `operation', therefore a method decl is an `op dcl'.
01092  * I blame Elliot.
01093  */
01094 static gboolean
01095 op_dcl(TreeState *state)
01096 {
01097     GSList *doc_comments = IDL_IDENT(IDL_OP_DCL(state->tree).ident).comments;
01098 
01099     /*
01100      * Verify that e.g. non-scriptable methods in [scriptable] interfaces
01101      * are declared so.  Do this in a separate verification pass?
01102      */
01103     if (!verify_method_declaration(state->tree))
01104         return FALSE;
01105 
01106     if (doc_comments != NULL) {
01107         write_indent(state->file);
01108         printlist(state->file, doc_comments);
01109     }
01110     xpidl_write_comment(state, 2);
01111 
01112     write_indent(state->file);
01113     if (!write_method_signature(state->tree, state->file, AS_DECL, NULL))
01114         return FALSE;
01115     fputs(" = 0;\n\n", state->file);
01116 
01117     return TRUE;
01118 }
01119 
01120 static void
01121 write_codefrag_line(gpointer data, gpointer user_data)
01122 {
01123     TreeState *state = (TreeState *)user_data;
01124     const char *line = (const char *)data;
01125     fputs(line, state->file);
01126     fputc('\n', state->file);
01127 }
01128 
01129 static gboolean
01130 codefrag(TreeState *state)
01131 {
01132     const char *desc = IDL_CODEFRAG(state->tree).desc;
01133     GSList *lines = IDL_CODEFRAG(state->tree).lines;
01134     guint fragment_length;
01135     
01136     if (strcmp(desc, "C++") && /* libIDL bug? */ strcmp(desc, "C++\r")) {
01137         XPIDL_WARNING((state->tree, IDL_WARNING1,
01138                        "ignoring '%%{%s' escape. "
01139                        "(Use '%%{C++' to escape verbatim C++ code.)", desc));
01140 
01141         return TRUE;
01142     }
01143 
01144     /*
01145      * Emit #file directive to point debuggers back to the original .idl file
01146      * for the duration of the code fragment.  We look at internal IDL node
01147      * properties _file, _line to do this; hopefully they won't change.
01148      *
01149      * _line seems to refer to the line immediately after the closing %}, so
01150      * we backtrack to get the proper line for the beginning of the block.
01151      */
01152     /*
01153      * Looks like getting this right means maintaining an accurate line
01154      * count of everything generated, so we can set the file back to the
01155      * correct line in the generated file afterwards.  Skipping for now...
01156      */
01157 
01158     fragment_length = g_slist_length(lines);
01159 /*      fprintf(state->file, "#line %d \"%s\"\n", */
01160 /*              state->tree->_line - fragment_length - 1, */
01161 /*              state->tree->_file); */
01162 
01163     g_slist_foreach(lines, write_codefrag_line, (gpointer)state);
01164 
01165     return TRUE;
01166 }
01167 
01168 backend *
01169 xpidl_header_dispatch(void)
01170 {
01171     static backend result;
01172     static nodeHandler table[IDLN_LAST];
01173     static gboolean initialized = FALSE;
01174     
01175     result.emit_prolog = header_prolog;
01176     result.emit_epilog = header_epilog;
01177 
01178     if (!initialized) {
01179         table[IDLN_LIST] = list;
01180         table[IDLN_ATTR_DCL] = attr_dcl;
01181         table[IDLN_OP_DCL] = op_dcl;
01182         table[IDLN_FORWARD_DCL] = forward_dcl;
01183         table[IDLN_TYPE_ENUM] = do_enum;
01184         table[IDLN_INTERFACE] = interface;
01185         table[IDLN_CODEFRAG] = codefrag;
01186         table[IDLN_TYPE_DCL] = do_typedef;
01187         table[IDLN_CONST_DCL] = do_const_dcl;
01188         table[IDLN_NATIVE] = check_native;
01189         initialized = TRUE;
01190     }
01191 
01192     result.dispatch_table = table;
01193     return &result;
01194 }