CACAO
suck.cpp
Go to the documentation of this file.
1 /* src/vm/suck.cpp - functions to read LE ordered types from a buffer
2 
3  Copyright (C) 1996-2012
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 <cassert>
29 #include <cstdlib>
30 
31 #include "vm/types.hpp"
32 
33 #include "mm/memory.hpp"
34 
35 #include "threads/mutex.hpp"
36 
37 #include "toolbox/buffer.hpp"
38 #include "toolbox/endianess.hpp"
39 #include "toolbox/hashtable.hpp"
40 #include "toolbox/list.hpp"
41 #include "toolbox/logging.hpp"
42 
43 #include "vm/exceptions.hpp"
44 #include "vm/loader.hpp"
45 #include "vm/options.hpp"
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 using namespace cacao;
53 
54 
55 /* scandir_filter **************************************************************
56 
57  Filters for zip/jar files.
58 
59 *******************************************************************************/
60 
61 static int scandir_filter(const struct dirent *a)
62 {
63  s4 namlen;
64 
65 #if defined(_DIRENT_HAVE_D_NAMLEN)
66  namlen = a->d_namlen;
67 #else
68  namlen = strlen(a->d_name);
69 #endif
70 
71  if ((strncasecmp(a->d_name + namlen - 4, ".zip", 4) == 0) ||
72  (strncasecmp(a->d_name + namlen - 4, ".jar", 4) == 0))
73  return 1;
74 
75  return 0;
76 }
77 
78 
79 /**
80  * Adds a classpath to the global classpath entries list.
81  */
82 void SuckClasspath::add(char *classpath)
83 {
84  char *start;
85  char *end;
86  char *cwd;
87  s4 cwdlen;
88 
89  /* parse the classpath string */
90 
91  for (start = classpath; (*start) != '\0'; ) {
92 
93  /* search for ':' delimiter to get the end of the current entry */
94  for (end = start; ((*end) != '\0') && ((*end) != ':'); end++);
95 
96  if (start != end) {
97  bool is_zip = false;
98  size_t filenamelen = end - start;
99 
100  if (filenamelen > 4) {
101  if ((strncasecmp(end - 4, ".zip", 4) == 0) ||
102  (strncasecmp(end - 4, ".jar", 4) == 0)) {
103  is_zip = true;
104  }
105  }
106 
107  /* save classpath entries as absolute pathnames */
108 
109  cwd = NULL;
110  cwdlen = 0;
111 
112  if (*start != '/') { /* XXX fix me for win32 */
113  cwd = os::getcwd();
114  cwdlen = strlen(cwd) + strlen("/");
115  }
116 
117  /* allocate memory for filename and fill it */
118 
119  char *filename = MNEW(char, filenamelen + cwdlen + strlen("/") + strlen("0"));
120 
121  if (cwd) {
122  strcpy(filename, cwd);
123  strcat(filename, "/");
124  strncat(filename, start, filenamelen);
125 
126  /* add cwd length to file length */
127  filenamelen += cwdlen;
128 
129  } else {
130  strncpy(filename, start, filenamelen);
131  filename[filenamelen] = '\0';
132  }
133 
134  if (is_zip) {
135 #if defined(ENABLE_ZLIB)
136  if (ZipFile *zip = ZipFile::open(filename)) {
138 
139  lce->type = CLASSPATH_ARCHIVE;
140  lce->zip = zip;
141  lce->path = filename;
142  lce->pathlen = filenamelen;
143 
144  /* SUN compatible -verbose:class output */
145 
146  if (opt_verboseclass)
147  printf("[Opened %s]\n", filename);
148 
149  push_back(lce);
150  }
151 #else
152  os::abort("suck_add: zip/jar files not supported");
153 #endif
154  }
155  else {
156  if (filename[filenamelen - 1] != '/') {/* XXX fixme for win32 */
157  filename[filenamelen] = '/';
158  filename[filenamelen + 1] = '\0';
159  filenamelen++;
160  }
161 
163 
164  lce->type = CLASSPATH_PATH;
165  lce->path = filename;
166  lce->pathlen = filenamelen;
167 
168  push_back(lce);
169  }
170  }
171 
172  /* goto next classpath entry, skip ':' delimiter */
173 
174  if ((*end) == ':')
175  start = end + 1;
176  else
177  start = end;
178  }
179 }
180 
181 
182 /**
183  * Adds a classpath form a property entry to the global classpath
184  * entries list.
185  */
186 void SuckClasspath::add_from_property(const char *key)
187 {
188  const char *value;
189  const char *start;
190  const char *end;
191  s4 pathlen;
192  struct dirent **namelist;
193  s4 n;
194  s4 i;
195  s4 namlen;
196  char *p;
197  char* boot_class_path = NULL;
198 
199  // Get the property value.
200  Properties& properties = VM::get_current()->get_properties();
201  value = properties.get(key);
202 
203  if (value == NULL)
204  return;
205 
206  /* get the directory entries of the property */
207 
208  for (start = value; (*start) != '\0'; ) {
209 
210  /* search for ':' delimiter to get the end of the current entry */
211 
212  for (end = start; ((*end) != '\0') && ((*end) != ':'); end++);
213 
214  /* found an entry */
215 
216  if (start != end) {
217  /* allocate memory for the path entry */
218 
219  pathlen = end - start;
220  char* path = MNEW(char, pathlen + strlen("0"));
221 
222  /* copy and terminate the string */
223 
224  strncpy(path, start, pathlen);
225  path[pathlen] = '\0';
226 
227  /* Reset namelist to NULL for the freeing in an error case
228  (see below). */
229 
230  namelist = NULL;
231 
232  /* scan the directory found for zip/jar files */
233 
234  n = os::scandir((const char*) path, &namelist, &scandir_filter, (int (*)(const void*, const void*)) &alphasort);
235 
236  /* On error, just continue, this should be ok. */
237 
238  if (n > 0) {
239  for (i = 0; i < n; i++) {
240 #if defined(_DIRENT_HAVE_D_NAMLEN)
241  namlen = namelist[i]->d_namlen;
242 #else
243  namlen = strlen(namelist[i]->d_name);
244 #endif
245 
246  if (boot_class_path == NULL) {
247  /* Allocate memory for bootclasspath. */
248  p = MNEW(char,
249  pathlen + strlen("/") + namlen +
250  strlen("0"));
251 
252  strcpy(p, path);
253  strcat(p, "/");
254  strcat(p, namelist[i]->d_name);
255 
256  } else {
257  /* Allocate memory for bootclasspath. */
258  p = MNEW(char,
259  pathlen + strlen("/") + namlen +
260  strlen(":") +
261  strlen(boot_class_path) +
262  strlen("0"));
263 
264  /* Append the file found to the bootclasspath. */
265 
266  strcpy(p, boot_class_path);
267  strcat(p, ":");
268  strcat(p, path);
269  strcat(p, "/");
270  strcat(p, namelist[i]->d_name);
271 
272  MFREE(boot_class_path, char, strlen(boot_class_path));
273  }
274 
275  boot_class_path = p;
276 
277  /* free the memory allocated by scandir */
278  /* (We use `free` as the memory came from the C library.) */
279 
280  free(namelist[i]);
281  }
282  }
283 
284  /* On some systems (like Linux) when n == 0, then namelist
285  returned from scnadir is NULL, thus we don't have to
286  free it.
287  (Use `free` as the memory came from the C library.) */
288 
289  if (namelist != NULL)
290  free(namelist);
291 
292  MFREE(path, char, pathlen + strlen("0"));
293  }
294 
295  /* goto next entry, skip ':' delimiter */
296 
297  if ((*end) == ':')
298  start = end + 1;
299  else
300  start = end;
301  }
302 
303  if (boot_class_path != NULL) {
304  // only update if something has changed
305 
306  // FIXME Make boot_class_path const char*.
307  char* old_boot_class_path = (char*) properties.get("sun.boot.class.path");
308 
309  p = MNEW(char,
310  strlen(boot_class_path) +
311  strlen(":") +
312  strlen(old_boot_class_path) +
313  strlen("0"));
314 
315  /* Prepend the file found to the bootclasspath. */
316 
317  strcpy(p, boot_class_path);
318  strcat(p, ":");
319  strcat(p, old_boot_class_path);
320 
321  MFREE(boot_class_path, char, strlen(boot_class_path));
322  MFREE(old_boot_class_path, char, strlen(old_boot_class_path));
323 
324  /* Prepend the file found to the bootclasspath. */
325  properties.put("sun.boot.class.path", p);
326  properties.put("java.boot.class.path", p);
327  }
328 
329 }
330 
331 
332 inline void ClassBuffer::init(classinfo *clazz, uint8_t *data, size_t sz, const char *path) {
333  this->clazz = clazz;
334  this->data = data;
335  this->pos = data;
336  this->end = data + sz;
337  this->path = path;
338 }
339 
340 ClassBuffer::ClassBuffer(classinfo *clazz, uint8_t *data, size_t sz, const char *path) {
341  init(clazz, data, sz, path);
342 }
343 
345 
346 /***
347  * Loads class file corresponding to given classinfo into new ClassBuffer.
348  * All directories of the searchpath are used to find the classfile (<classname>.class).
349  * Use operator bool to check if initialization was successfull.
350  */
352  init(NULL, NULL, 0, NULL);
353 
354  // get the classname as char string
355  // (do it here for the warning at the end of the function)
356 
357  size_t filenamelen = c->name.size() + strlen(".class") + strlen("0");
358 
359  Buffer<> filename(filenamelen);
360  Buffer<> path;
361 
362  filename.write(c->name)
363  .write(".class");
364 
365  // Get current list of classpath entries.
366  SuckClasspath& suckclasspath = VM::get_current()->get_suckclasspath();
367 
368  // walk through all classpath entries
369 
370  for (SuckClasspath::iterator it = suckclasspath.begin(); it != suckclasspath.end(); it++) {
371  list_classpath_entry *lce = *it;
372 
373 #if defined(ENABLE_ZLIB)
374  if (lce->type == CLASSPATH_ARCHIVE) {
375 
376  // enter a monitor on zip/jar archives
377  MutexLocker lock(*lce->mutex);
378 
379  // try to get the file in current archive
380  if (ZipFile::EntryRef zip = lce->zip->find(c->name)) {
381  // found class, fill in classbuffer
382  size_t size = zip->uncompressedsize;
383  uint8_t *data = MNEW(uint8_t, size);
384 
385  zip->get(data);
386 
387  init(c, data, size, lce->path);
388  return;
389  }
390  } else {
391 #endif /* defined(ENABLE_ZLIB) */
392  path.reset();
393 
394  path.write(lce->path)
395  .write(filename);
396 
397  if (FILE *classfile = os::fopen(path.c_str(), "r")) {
398  struct stat stat_buffer;
399 
400  if (os::stat(path.c_str(), &stat_buffer) == -1)
401  continue;
402 
403  size_t size = stat_buffer.st_size;
404  uint8_t *data = MNEW(u1, size);
405 
406  // read class data
407  size_t bytes_read = os::fread(data, 1, size, classfile);
408  os::fclose(classfile);
409 
410  if (bytes_read != size) {
411  free();
412  return;
413  }
414 
415  init(c, data, size, lce->path);
416  return;
417  }
418 #if defined(ENABLE_ZLIB)
419  }
420 #endif
421  }
422 
423  // if we get here, we could not find the file
424  if (opt_verbose)
425  dolog("Warning: Can not open class file '%s'", filename.c_str());
426 }
427 
428 
429 /* suck_stop *******************************************************************
430 
431  Frees memory for buffer with classfile data.
432 
433  CAUTION: This function may only be called if buffer has been
434  allocated by suck_start with reading a file.
435 
436 *******************************************************************************/
437 
439  // free memory
440 
441  MFREE(data, u1, end - data);
442 }
443 
444 
445 /*
446  * These are local overrides for various environment variables in Emacs.
447  * Please do not remove this and leave it at the end of the file, where
448  * Emacs will automagically detect them.
449  * ---------------------------------------------------------------------
450  * Local variables:
451  * mode: c++
452  * indent-tabs-mode: t
453  * c-basic-offset: 4
454  * tab-width: 4
455  * End:
456  * vim:noexpandtab:sw=4:ts=4:
457  */
#define dolog
Definition: logging.hpp:171
static FILE * fopen(const char *path, const char *mode)
Definition: os.hpp:337
void init(classinfo *, uint8_t *, size_t, const char *)
Definition: suck.cpp:332
static size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
Definition: os.hpp:361
ZipFile * zip
Definition: suck.hpp:55
ClassBuffer(Utf8String classname)
Locate and load class file for class.
#define NEW(type)
Definition: memory.hpp:93
size_t size() const
Definition: utf8.hpp:161
static int scandir_filter(const struct dirent *a)
Definition: suck.cpp:61
void reset()
Reset buffer position to start of buffer.
Definition: buffer.hpp:553
const char * c_str()
get contents of buffer as zero-terminated c-style-string This strings lifetime is tied to it&#39;s buffer...
Definition: buffer.hpp:489
char * path
Definition: suck.hpp:52
static int stat(const char *path, struct stat *buf)
Definition: os.hpp:607
void add_from_property(const char *key)
Adds a classpath form a property entry to the global classpath entries list.
Definition: suck.cpp:186
uint8_t u1
Definition: types.hpp:40
Definition: zip.hpp:98
Commandline properties.
Definition: properties.hpp:45
uint8_t * end
Definition: suck.hpp:131
JNIEnv jthread jobject jclass jlong size
Definition: jvmti.h:387
ClassFileVersion version() const
Definition: suck.cpp:344
static Mutex lock
Definition: atomic.cpp:34
cacao::ClassFileVersion version
Definition: class.hpp:92
A version of the Java class file format.
Definition: loader.hpp:108
uint8_t * data
Definition: suck.hpp:129
static int fclose(FILE *fp)
Definition: os.hpp:328
Table::EntryRef EntryRef
Definition: zip.hpp:102
Utf8String name
Definition: class.hpp:91
Properties & get_properties()
Definition: vm.hpp:113
static char * getcwd(void)
Return the current working directory.
Definition: os.cpp:181
MIIterator i
Classpath entries list.
Definition: suck.hpp:62
int32_t s4
Definition: types.hpp:45
s4 type
Definition: suck.hpp:51
Mutex * mutex
Definition: suck.hpp:50
EntryRef find(Utf8String filename)
Find file in zip archive.
Definition: zip.hpp:108
Helper class used to implicitly acquire and release a mutex within a method scope.
Definition: mutex.hpp:42
uint8_t * pos
Definition: suck.hpp:130
static void abort()
Definition: os.hpp:196
void free()
Free memory held by this classbuffer.
Definition: suck.cpp:438
Definition: suck.hpp:49
bool opt_verboseclass
Definition: options.cpp:73
void add(char *classpath)
Adds a classpath to the global classpath entries list.
Definition: suck.cpp:82
#define MNEW(type, num)
Definition: memory.hpp:96
s4 pathlen
Definition: suck.hpp:53
Buffer & write(char)
Definition: buffer.hpp:280
const char * path
Definition: suck.hpp:132
SuckClasspath & get_suckclasspath()
Definition: vm.hpp:129
static ZipFile * open(const char *path)
Load zip archive.
Definition: zip.cpp:160
static int scandir(const char *dir, struct dirent ***namelist, int(*filter)(const struct dirent *), int(*compar)(const void *, const void *))
Definition: os.hpp:561
#define MFREE(ptr, type, num)
Definition: memory.hpp:97
static void put(java_handle_t *p, const char *key, const char *value)
Add the given property to the given Java system properties.
Definition: properties.cpp:531
const char * get(const char *key)
Get a property entry from the internal property map.
Definition: properties.cpp:590
#define printf(...)
Definition: ssa2.cpp:40
classinfo * clazz
Definition: suck.hpp:128
static VM * get_current()
Definition: vm.hpp:99
bool opt_verbose
Definition: options.cpp:67