* COUNT: Removed.
[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 R. Grafl, A. Krall, C. Kruegel, C. Oates,
4    R. Obermaisser, M. Platter, M. Probst, S. Ring, E. Steiner,
5    C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, J. Wenninger,
6    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., 59 Temple Place - Suite 330, Boston, MA
23    02111-1307, USA.
24
25    Contact: cacao@complang.tuwien.ac.at
26
27    Authors: Christian Thalinger
28
29    Changes:
30
31    $Id: suck.c 3959 2005-12-20 23:25:07Z twisti $
32
33 */
34
35
36 #include "config.h"
37
38 #include <sys/stat.h>
39
40 #include "vm/types.h"
41
42 #include "mm/memory.h"
43 #include "toolbox/list.h"
44 #include "toolbox/logging.h"
45 #include "toolbox/util.h"
46 #include "vm/exceptions.h"
47 #include "vm/loader.h"
48 #include "vm/options.h"
49 #include "vm/stringlocal.h"
50 #include "vm/suck.h"
51 #include "vm/zip.h"
52
53
54 /* global variables ***********************************************************/
55
56 list *list_classpath_entries;
57
58
59 /* suck_init *******************************************************************
60
61    Initializes the suck subsystem like initializing the classpath
62    entries list.
63
64 *******************************************************************************/
65
66 bool suck_init(void)
67 {
68         list_classpath_entries = NEW(list);
69
70         list_init(list_classpath_entries, OFFSET(list_classpath_entry, linkage));
71
72         /* everything's ok */
73
74         return true;
75 }
76
77
78 /* suck_add ********************************************************************
79
80    Adds a classpath to the global classpath entries list.
81
82 *******************************************************************************/
83
84 void suck_add(char *classpath)
85 {
86         list_classpath_entry *lce;
87         char                 *start;
88         char                 *end;
89         char                 *filename;
90         s4                    filenamelen;
91         bool                  is_zip;
92         char                 *cwd;
93         s4                    cwdlen;
94         hashtable            *ht;
95
96         /* parse the classpath string */
97
98         for (start = classpath; (*start) != '\0'; ) {
99
100                 /* search for ':' delimiter to get the end of the current entry */
101                 for (end = start; ((*end) != '\0') && ((*end) != ':'); end++);
102
103                 if (start != end) {
104                         is_zip = false;
105                         filenamelen = end - start;
106
107                         if (filenamelen > 3) {
108                                 if (strncasecmp(end - 3, "zip", 3) == 0 ||
109                                         strncasecmp(end - 3, "jar", 3) == 0) {
110                                         is_zip = true;
111                                 }
112                         }
113
114                         /* save classpath entries as absolute pathnames */
115
116                         cwd = NULL;
117                         cwdlen = 0;
118
119                         if (*start != '/') {                      /* XXX fix me for win32 */
120                                 cwd = _Jv_getcwd();
121                                 cwdlen = strlen(cwd) + strlen("/");
122                         }
123
124                         /* allocate memory for filename and fill it */
125
126                         filename = MNEW(char, filenamelen + cwdlen + strlen("/") +
127                                                         strlen("0"));
128
129                         if (cwd) {
130                                 strcpy(filename, cwd);
131                                 strcat(filename, "/");
132                                 strncat(filename, start, filenamelen);
133
134                                 /* add cwd length to file length */
135                                 filenamelen += cwdlen;
136
137                         } else {
138                                 strncpy(filename, start, filenamelen);
139                                 filename[filenamelen] = '\0';
140                         }
141
142                         lce = NULL;
143
144                         if (is_zip) {
145 #if defined(ENABLE_ZLIB)
146                                 ht = zip_open(filename);
147
148                                 if (ht) {
149                                         lce = NEW(list_classpath_entry);
150
151                                         lce->type      = CLASSPATH_ARCHIVE;
152                                         lce->htclasses = ht;
153                                         lce->path      = filename;
154                                         lce->pathlen   = filenamelen;
155
156                                         /* SUN compatible -verbose:class output */
157
158                                         if (opt_verboseclass)
159                                                 printf("[Opened %s]\n", filename);
160                                 }
161
162 #else
163                                 throw_cacao_exception_exit(string_java_lang_InternalError,
164                                                                                    "zip/jar files not supported");
165 #endif
166                                 
167                         } else {
168                                 if (filename[filenamelen - 1] != '/') {/* XXX fixme for win32 */
169                                         filename[filenamelen] = '/';
170                                         filename[filenamelen + 1] = '\0';
171                                         filenamelen++;
172                                 }
173
174                                 lce = NEW(list_classpath_entry);
175
176                                 lce->type    = CLASSPATH_PATH;
177                                 lce->path    = filename;
178                                 lce->pathlen = filenamelen;
179                         }
180
181                         /* add current classpath entry */
182
183                         list_addlast(list_classpath_entries, lce);
184                 }
185
186                 /* goto next classpath entry, skip ':' delimiter */
187
188                 if ((*end) == ':') {
189                         start = end + 1;
190
191                 } else {
192                         start = end;
193                 }
194         }
195 }
196
197
198 /* suck_check_classbuffer_size *************************************************
199
200    Assert that at least <len> bytes are left to read <len> is limited
201    to the range of non-negative s4 values.
202
203 *******************************************************************************/
204
205 bool suck_check_classbuffer_size(classbuffer *cb, s4 len)
206 {
207 #ifdef ENABLE_VERIFIER
208         if (len < 0 || ((cb->data + cb->size) - cb->pos) < len) {
209                 *exceptionptr =
210                         new_classformaterror((cb)->class, "Truncated class file");
211
212                 return false;
213         }
214 #endif /* ENABLE_VERIFIER */
215
216         return true;
217 }
218
219
220 u1 suck_u1(classbuffer *cb)
221 {
222         u1 a;
223
224         a = SUCK_BE_U1(cb->pos);
225         cb->pos++;
226
227         return a;
228 }
229
230
231 u2 suck_u2(classbuffer *cb)
232 {
233         u2 a;
234
235         a = SUCK_BE_U2(cb->pos);
236         cb->pos += 2;
237
238         return a;
239 }
240
241
242 u4 suck_u4(classbuffer *cb)
243 {
244         u4 a;
245
246         a = SUCK_BE_U4(cb->pos);
247         cb->pos += 4;
248
249         return a;
250 }
251
252
253 u8 suck_u8(classbuffer *cb)
254 {
255 #if U8_AVAILABLE == 1
256         u8 a;
257
258         a = SUCK_BE_U8(cb->pos);
259         cb->pos += 8;
260
261         return a;
262 #else
263         u8 v;
264
265         v.high = suck_u4(cb);
266         v.low = suck_u4(cb);
267
268         return v;
269 #endif
270 }
271
272
273 float suck_float(classbuffer *cb)
274 {
275         float f;
276
277 #if WORDS_BIGENDIAN == 0
278         u1 buffer[4];
279         u2 i;
280
281         for (i = 0; i < 4; i++)
282                 buffer[3 - i] = suck_u1(cb);
283
284         MCOPY((u1 *) (&f), buffer, u1, 4);
285 #else
286         suck_nbytes((u1*) (&f), cb, 4);
287 #endif
288
289         if (sizeof(float) != 4) {
290                 *exceptionptr = new_internalerror("Incompatible float-format");
291
292                 /* XXX should we exit in such a case? */
293                 throw_exception_exit();
294         }
295         
296         return f;
297 }
298
299
300 double suck_double(classbuffer *cb)
301 {
302         double d;
303
304 #if WORDS_BIGENDIAN == 0
305         u1 buffer[8];
306         u2 i;   
307
308 #if defined(__ARM__) && defined(__ARMEL__) && !defined(__VFP_FP__)
309         /*
310          * On little endian ARM processors when using FPA, word order
311          * of doubles is still big endian. So take that into account
312          * here. When using VFP, word order of doubles follows byte
313          * order. (michi 2005/07/24)
314          */
315         for (i = 0; i < 4; i++)
316                 buffer[3 - i] = suck_u1(cb);
317         for (i = 0; i < 4; i++)
318                 buffer[7 - i] = suck_u1(cb);
319 #else
320         for (i = 0; i < 8; i++)
321                 buffer[7 - i] = suck_u1(cb);
322 #endif /* defined(__ARM__) && ... */
323
324         MCOPY((u1 *) (&d), buffer, u1, 8);
325 #else 
326         suck_nbytes((u1*) (&d), cb, 8);
327 #endif
328
329         if (sizeof(double) != 8) {
330                 *exceptionptr = new_internalerror("Incompatible double-format");
331
332                 /* XXX should we exit in such a case? */
333                 throw_exception_exit();
334         }
335         
336         return d;
337 }
338
339
340 /* suck_nbytes *****************************************************************
341
342    Transfer block of classfile data into a buffer.
343
344 *******************************************************************************/
345
346 void suck_nbytes(u1 *buffer, classbuffer *cb, s4 len)
347 {
348         MCOPY(buffer, cb->pos, u1, len);
349         cb->pos += len;
350 }
351
352
353 /* suck_skip_nbytes ************************************************************
354
355    Skip block of classfile data.
356
357 *******************************************************************************/
358
359 void suck_skip_nbytes(classbuffer *cb, s4 len)
360 {
361         cb->pos += len;
362 }
363
364
365 /* suck_start ******************************************************************
366
367    Returns true if classbuffer is already loaded or a file for the
368    specified class has succussfully been read in. All directories of
369    the searchpath are used to find the classfile (<classname>.class).
370    Returns NULL if no classfile is found and writes an error message.
371         
372 *******************************************************************************/
373
374 classbuffer *suck_start(classinfo *c)
375 {
376         list_classpath_entry *lce;
377         char                 *filename;
378         s4                    filenamelen;
379         char                 *path;
380         FILE                 *classfile;
381         s4                    len;
382         struct stat           buffer;
383         classbuffer          *cb;
384
385         /* initialize return value */
386
387         cb = NULL;
388
389         /* get the classname as char string (do it here for the warning at
390        the and of the function) */
391
392         filenamelen = utf_strlen(c->name) + strlen(".class") + strlen("0");
393         filename = MNEW(char, filenamelen);
394
395         utf_sprint(filename, c->name);
396         strcat(filename, ".class");
397
398         /* walk through all classpath entries */
399
400         for (lce = list_first(list_classpath_entries); lce != NULL && cb == NULL;
401                  lce = list_next(list_classpath_entries, lce)) {
402 #if defined(ENABLE_ZLIB)
403                 if (lce->type == CLASSPATH_ARCHIVE) {
404
405 #if defined(USE_THREADS)
406                         /* enter a monitor on zip/jar archives */
407
408                         builtin_monitorenter((java_objectheader *) lce);
409 #endif
410
411                         /* try to get the file in current archive */
412
413                         cb = zip_get(lce, c);
414
415 #if defined(USE_THREADS)
416                         /* leave the monitor */
417
418                         builtin_monitorexit((java_objectheader *) lce);
419 #endif
420
421                 } else {
422 #endif /* defined(ENABLE_ZLIB) */
423                         path = MNEW(char, lce->pathlen + filenamelen);
424                         strcpy(path, lce->path);
425                         strcat(path, filename);
426
427                         classfile = fopen(path, "r");
428
429                         if (classfile) {                                   /* file exists */
430                                 if (!stat(path, &buffer)) {            /* read classfile data */
431                                         cb = NEW(classbuffer);
432                                         cb->class = c;
433                                         cb->size  = buffer.st_size;
434                                         cb->data  = MNEW(u1, cb->size);
435                                         cb->pos   = cb->data;
436                                         cb->path  = lce->path;
437
438                                         /* read class data */
439                                         len = fread(cb->data, 1, cb->size, classfile);
440
441                                         if (len != buffer.st_size) {
442                                                 suck_stop(cb);
443 /*                                              if (ferror(classfile)) { */
444 /*                                              } */
445                                         }
446                                 }
447                         }
448
449                         MFREE(path, char, lce->pathlen + filenamelen);
450 #if defined(ENABLE_ZLIB)
451                 }
452 #endif
453         }
454
455         if (opt_verbose)
456                 if (cb == NULL)
457                         dolog("Warning: Can not open class file '%s'", filename);
458
459         MFREE(filename, char, filenamelen);
460
461         return cb;
462 }
463
464
465 /* suck_stop *******************************************************************
466
467    Frees memory for buffer with classfile data.
468
469    CAUTION: This function may only be called if buffer has been
470    allocated by suck_start with reading a file.
471         
472 *******************************************************************************/
473
474 void suck_stop(classbuffer *cb)
475 {
476         /* free memory */
477
478         MFREE(cb->data, u1, cb->size);
479         FREE(cb, classbuffer);
480 }
481
482
483 /*
484  * These are local overrides for various environment variables in Emacs.
485  * Please do not remove this and leave it at the end of the file, where
486  * Emacs will automagically detect them.
487  * ---------------------------------------------------------------------
488  * Local variables:
489  * mode: c
490  * indent-tabs-mode: t
491  * c-basic-offset: 4
492  * tab-width: 4
493  * End:
494  */