Added initial support for writing out a header file.
authorJustin Seyster <jseyster@cs.sunysb.edu>
Thu, 15 Jul 2010 23:22:37 +0000 (19:22 -0400)
committerJustin Seyster <jseyster@cs.sunysb.edu>
Thu, 15 Jul 2010 23:22:37 +0000 (19:22 -0400)
src/aop-header.c
src/aop-type.c
src/aop-type.h
src/aop.h

index 9ca2c61c6f728f445f91a9375e26edc63aa1efa7..f98e3747fbbcf83c602d431d6878a80ba9bc236b 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <locale.h>
 #include <stdarg.h>
+#include <stdio.h>
 
 #include <config.h>
 #include <system.h>
 #include "aop-header.h"
 #include "aop-type.h"
 
+
+/* The default license text for InterAspect-generated headers.  This
+   license text _does not_ apply to this file, of course! */
+static const char *default_license =
+  "/* Though this file was auto-generated by InterAspect, it _is not_\n"
+  "   bound by InterAspect's license (and copyright does not belong to\n"
+  "   the InterAspect team).  If this file was provided to you by\n"
+  "   somebody else, contact that person to find out its licensing terms.\n"
+  "   If you generated this file, you can replace this text with your own\n"
+  "   licensing terms by specifying them to aop_write_c_header(). */\n";
+
+/* The default preamble for InterAspect-generated headers. */
+static const char *default_preamble =
+  "/* This header file was auto-generated by an InterAspect-based\n"
+  "   plug-in.  Do not change this file: modifications will be lost and\n"
+  "   may result in errors when InterAspect attempts to parse this file\n"
+  "   in the future. */\n";
+
+/* Some typedefs that InterAspect-generated headers use for
+   parameters. */
+static const char *header_typedefs =
+  "typedef int ALL_SIGNED_T;\n"
+  "typedef unsigned int ALL_UNSIGNED_T;\n"
+  "typedef double ALL_FP_T;\n"
+  "typedef void *ALL_POINTER_T;\n";
+
 /* An aop_prototype struct represents the prototype of an advice
    function, including its name, return type and parameters. */
 struct aop_prototype {
@@ -201,6 +228,204 @@ insert_prototype (bool has_return_value, const char *name, int num_params,
   memcpy (prototype->param_types, param_types, param_types_size);
 }
 
+/* Returns true if symbol is a valid C preprocessor symbol. */
+static bool
+is_valid_c_symbol (const char *symbol)
+{
+  /* No, the empty string is not a valid symbol. */
+  if (*symbol == '\0')
+    return false;
+
+  /* C symbols may contain numerals, but they may not begin with
+     one. */
+  if (*symbol >= '0' && *symbol <= '9')
+    return false;
+
+  for (; *symbol != '\0'; symbol++)
+    {
+      if (*symbol >= '0' && *symbol <= '9')
+       continue;
+      else if (*symbol >= 'a' && *symbol <= 'z')
+       continue;
+      else if (*symbol >= 'A' && *symbol <= 'Z')
+       continue;
+      else if (*symbol == '_')
+       continue;
+      else
+       return false;  /* Invalid character. */
+    }
+
+  return true;  /* No invalid characters. */
+}
+
+/* Quick-and-dirty printf variant that will check errors for us. */
+#define CHECK_PRINTF(format, ...)                      \
+  do {                                                 \
+    size = fprintf (header, format, __VA_ARGS__);      \
+    if (size < 0)                                      \
+      return 0;                                                \
+  } while (0)
+
+/* Write a single aop_prototype to a header.  This is used as a
+   callback for htab_traverse.  Returns 0 to stop the table traversal
+   in the event of an error. */
+static int
+write_prototype (void **table_entry, void *info)
+{
+  int i;
+  int size;
+  struct aop_prototype *prototype = *table_entry;
+  FILE *header = info;
+  char c_type[256];
+
+  /* Note that an error will set the stream's error, so we don't need
+     to return anything. */
+  CHECK_PRINTF ("void %s(", prototype->name);
+
+  /* Print each of the types. */
+  for (i = 0; i < prototype->num_params; i++)
+    {
+      const char *separator;
+      const struct aop_type *param_type = prototype->param_types[i];
+      size = format_c_type (param_type, sizeof (c_type), c_type);
+
+      /* There's not much we can do to fix this! */
+      if (size >= sizeof (c_type))
+       sprintf (c_type, "#OVERLONG_TYPE_NAME#");
+
+      /* Use a comma separator if there are more parameters to
+        list. */
+      if (i < prototype->num_params - 1)
+       separator = ", ";
+      else
+       separator = "";
+
+      CHECK_PRINTF ("%s%s", c_type, separator);
+    }
+
+  CHECK_PRINTF ("%s", ");\n");
+
+  return 1;  /* Continue the traversal. */
+}
+
+#undef CHECK_PRINTF
+
+/* Write the actual header contents to an already opened file.
+   Returns 0 on success or a UNIX error on failure. */
+static int
+write_header_contents (FILE *header, const char *guard, const char *license,
+                      const char *preamble)
+{
+  int size;
+
+  /* Print the guard. */
+  if (guard != NULL)
+    {
+      size = fprintf (header, "#ifndef %s\n#define %s\n\n", guard, guard);
+      if (size < 0)
+       return errno;
+    }
+
+  /* Print the license and preamble text and header typedefs. */
+  if (license == NULL)
+    license = default_license;
+  if (preamble == NULL)
+    preamble = default_preamble;
+
+  size = fprintf (header, "%s\n%s\n%s\n", license, preamble, header_typedefs);
+  if (size < 0)
+    return errno;
+
+  /* Print each of the function prototypes by traversing the hash
+     table. */
+  htab_traverse (prototype_table, write_prototype, header);
+  if (ferror (header))
+    return errno;
+
+  /* Close the guard. */
+  if (guard != NULL)
+    {
+      size = fprintf (header, "\n#endif\n");
+      if (size < 0)
+       return errno;
+    }
+
+  return 0; /* Success. */
+}
+
+/**
+ * Add prototypes for every advice function added with
+ * aop_insert_advice().  If the specified header file already exists,
+ * aop_write_c_header() reads it and adds only prototypes that do not
+ * already appear in the file.  InterAspect uses advisory file locks
+ * to control access to a header file, so it is ok for parallel
+ * compiles to write to the same header (so long as your system
+ * supports advisory locks).
+ *
+ * Note that generated header files <i>are not</i> bound to
+ * InterAspect's license.  If you choose to distribute an
+ * InterAspect-generated header, you may use any terms you wish.
+ *
+ * \param filename The name of the header file to generate.  The file
+ * will be created if it does not exist.  May not be NULL.
+ * \param guard A valid C preprocessor symbol to use for the header
+ * file's ifdef guard.  If this is NULL, the header file will not have
+ * an ifdef guard.  Guard symbols are typically of the form
+ * _FILENAME_H_.
+ * \param license Your choice of license text, if you wish to
+ * distribute the generated header under a license.  Your text should
+ * be in the form of a C comment.  If this is NULL, the header will
+ * begin with the default license text, which indicates that the file
+ * is <i>not bound</i> by the terms of any license.  Pass the empty
+ * string if you do not wish to include any license text.
+ * \param preamble Any other preamble text you wish to begin the
+ * header with.  This text should be valid C/C++ comments or code.  If
+ * this is NULL, the header preamble will be a general comment about
+ * InterAspect-generated headers.
+ * \return On success, the return value is 0.  On failure, the return
+ * value is one of the UNIX error codes from errno.h.
+ * - EINVAL: The specified filename is NULL, or the specified guard is
+ *   not a valid C preprocessor symbol.
+ * - Any of the possible errno results from the open(2) function.
+ * - Any of the possible errno results from the write(2) function.
+ * - Any of the possilbe errno results from the fcntl(2) function.
+ * - Any of the possilbe errno results from the fprintf(3) function.
+ * Failure will also set errno to the returned error value, so you can
+ * use the perror(3) function to print a user-readable error message.
+ */
+int
+aop_write_c_header (const char *filename, const char *guard,
+                   const char *license, const char *preamble)
+{
+  int result = 0;
+  FILE *header;
+
+  if (filename == NULL)
+    return EINVAL;
+
+  if (guard != NULL && !is_valid_c_symbol (guard))
+    return EINVAL;
+
+  header = fopen (filename, "w+");
+  if (header == NULL)
+    return errno;
+
+  result = write_header_contents (header, guard, license, preamble);
+
+  /* TODO: Flush */
+
+  if (fclose (header) != 0)
+    {
+      /* A bad file descriptor means an error in InterAspect. */
+      aop_assert (errno != EBADF);
+
+      /* Anything else is a regular I/O error. */
+      result = errno;
+    }
+
+  return result;
+}
+
 void
 init_prototype_table()
 {
index 52eb02237858668f244375d62797354cb3c07f66..605bf7835ee702d46d065166f29ed705be49d06d 100644 (file)
@@ -117,3 +117,55 @@ are_types_equal (const struct aop_type *a, const struct aop_type *b)
 {
   return (a == b);
 }
+
+/* C names for special aop_type objects. */
+static const char *all_signed_name = "ALL_SIGNED_T";
+static const char *all_unsigned_name = "ALL_UNSIGNED_T";
+static const char *all_fp_name = "ALL_FP_T";
+static const char *all_pointer_name = "ALL_POINTER_T";
+
+/* Format an aop_type as a C/C++ typename.  The result is stored in
+   the output buffer (n is the size of the buffer).  Returns the
+   number characters written (but without regard to truncation because
+   of a too small output buffer, as with snprintf()). */
+int
+format_c_type (const struct aop_type *type, int n, char *output)
+{
+  int size;
+
+  if (type->kind == ATK_ALL_POINTER)
+    {
+      size = snprintf (output, n, "%s", all_pointer_name);
+      goto terminate;
+    }
+  else if (type->pointer_levels == 0)
+    {
+      switch (type->kind)
+       {
+       case ATK_ALL_SIGNED:
+         size = snprintf (output, n, "%s", all_signed_name);
+         goto terminate;
+       case ATK_ALL_UNSIGNED:
+         size = snprintf (output, n, "%s", all_unsigned_name);
+         goto terminate;
+       case ATK_ALL_FP:
+         size = snprintf (output, n, "%s", all_fp_name);
+         goto terminate;
+       default:
+         break;
+       }
+    }
+
+  aop_assert (0);
+
+ terminate:
+  /* Before returning, make absolutely sure that the result is
+     NULL-terminated.  Note that snprintf will not terminate a string
+     that it has to truncate because of too small buffer. */
+  if (size < n)
+    output[size] = '\0';
+  else
+    output[n - 1] = '\0';
+
+  return size;
+}
index aa8d3799c73194924e4e8ff0bbb0a3028b8add87..4eff58c411f36fa002626b30408c9c663e8d5b7f 100644 (file)
@@ -38,4 +38,6 @@ struct aop_type {
 bool does_type_match (tree gcc_type, const struct aop_type *aop_type);
 bool are_types_equal (const struct aop_type *a, const struct aop_type *b);
 
+int format_c_type (const struct aop_type *type, int n, char *output);
+
 #endif
index b74f0800f974786af0fb41e895c36a833f3ffbdd..d2d40bafa59ce53e4d637f961533c48d373dcbeb 100644 (file)
--- a/src/aop.h
+++ b/src/aop.h
@@ -263,4 +263,7 @@ extern struct aop_dynval *aop_capture_lhs_addr (struct aop_joinpoint *jp);
 extern const char *aop_capture_lhs_name (struct aop_joinpoint *jp);
 extern struct aop_dynval *aop_capture_assigned_value (struct aop_joinpoint *jp);
 
+extern int aop_write_c_header (const char *filename, const char *guard,
+                              const char *license, const char *preamble);
+
 #endif