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