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