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