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