OSDN Git Service

* expr.c, gjavah.c, javaop.h, jcf-dump.c, jcf-io.c, jcf-reader.c,
[pf3gnuchains/gcc-fork.git] / gcc / java / jcf-io.c
1 /* Utility routines for finding and reading Java(TM) .class files.
2    Copyright (C) 1996, 1997, 1998, 1999, 2000, 2002, 2003
3    Free Software Foundation, Inc.
4
5 This file is part of GCC.
6
7 GCC is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11
12 GCC 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
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING.  If not, write to
19 the Free Software Foundation, 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA.  
21
22 Java and all Java-based marks are trademarks or registered trademarks
23 of Sun Microsystems, Inc. in the United States and other countries.
24 The Free Software Foundation is independent of Sun Microsystems, Inc.  */
25
26 /* Written by Per Bothner <bothner@cygnus.com>, February 1996. */
27
28 #include "config.h"
29 #include "system.h"
30 #include "coretypes.h"
31 #include "tm.h"
32
33 #include "jcf.h"
34 #include "tree.h"
35 #include "toplev.h"
36 #include "java-tree.h"
37 #include "hashtab.h"
38 #if JCF_USE_SCANDIR
39 #include <dirent.h>
40 #include <fnmatch.h>
41 #endif
42
43 #include "zlib.h"
44
45 /* DOS brain-damage */
46 #ifndef O_BINARY
47 #define O_BINARY 0 /* MS-DOS brain-damage */
48 #endif
49
50 int
51 jcf_unexpected_eof (JCF *jcf, int count ATTRIBUTE_UNUSED)
52 {
53   if (jcf->filename)
54     fprintf (stderr, "Premature end of .class file %s.\n", jcf->filename);
55   else
56     fprintf (stderr, "Premature end of .class file <stdin>.\n");
57   exit (-1);
58 }
59
60 void
61 jcf_trim_old_input (JCF *jcf)
62 {
63   int count = jcf->read_ptr - jcf->buffer;
64   if (count > 0)
65     {
66       memmove (jcf->buffer, jcf->read_ptr, jcf->read_end - jcf->read_ptr);
67       jcf->read_ptr -= count;
68       jcf->read_end -= count;
69     }
70 }
71
72 int
73 jcf_filbuf_from_stdio (JCF *jcf, int count)
74 {
75   FILE *file = (FILE*) (jcf->read_state);
76   if (count > jcf->buffer_end - jcf->read_ptr)
77     {
78       JCF_u4 old_read_ptr = jcf->read_ptr - jcf->buffer;
79       JCF_u4 old_read_end = jcf->read_end - jcf->buffer;
80       JCF_u4 old_size = jcf->buffer_end - jcf->buffer;
81       JCF_u4 new_size = (old_size == 0 ? 2000 : 2 * old_size) + count;
82       unsigned char *new_buffer = jcf->buffer == NULL ? ALLOC (new_size)
83         : REALLOC (jcf->buffer, new_size);
84       jcf->buffer = new_buffer;
85       jcf->buffer_end = new_buffer + new_size;
86       jcf->read_ptr = new_buffer + old_read_ptr;
87       jcf->read_end = new_buffer + old_read_end;
88     }
89   count -= jcf->read_end - jcf->read_ptr;
90   if (count <= 0)
91     return 0;
92   if ((int) fread (jcf->read_end, 1, count, file) != count)
93     jcf_unexpected_eof (jcf, count);
94   jcf->read_end += count;
95   return 0;
96 }
97
98 #include "zipfile.h"
99
100 struct ZipFile *SeenZipFiles = NULL;
101
102 /* Open a zip file with the given name, and cache directory and file
103    descriptor.  If the file is missing, treat it as an empty archive.
104    Return NULL if the .zip file is malformed.
105 */
106
107 ZipFile *
108 opendir_in_zip (const char *zipfile, int is_system)
109 {
110   struct ZipFile* zipf;
111   char magic [4];
112   int fd;
113   for (zipf = SeenZipFiles;  zipf != NULL;  zipf = zipf->next)
114     {
115       if (strcmp (zipf->name, zipfile) == 0)
116         return zipf;
117     }
118
119   zipf = ALLOC (sizeof (struct ZipFile) + strlen (zipfile) + 1);
120   zipf->next = SeenZipFiles;
121   zipf->name = (char*)(zipf+1);
122   strcpy (zipf->name, zipfile);
123   SeenZipFiles = zipf;
124   fd = open (zipfile, O_RDONLY | O_BINARY);
125   zipf->fd = fd;
126   if (fd < 0)
127     {
128       /* A missing zip file is not considered an error.
129        We may want to re-consider that.  FIXME. */
130       zipf->count = 0;
131       zipf->dir_size = 0;
132       zipf->central_directory = NULL;
133     }
134   else
135     {
136       jcf_dependency_add_file (zipfile, is_system);
137       if (read (fd, magic, 4) != 4 || GET_u4 (magic) != (JCF_u4)ZIPMAGIC)
138         return NULL;
139       lseek (fd, 0L, SEEK_SET);
140       if (read_zip_archive (zipf) != 0)
141         return NULL;
142     }
143   return zipf;
144 }
145
146 /* Returns:
147    0:  OK - zipmember found.
148    -1: Not found.
149    -2: Malformed archive.
150 */
151
152 int
153 open_in_zip (JCF *jcf, const char *zipfile, const char *zipmember,
154              int is_system)
155 {
156   ZipDirectory *zipd;
157   int i, len;
158   ZipFile *zipf = opendir_in_zip (zipfile, is_system);
159
160   if (zipf == NULL)
161     return -2;
162
163   if (!zipmember)
164     return 0;
165
166   len = strlen (zipmember);
167   
168   zipd = (struct ZipDirectory*) zipf->central_directory;
169   for (i = 0; i < zipf->count; i++, zipd = ZIPDIR_NEXT (zipd))
170     {
171       if (len == zipd->filename_length &&
172           strncmp (ZIPDIR_FILENAME (zipd), zipmember, len) == 0)
173         {
174           JCF_ZERO (jcf);
175
176           jcf->filename = xstrdup (zipfile);
177           jcf->classname = xstrdup (zipmember);
178           return read_zip_member(jcf, zipd, zipf);
179         }
180     }
181   return -1;
182 }
183
184 /* Read data from zip archive member. */
185
186 int
187 read_zip_member (JCF *jcf,  ZipDirectory *zipd, ZipFile *zipf)
188 {
189           jcf->filbuf = jcf_unexpected_eof;
190           jcf->zipd = (void *)zipd;
191
192           if (zipd->compression_method == Z_NO_COMPRESSION)
193             {
194               jcf->buffer = ALLOC (zipd->size);
195               jcf->buffer_end = jcf->buffer + zipd->size;
196               jcf->read_ptr = jcf->buffer;
197               jcf->read_end = jcf->buffer_end;
198               if (lseek (zipf->fd, zipd->filestart, 0) < 0
199                   || read (zipf->fd, jcf->buffer, zipd->size) != (long) zipd->size)
200                 return -2;
201             }
202           else
203             {
204               char *buffer;
205               z_stream d_stream; /* decompression stream */
206               d_stream.zalloc = (alloc_func) 0;
207               d_stream.zfree = (free_func) 0;
208               d_stream.opaque = (voidpf) 0;
209
210               jcf->buffer = ALLOC (zipd->uncompressed_size);
211               d_stream.next_out = jcf->buffer;
212               d_stream.avail_out = zipd->uncompressed_size;
213               jcf->buffer_end = jcf->buffer + zipd->uncompressed_size;
214               jcf->read_ptr = jcf->buffer;
215               jcf->read_end = jcf->buffer_end;
216               buffer = ALLOC (zipd->size);
217               d_stream.next_in = buffer;
218               d_stream.avail_in = zipd->size;
219               if (lseek (zipf->fd, zipd->filestart, 0) < 0
220                   || read (zipf->fd, buffer, zipd->size) != (long) zipd->size)
221                 return -2;
222               /* Handle NO_HEADER using undocumented zlib feature.
223                  This is a very common hack.  */
224               inflateInit2 (&d_stream, -MAX_WBITS);
225               inflate (&d_stream, Z_NO_FLUSH);
226               inflateEnd (&d_stream);
227               FREE (buffer);
228             }
229
230           return 0;
231 }
232
233 const char *
234 open_class (const char *filename, JCF *jcf, int fd, const char *dep_name)
235 {
236   if (jcf)
237     {
238       struct stat stat_buf;
239       if (fstat (fd, &stat_buf) != 0
240           || ! S_ISREG (stat_buf.st_mode))
241         {
242           perror ("Could not figure length of .class file");
243           return NULL;
244         }
245       if (dep_name != NULL)
246         jcf_dependency_add_file (dep_name, 0);
247       JCF_ZERO (jcf);
248       jcf->buffer = ALLOC (stat_buf.st_size);
249       jcf->buffer_end = jcf->buffer + stat_buf.st_size;
250       jcf->read_ptr = jcf->buffer;
251       jcf->read_end = jcf->buffer_end;
252       jcf->read_state = NULL;
253       jcf->filename = filename;
254       if (read (fd, jcf->buffer, stat_buf.st_size) != stat_buf.st_size)
255         {
256           perror ("Failed to read .class file");
257           return NULL;
258         }
259       close (fd);
260       jcf->filbuf = jcf_unexpected_eof;
261     }
262   else
263     close (fd);
264   return filename;
265 }
266
267
268 const char *
269 find_classfile (char *filename, JCF *jcf, const char *dep_name)
270 {
271   int fd = open (filename, O_RDONLY | O_BINARY);
272   if (fd < 0)
273     return NULL;
274   return open_class (filename, jcf, fd, dep_name);
275 }
276
277 #if JCF_USE_SCANDIR
278
279 /* A comparison function (as for qsort) that compares KEY (a char *
280    giving the basename of a file) with the name stored in ENTRY (a
281    dirent **).  */
282
283 static int
284 compare_path (const void *key, const void *entry)
285 {
286   return strcmp ((const char *) key, 
287                  (*((const struct dirent **) entry))->d_name);
288 }
289
290 /* Returns nonzero if ENTRY names a .java or .class file.  */
291
292 static int
293 java_or_class_file (const struct dirent *entry)
294 {
295   const char *base = basename (entry->d_name);
296   return (fnmatch ("*.java", base, 0) == 0 || 
297           fnmatch ("*.class", base, 0) == 0);
298 }
299
300 /* Information about the files present in a particular directory.  */
301 typedef struct memoized_dirlist_entry 
302 {
303   /* The name of the directory.  */
304   const char *dir;
305   /* The number of .java and .class files present, or -1 if we could
306      not, for some reason, obtain the list.  */
307   int num_files;
308   /* The .java and .class files in the directory, in alphabetical
309      order.  */
310   struct dirent **files;
311 } memoized_dirlist_entry;
312
313 /* Returns true if ENTRY (a memoized_dirlist_entry *) correponds to
314    the directory given by KEY (a char *) giving the directory 
315    name.  */
316
317 static int
318 memoized_dirlist_lookup_eq (const void *entry, const void *key)
319 {
320   return strcmp ((const char *) key,
321                  ((const memoized_dirlist_entry *) entry)->dir) == 0;
322 }
323
324 /* A hash table mapping directory names to the lists of .java and
325    .class files in that directory.  */
326
327 static htab_t memoized_dirlists;
328
329 #endif
330
331 /* Like stat, but avoids actually making the stat system call if we
332    know that it cannot succeed.  FILENAME and BUF are as for stat.  */
333
334 static int
335 caching_stat (char *filename, struct stat *buf)
336 {
337 #if JCF_USE_SCANDIR
338   char *sep;
339   char *base;
340   memoized_dirlist_entry *dent;
341   void **slot;
342   
343   /* If the hashtable has not already been created, create it now.  */
344   if (!memoized_dirlists)
345     memoized_dirlists = htab_create (37,
346                                      htab_hash_string,
347                                      memoized_dirlist_lookup_eq,
348                                      NULL);
349
350   /* Get the name of the directory.  */
351   sep = strrchr (filename, DIR_SEPARATOR);
352   if (sep)
353     {
354       *sep = '\0';
355       base = sep + 1;
356     }
357   else
358     base = filename;
359
360   /* Obtain the entry for this directory form the hash table.  */
361   slot = htab_find_slot (memoized_dirlists, filename, INSERT);
362   if (!*slot)
363     {
364       /* We have not already scanned this directory; scan it now.  */
365       dent = ((memoized_dirlist_entry *) 
366               ALLOC (sizeof (memoized_dirlist_entry)));
367       dent->dir = xstrdup (filename);
368       /* Unfortunately, scandir is not fully standardized.  In
369          particular, the type of the function pointer passed as the
370          third argument sometimes takes a "const struct dirent *"
371          parameter, and sometimes just a "struct dirent *".  We rely
372          on the ability to interchange these two types of function
373          pointers.  */
374       dent->num_files = scandir (filename, &dent->files, 
375                                  java_or_class_file, 
376                                  alphasort);
377       *slot = dent;
378     }
379   else
380     dent = *((memoized_dirlist_entry **) slot);
381
382   /* Put the spearator back.  */
383   if (sep)
384     *sep = DIR_SEPARATOR;
385
386   /* If the file is not in the list, there is no need to stat it; it
387      does not exist.  */
388   if (dent->num_files != -1
389       && !bsearch (base, dent->files, dent->num_files,
390                    sizeof (struct dirent *), compare_path))
391     return -1;
392 #endif
393   
394   return stat (filename, buf);
395 }
396
397 /* Returns 1 if the CLASSNAME (really a char *) matches the name
398    stored in TABLE_ENTRY (also a char *).  */
399
400 static int
401 memoized_class_lookup_eq (const void *table_entry, const void *classname)
402 {
403   return strcmp ((const char *)classname, (const char *)table_entry) == 0;
404 }
405
406 /* A hash table keeping track of class names that were not found
407    during class lookup.  (There is no need to cache the values
408    associated with names that were found; they are saved in
409    IDENTIFIER_CLASS_VALUE.)  */
410 static htab_t memoized_class_lookups;
411
412 /* Returns a freshly malloc'd string with the fully qualified pathname
413    of the .class file for the class CLASSNAME.  CLASSNAME must be
414    allocated in permanent storage; this function may retain a pointer
415    to it.  Returns NULL on failure.  If JCF != NULL, it is suitably
416    initialized.  SOURCE_OK is true if we should also look for .java
417    file. */
418
419 const char *
420 find_class (const char *classname, int classname_length, JCF *jcf,
421             int source_ok)
422 {
423   int fd;
424   int i, k, java = -1, class = -1;
425   struct stat java_buf, class_buf;
426   char *dep_file;
427   void *entry;
428   char *java_buffer;
429   int buflen;
430   char *buffer;
431   hashval_t hash;
432
433   /* Create the hash table, if it does not already exist.  */
434   if (!memoized_class_lookups)
435     memoized_class_lookups = htab_create (37, 
436                                           htab_hash_string, 
437                                           memoized_class_lookup_eq,
438                                           NULL);
439
440   /* Loop for this class in the hashtable.  If it is present, we've
441      already looked for this class and failed to find it.  */
442   hash = htab_hash_string (classname);
443   if (htab_find_with_hash (memoized_class_lookups, classname, hash))
444     return NULL;
445
446   /* Allocate and zero out the buffer, since we don't explicitly put a
447      null pointer when we're copying it below.  */
448   buflen = jcf_path_max_len () + classname_length + 10;
449   buffer = ALLOC (buflen);
450   memset (buffer, 0, buflen);
451
452   java_buffer = alloca (buflen);
453
454   jcf->java_source = 0;
455
456   for (entry = jcf_path_start (); entry != NULL; entry = jcf_path_next (entry))
457     {
458       const char *path_name = jcf_path_name (entry);
459       if (class != 0)
460         {
461           int dir_len;
462
463           strcpy (buffer, path_name);
464           i = strlen (buffer);
465
466           /* This is right because we know that `.zip' entries will have a
467              trailing slash.  See jcf-path.c.  */
468           dir_len = i - 1;
469
470           for (k = 0; k < classname_length; k++, i++)
471             {
472               char ch = classname[k];
473               buffer[i] = ch == '.' ? '/' : ch;
474             }
475           strcpy (buffer+i, ".class");
476
477           if (jcf_path_is_zipfile (entry))
478             {
479               int err_code;
480               JCF _jcf;
481               buffer[dir_len] = '\0';
482               SOURCE_FRONTEND_DEBUG 
483                 (("Trying [...%s]:%s", 
484                   &buffer[dir_len-(dir_len > 15 ? 15 : dir_len)], 
485                   buffer+dir_len+1));
486               if (jcf == NULL)
487                 jcf = &_jcf;
488               err_code = open_in_zip (jcf, buffer, buffer+dir_len+1,
489                                       jcf_path_is_system (entry));
490               if (err_code == 0)
491                 {
492                   /* Should we check if .zip is out-of-date wrt .java? */
493                   buffer[dir_len] = '(';
494                   strcpy (buffer+i, ".class)");
495                   if (jcf == &_jcf)
496                     JCF_FINISH (jcf);
497                   return buffer;
498                 }
499               else
500                 continue;
501             }
502           class = caching_stat(buffer, &class_buf);
503         }
504
505       if (source_ok)
506         {
507           /* Compute name of .java file.  */
508           int l, m;
509           strcpy (java_buffer, path_name);
510           l = strlen (java_buffer);
511           for (m = 0; m < classname_length; ++m)
512             java_buffer[m + l] = (classname[m] == '.' ? '/' : classname[m]);
513           strcpy (java_buffer + m + l, ".java");
514           java = caching_stat (java_buffer, &java_buf);
515           if (java == 0)
516             break;
517         }
518     }
519
520   /* We preferably pick a class file if we have a chance. If the source
521      file is newer than the class file, we issue a warning and parse the
522      source file instead.
523      There should be a flag to allow people have the class file picked
524      up no matter what. FIXME. */
525   if (! java && ! class && java_buf.st_mtime > class_buf.st_mtime)
526     {
527       if (flag_newer)
528         warning ("source file for class `%s' is newer than its matching class file.  Source file `%s' used instead", classname, java_buffer);
529       class = -1;
530     }
531
532   if (! java)
533     dep_file = java_buffer;
534   else
535     dep_file = buffer;
536   if (!class)
537     {
538       SOURCE_FRONTEND_DEBUG ((stderr, "[Class selected: %s]\n",
539                               classname+classname_length-
540                               (classname_length <= 30 ? 
541                                classname_length : 30)));
542       fd = open (buffer, O_RDONLY | O_BINARY);
543       if (fd >= 0)
544         goto found;
545     }
546   /* Give .java a try, if necessary */
547   if (!java)
548     {
549       strcpy (buffer, java_buffer);
550       SOURCE_FRONTEND_DEBUG ((stderr, "[Source selected: %s]\n",
551                               classname+classname_length-
552                               (classname_length <= 30 ? 
553                                classname_length : 30)));
554       fd = open (buffer, O_RDONLY);
555       if (fd >= 0)
556         {
557           jcf->java_source = 1;
558           goto found;
559         }
560     }
561
562   free (buffer);
563
564   /* Remember that this class could not be found so that we do not
565      have to look again.  */
566   *htab_find_slot_with_hash (memoized_class_lookups, classname, hash, INSERT) 
567     = (void *) classname;
568
569   return NULL;
570  found:
571   if (jcf->java_source)
572     {
573       JCF_ZERO (jcf);           /* JCF_FINISH relies on this */
574       jcf->java_source = 1;
575       jcf->filename = xstrdup (buffer);
576       close (fd);               /* We use STDIO for source file */
577     }
578   else
579     buffer = (char *) open_class (buffer, jcf, fd, dep_file);
580   jcf->classname = xstrdup (classname);
581   return buffer;
582 }
583
584 void
585 jcf_print_char (FILE *stream, int ch)
586 {
587   switch (ch)
588     {
589     case '\'':
590     case '\\':
591     case '\"':
592       fprintf (stream, "\\%c", ch);
593       break;
594     case '\n':
595       fprintf (stream, "\\n");
596       break;
597     case '\t':
598       fprintf (stream, "\\t");
599       break;
600     case '\r':
601       fprintf (stream, "\\r");
602       break;
603     default:
604       if (ch >= ' ' && ch < 127)
605         putc (ch, stream);
606       else if (ch < 256)
607         fprintf (stream, "\\%03x", ch);
608       else
609         fprintf (stream, "\\u%04x", ch);
610     }
611 }
612
613 /* Print UTF8 string at STR of length LENGTH bytes to STREAM. */
614
615 void
616 jcf_print_utf8 (FILE *stream, register const unsigned char *str, int length)
617 {
618   const unsigned char * limit = str + length;
619   while (str < limit)
620     {
621       int ch = UTF8_GET (str, limit);
622       if (ch < 0)
623         {
624           fprintf (stream, "\\<invalid>");
625           return;
626         }
627       jcf_print_char (stream, ch);
628     }
629 }
630
631 /* Same as jcf_print_utf8, but print IN_CHAR as OUT_CHAR. */
632
633 void
634 jcf_print_utf8_replace (FILE *stream, const unsigned char *str, int length,
635                         int in_char, int out_char)
636 {
637   const unsigned char *limit = str + length;
638   while (str < limit)
639     {
640       int ch = UTF8_GET (str, limit);
641       if (ch < 0)
642         {
643           fprintf (stream, "\\<invalid>");
644           return;
645         }
646       jcf_print_char (stream, ch == in_char ? out_char : ch);
647     }
648 }
649
650 /* Check that all the cross-references in the constant pool are
651    valid.  Returns 0 on success.
652    Otherwise, returns the index of the (first) invalid entry.
653    Only checks internal consistency, but does not check that
654    any classes, fields, or methods are valid.*/
655
656 int
657 verify_constant_pool (JCF *jcf)
658 {
659   int i, n;
660   for (i = 1; i < JPOOL_SIZE (jcf); i++)
661     {
662       switch (JPOOL_TAG (jcf, i))
663         {
664         case CONSTANT_NameAndType:
665           n = JPOOL_USHORT2 (jcf, i);
666           if (n <= 0 || n >= JPOOL_SIZE(jcf)
667               || JPOOL_TAG (jcf, n) != CONSTANT_Utf8)
668             return i;
669           /* ... fall through ... */
670         case CONSTANT_Class:
671         case CONSTANT_String:
672           n = JPOOL_USHORT1 (jcf, i);
673           if (n <= 0 || n >= JPOOL_SIZE(jcf)
674               || JPOOL_TAG (jcf, n) != CONSTANT_Utf8)
675             return i;
676           break;
677         case CONSTANT_Fieldref:
678         case CONSTANT_Methodref:
679         case CONSTANT_InterfaceMethodref:
680           n = JPOOL_USHORT1 (jcf, i);
681           if (n <= 0 || n >= JPOOL_SIZE(jcf)
682               || JPOOL_TAG (jcf, n) != CONSTANT_Class)
683             return i;
684           n = JPOOL_USHORT2 (jcf, i);
685           if (n <= 0 || n >= JPOOL_SIZE(jcf)
686               || JPOOL_TAG (jcf, n) != CONSTANT_NameAndType)
687             return i;
688           break;
689         case CONSTANT_Long:
690         case CONSTANT_Double:
691           i++;
692           break;
693         case CONSTANT_Float:
694         case CONSTANT_Integer:
695         case CONSTANT_Utf8:
696         case CONSTANT_Unicode:
697           break;
698         default:
699           return i;
700         }
701     }
702   return 0;
703 }
704
705 void
706 format_uint (char *buffer, uint64 value, int base)
707 {
708 #define WRITE_BUF_SIZE (4 + sizeof(uint64) * 8)
709   char buf[WRITE_BUF_SIZE];
710   register char *buf_ptr = buf+WRITE_BUF_SIZE; /* End of buf. */
711   int chars_written;
712   int i;
713
714   /* Now do the actual conversion, placing the result at the *end* of buf. */
715   /* Note this code does not pretend to be optimized. */
716   do {
717     int digit = value % base;
718     static const char digit_chars[] = "0123456789abcdefghijklmnopqrstuvwxyz";
719     *--buf_ptr = digit_chars[digit];
720     value /= base;
721   } while (value != 0);
722
723   chars_written = buf+WRITE_BUF_SIZE - buf_ptr;
724   for (i = 0; i < chars_written; i++)
725     buffer[i] = *buf_ptr++;
726   buffer[i] = 0;
727 }
728
729 void
730 format_int (char *buffer, jlong value, int base)
731 {
732   uint64 abs_value;
733   if (value < 0)
734     {
735       abs_value = -(uint64)value;
736       *buffer++ = '-';
737     }
738   else
739     abs_value = (uint64) value;
740   format_uint (buffer, abs_value, base);
741 }