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