* src/vm/jit/powerpc/emit.c (emit_replacement_stubs): Do not
[cacao.git] / src / vm / suck.c
1 /* src/vm/suck.c - functions to read LE ordered types from a buffer
2
3    Copyright (C) 1996-2005, 2006 R. Grafl, A. Krall, C. Kruegel,
4    C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring,
5    E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich,
6    J. Wenninger, Institut f. Computersprachen - TU Wien
7
8    This file is part of CACAO.
9
10    This program is free software; you can redistribute it and/or
11    modify it under the terms of the GNU General Public License as
12    published by the Free Software Foundation; either version 2, or (at
13    your option) any later version.
14
15    This program is distributed in the hope that it will be useful, but
16    WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23    02110-1301, USA.
24
25    Contact: cacao@cacaojvm.org
26
27    Authors: Christian Thalinger
28
29    Changes:
30
31    $Id: suck.c 6037 2006-11-22 11:05:06Z twisti $
32
33 */
34
35
36 #include "config.h"
37
38 #include <dirent.h>
39 #include <sys/stat.h>
40 #include <stdlib.h>
41
42 #include "vm/types.h"
43
44 #include "mm/memory.h"
45
46 #if defined(ENABLE_THREADS)
47 # include "threads/native/lock.h"
48 #else
49 # include "threads/none/lock.h"
50 #endif
51
52 #include "toolbox/list.h"
53 #include "toolbox/logging.h"
54 #include "toolbox/util.h"
55 #include "vm/exceptions.h"
56 #include "vm/loader.h"
57 #include "vm/options.h"
58 #include "vm/properties.h"
59 #include "vm/stringlocal.h"
60 #include "vm/suck.h"
61 #include "vm/vm.h"
62 #include "vm/zip.h"
63
64
65 /* global variables ***********************************************************/
66
67 list *list_classpath_entries;
68
69
70 /* suck_init *******************************************************************
71
72    Initializes the suck subsystem like initializing the classpath
73    entries list.
74
75 *******************************************************************************/
76
77 bool suck_init(void)
78 {
79         list_classpath_entries = list_create(OFFSET(list_classpath_entry, linkage));
80
81         /* everything's ok */
82
83         return true;
84 }
85
86
87 /* scandir_filter **************************************************************
88
89    Filters for zip/jar files.
90
91 *******************************************************************************/
92
93 #if defined(__LINUX__)
94 static int scandir_filter(const struct dirent *a)
95 #else
96 static int scandir_filter(struct dirent *a)
97 #endif
98 {
99         s4 namlen;
100
101 #if defined(_DIRENT_HAVE_D_NAMLEN)
102         namlen = a->d_namlen;
103 #else
104         namlen = strlen(a->d_name);
105 #endif
106
107         if ((strncasecmp(a->d_name + namlen - 4, ".zip", 4) == 0) ||
108                 (strncasecmp(a->d_name + namlen - 4, ".jar", 4) == 0))
109                 return 1;
110
111         return 0;
112 }
113
114
115 /* suck_add ********************************************************************
116
117    Adds a classpath to the global classpath entries list.
118
119 *******************************************************************************/
120
121 void suck_add(char *classpath)
122 {
123         list_classpath_entry *lce;
124         char                 *start;
125         char                 *end;
126         char                 *filename;
127         s4                    filenamelen;
128         bool                  is_zip;
129         char                 *cwd;
130         s4                    cwdlen;
131 #if defined(ENABLE_ZLIB)
132         hashtable            *ht;
133 #endif
134
135         /* parse the classpath string */
136
137         for (start = classpath; (*start) != '\0'; ) {
138
139                 /* search for ':' delimiter to get the end of the current entry */
140                 for (end = start; ((*end) != '\0') && ((*end) != ':'); end++);
141
142                 if (start != end) {
143                         is_zip = false;
144                         filenamelen = end - start;
145
146                         if (filenamelen > 4) {
147                                 if ((strncasecmp(end - 4, ".zip", 4) == 0) ||
148                                         (strncasecmp(end - 4, ".jar", 4) == 0)) {
149                                         is_zip = true;
150                                 }
151                         }
152
153                         /* save classpath entries as absolute pathnames */
154
155                         cwd = NULL;
156                         cwdlen = 0;
157
158                         if (*start != '/') {                      /* XXX fix me for win32 */
159                                 cwd = _Jv_getcwd();
160                                 cwdlen = strlen(cwd) + strlen("/");
161                         }
162
163                         /* allocate memory for filename and fill it */
164
165                         filename = MNEW(char, filenamelen + cwdlen + strlen("/") +
166                                                         strlen("0"));
167
168                         if (cwd) {
169                                 strcpy(filename, cwd);
170                                 strcat(filename, "/");
171                                 strncat(filename, start, filenamelen);
172
173                                 /* add cwd length to file length */
174                                 filenamelen += cwdlen;
175
176                         } else {
177                                 strncpy(filename, start, filenamelen);
178                                 filename[filenamelen] = '\0';
179                         }
180
181                         lce = NULL;
182
183                         if (is_zip) {
184 #if defined(ENABLE_ZLIB)
185                                 ht = zip_open(filename);
186
187                                 if (ht) {
188                                         lce = NEW(list_classpath_entry);
189
190                                         lce->type      = CLASSPATH_ARCHIVE;
191                                         lce->htclasses = ht;
192                                         lce->path      = filename;
193                                         lce->pathlen   = filenamelen;
194
195                                         /* SUN compatible -verbose:class output */
196
197                                         if (opt_verboseclass)
198                                                 printf("[Opened %s]\n", filename);
199                                 }
200
201 #else
202                                 throw_cacao_exception_exit(string_java_lang_InternalError,
203                                                                                    "zip/jar files not supported");
204 #endif
205                                 
206                         } else {
207                                 if (filename[filenamelen - 1] != '/') {/* XXX fixme for win32 */
208                                         filename[filenamelen] = '/';
209                                         filename[filenamelen + 1] = '\0';
210                                         filenamelen++;
211                                 }
212
213                                 lce = NEW(list_classpath_entry);
214
215                                 lce->type    = CLASSPATH_PATH;
216                                 lce->path    = filename;
217                                 lce->pathlen = filenamelen;
218                         }
219
220                         /* add current classpath entry, if no error */
221
222                         if (lce)
223                                 list_add_last(list_classpath_entries, lce);
224                 }
225
226                 /* goto next classpath entry, skip ':' delimiter */
227
228                 if ((*end) == ':')
229                         start = end + 1;
230                 else
231                         start = end;
232         }
233 }
234
235
236 /* suck_add_from_property ******************************************************
237
238    Adds a classpath form a property entry to the global classpath
239    entries list.
240
241 *******************************************************************************/
242
243 void suck_add_from_property(char *key)
244 {
245         char           *value;
246         char           *start;
247         char           *end;
248         char           *path;
249         s4              pathlen;
250         struct dirent **namelist;
251         s4              n;
252         s4              i;
253         s4              namlen;
254         char           *tmpbootclasspath;
255
256         /* get the property value */
257
258         value = properties_get(key);
259
260         if (value == NULL)
261                 return;
262
263         /* get the directory entries of the property */
264
265         for (start = value; (*start) != '\0'; ) {
266
267                 /* search for ':' delimiter to get the end of the current entry */
268
269                 for (end = start; ((*end) != '\0') && ((*end) != ':'); end++);
270
271                 /* found an entry */
272
273                 if (start != end) {
274                         /* allocate memory for the path entry */
275
276                         pathlen = end - start;
277                         path = MNEW(char, pathlen + strlen("0"));
278
279                         /* copy and terminate the string */
280
281                         strncpy(path, start, pathlen);
282                         path[pathlen] = '\0';
283
284                         /* Reset namelist to NULL for the freeing in an error case
285                            (see below). */
286
287                         namelist = NULL;
288
289                         /* scan the directory found for zip/jar files */
290
291                         n = scandir(path, &namelist, scandir_filter, alphasort);
292
293                         /* On error, just continue, this should be ok. */
294
295                         if (n > 0) {
296                                 for (i = 0; i < n; i++) {
297 #if defined(_DIRENT_HAVE_D_NAMLEN)
298                                         namlen = namelist[i]->d_namlen;
299 #else
300                                         namlen = strlen(namelist[i]->d_name);
301 #endif
302
303                                         /* reallocate memory for bootclasspath */
304
305                                         tmpbootclasspath = MNEW(char,
306                                                                                         pathlen + strlen("/") + namlen +
307                                                                                         strlen(":") +
308                                                                                         strlen(_Jv_bootclasspath) +
309                                                                                         strlen("0"));
310
311                                         /* prepend the file found to bootclasspath */
312
313                                         strcpy(tmpbootclasspath, path);
314                                         strcat(tmpbootclasspath, "/");
315                                         strcat(tmpbootclasspath, namelist[i]->d_name);
316                                         strcat(tmpbootclasspath, ":");
317
318                                         strcat(tmpbootclasspath, _Jv_bootclasspath);
319
320                                         /* free old bootclasspath memory */
321
322                                         MFREE(_Jv_bootclasspath, u1, strlen(_Jv_bootclasspath));
323
324                                         /* and set the new bootclasspath */
325
326                                         _Jv_bootclasspath = tmpbootclasspath;
327
328                                         /* free the memory allocated by scandir */
329                                         /* (We use `free` as the memory came from the C library.) */
330
331                                         free(namelist[i]);
332                                 }
333                         }
334
335                         /* On some systems (like Linux) when n == 0, then namelist
336                            returned from scnadir is NULL, thus we don't have to
337                            free it.
338                            (Use `free` as the memory came from the C library.) */
339
340                         if (namelist != NULL)
341                                 free(namelist);
342
343                         MFREE(path, char, pathlen + strlen("0"));
344                 }
345
346                 /* goto next entry, skip ':' delimiter */
347
348                 if ((*end) == ':')
349                         start = end + 1;
350                 else
351                         start = end;
352         }
353 }
354
355
356 /* suck_check_classbuffer_size *************************************************
357
358    Assert that at least <len> bytes are left to read <len> is limited
359    to the range of non-negative s4 values.
360
361 *******************************************************************************/
362
363 bool suck_check_classbuffer_size(classbuffer *cb, s4 len)
364 {
365 #ifdef ENABLE_VERIFIER
366         if (len < 0 || ((cb->data + cb->size) - cb->pos) < len) {
367                 *exceptionptr =
368                         new_classformaterror((cb)->class, "Truncated class file");
369
370                 return false;
371         }
372 #endif /* ENABLE_VERIFIER */
373
374         return true;
375 }
376
377
378 u1 suck_u1(classbuffer *cb)
379 {
380         u1 a;
381
382         a = SUCK_BE_U1(cb->pos);
383         cb->pos++;
384
385         return a;
386 }
387
388
389 u2 suck_u2(classbuffer *cb)
390 {
391         u2 a;
392
393         a = SUCK_BE_U2(cb->pos);
394         cb->pos += 2;
395
396         return a;
397 }
398
399
400 u4 suck_u4(classbuffer *cb)
401 {
402         u4 a;
403
404         a = SUCK_BE_U4(cb->pos);
405         cb->pos += 4;
406
407         return a;
408 }
409
410
411 u8 suck_u8(classbuffer *cb)
412 {
413 #if U8_AVAILABLE == 1
414         u8 a;
415
416         a = SUCK_BE_U8(cb->pos);
417         cb->pos += 8;
418
419         return a;
420 #else
421         u8 v;
422
423         v.high = suck_u4(cb);
424         v.low = suck_u4(cb);
425
426         return v;
427 #endif
428 }
429
430
431 float suck_float(classbuffer *cb)
432 {
433         float f;
434
435 #if WORDS_BIGENDIAN == 0
436         u1 buffer[4];
437         u2 i;
438
439         for (i = 0; i < 4; i++)
440                 buffer[3 - i] = suck_u1(cb);
441
442         MCOPY((u1 *) (&f), buffer, u1, 4);
443 #else
444         suck_nbytes((u1*) (&f), cb, 4);
445 #endif
446
447         if (sizeof(float) != 4) {
448                 *exceptionptr = new_internalerror("Incompatible float-format");
449
450                 /* XXX should we exit in such a case? */
451                 throw_exception_exit();
452         }
453         
454         return f;
455 }
456
457
458 double suck_double(classbuffer *cb)
459 {
460         double d;
461
462 #if WORDS_BIGENDIAN == 0
463         u1 buffer[8];
464         u2 i;   
465
466 #if defined(__ARM__) && defined(__ARMEL__) && !defined(__VFP_FP__)
467         /*
468          * On little endian ARM processors when using FPA, word order
469          * of doubles is still big endian. So take that into account
470          * here. When using VFP, word order of doubles follows byte
471          * order. (michi 2005/07/24)
472          */
473         for (i = 0; i < 4; i++)
474                 buffer[3 - i] = suck_u1(cb);
475         for (i = 0; i < 4; i++)
476                 buffer[7 - i] = suck_u1(cb);
477 #else
478         for (i = 0; i < 8; i++)
479                 buffer[7 - i] = suck_u1(cb);
480 #endif /* defined(__ARM__) && ... */
481
482         MCOPY((u1 *) (&d), buffer, u1, 8);
483 #else 
484         suck_nbytes((u1*) (&d), cb, 8);
485 #endif
486
487         if (sizeof(double) != 8) {
488                 *exceptionptr = new_internalerror("Incompatible double-format");
489
490                 /* XXX should we exit in such a case? */
491                 throw_exception_exit();
492         }
493         
494         return d;
495 }
496
497
498 /* suck_nbytes *****************************************************************
499
500    Transfer block of classfile data into a buffer.
501
502 *******************************************************************************/
503
504 void suck_nbytes(u1 *buffer, classbuffer *cb, s4 len)
505 {
506         MCOPY(buffer, cb->pos, u1, len);
507         cb->pos += len;
508 }
509
510
511 /* suck_skip_nbytes ************************************************************
512
513    Skip block of classfile data.
514
515 *******************************************************************************/
516
517 void suck_skip_nbytes(classbuffer *cb, s4 len)
518 {
519         cb->pos += len;
520 }
521
522
523 /* suck_start ******************************************************************
524
525    Returns true if classbuffer is already loaded or a file for the
526    specified class has succussfully been read in. All directories of
527    the searchpath are used to find the classfile (<classname>.class).
528    Returns NULL if no classfile is found and writes an error message.
529         
530 *******************************************************************************/
531
532 classbuffer *suck_start(classinfo *c)
533 {
534         list_classpath_entry *lce;
535         char                 *filename;
536         s4                    filenamelen;
537         char                 *path;
538         FILE                 *classfile;
539         s4                    len;
540         struct stat           buffer;
541         classbuffer          *cb;
542
543         /* initialize return value */
544
545         cb = NULL;
546
547         /* get the classname as char string (do it here for the warning at
548        the end of the function) */
549
550         filenamelen = utf_bytes(c->name) + strlen(".class") + strlen("0");
551         filename = MNEW(char, filenamelen);
552
553         utf_copy(filename, c->name);
554         strcat(filename, ".class");
555
556         /* walk through all classpath entries */
557
558         for (lce = list_first(list_classpath_entries); lce != NULL && cb == NULL;
559                  lce = list_next(list_classpath_entries, lce)) {
560 #if defined(ENABLE_ZLIB)
561                 if (lce->type == CLASSPATH_ARCHIVE) {
562
563                         /* enter a monitor on zip/jar archives */
564
565                         LOCK_MONITOR_ENTER(lce);
566
567                         /* try to get the file in current archive */
568
569                         cb = zip_get(lce, c);
570
571                         /* leave the monitor */
572
573                         LOCK_MONITOR_EXIT(lce);
574
575                 } else {
576 #endif /* defined(ENABLE_ZLIB) */
577                         path = MNEW(char, lce->pathlen + filenamelen);
578                         strcpy(path, lce->path);
579                         strcat(path, filename);
580
581                         classfile = fopen(path, "r");
582
583                         if (classfile) {                                   /* file exists */
584                                 if (!stat(path, &buffer)) {            /* read classfile data */
585                                         cb = NEW(classbuffer);
586                                         cb->class = c;
587                                         cb->size  = buffer.st_size;
588                                         cb->data  = MNEW(u1, cb->size);
589                                         cb->pos   = cb->data;
590                                         cb->path  = lce->path;
591
592                                         /* read class data */
593
594                                         len = fread(cb->data, 1, cb->size, classfile);
595
596                                         if (len != buffer.st_size) {
597                                                 suck_stop(cb);
598 /*                                              if (ferror(classfile)) { */
599 /*                                              } */
600                                         }
601
602                                         /* close the class file */
603
604                                         fclose(classfile);
605                                 }
606                         }
607
608                         MFREE(path, char, lce->pathlen + filenamelen);
609 #if defined(ENABLE_ZLIB)
610                 }
611 #endif
612         }
613
614         if (opt_verbose)
615                 if (cb == NULL)
616                         dolog("Warning: Can not open class file '%s'", filename);
617
618         MFREE(filename, char, filenamelen);
619
620         return cb;
621 }
622
623
624 /* suck_stop *******************************************************************
625
626    Frees memory for buffer with classfile data.
627
628    CAUTION: This function may only be called if buffer has been
629    allocated by suck_start with reading a file.
630         
631 *******************************************************************************/
632
633 void suck_stop(classbuffer *cb)
634 {
635         /* free memory */
636
637         MFREE(cb->data, u1, cb->size);
638         FREE(cb, classbuffer);
639 }
640
641
642 /*
643  * These are local overrides for various environment variables in Emacs.
644  * Please do not remove this and leave it at the end of the file, where
645  * Emacs will automagically detect them.
646  * ---------------------------------------------------------------------
647  * Local variables:
648  * mode: c
649  * indent-tabs-mode: t
650  * c-basic-offset: 4
651  * tab-width: 4
652  * End:
653  */