summaryrefslogtreecommitdiff
path: root/libjava/gnu
diff options
context:
space:
mode:
authortromey <tromey@138bc75d-0d04-0410-961f-82ee72b054a4>2004-11-25 03:47:08 +0000
committertromey <tromey@138bc75d-0d04-0410-961f-82ee72b054a4>2004-11-25 03:47:08 +0000
commita4ccc41f9a5f050d518b8c30739a647f67756f9e (patch)
tree477abdf83653e20b0e74447d6ca47eb67b0511b8 /libjava/gnu
parent2f3c6e08b9d664df3e416a186fd2938de188e706 (diff)
downloadgcc-a4ccc41f9a5f050d518b8c30739a647f67756f9e.tar.gz
* Merged gcj-abi-2-dev-branch to trunk.
(Actual changes too large to list in the commit message; see ChangeLog.) git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@91270 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libjava/gnu')
-rw-r--r--libjava/gnu/gcj/runtime/PersistentByteMap.java484
-rw-r--r--libjava/gnu/gcj/runtime/SharedLibHelper.java98
-rw-r--r--libjava/gnu/gcj/runtime/VMClassLoader.java3
-rw-r--r--libjava/gnu/gcj/runtime/natSharedLibLoader.cc13
-rw-r--r--libjava/gnu/gcj/tools/gcj_dbtool/Main.java272
5 files changed, 852 insertions, 18 deletions
diff --git a/libjava/gnu/gcj/runtime/PersistentByteMap.java b/libjava/gnu/gcj/runtime/PersistentByteMap.java
new file mode 100644
index 00000000000..90b7d33a8f4
--- /dev/null
+++ b/libjava/gnu/gcj/runtime/PersistentByteMap.java
@@ -0,0 +1,484 @@
+/* Copyright (C) 2004 Free Software Foundation
+
+ This file is part of libgcj.
+
+This software is copyrighted work licensed under the terms of the
+Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
+details. */
+
+
+
+/* A PersistentByteMap maps a byte array to another byte array. It
+uses a file that does not need to be serialized but may be
+memory-mapped and read in-place. So, even if there are many instances
+of gcj applications running, the can share PersistentByteMaps.
+
+The idea is to make searches as fast as possible: opening a
+PersistentByteMap is cheap and search time doesn't grow with the
+number of entries in the table. On the other hand, enumerating the
+map is slow, but that is a relatively uncommon operation.
+
+The main use of this class is to provide a way to map the
+MessageDigest of a class file to the location of a DSO that contains
+the compiled version of that class. It is up the the installer of an
+application to keep the DSO up to date with the jar.
+
+USAGE:
+ MessageDigest md = MessageDigest.getInstance("MD5");
+ digest = md.digest(bytes);
+
+ PersistentByteMap map
+ = new PersistentByteMap
+ (fileName, PersistentByteMap.AccessMode.READ_ONLY);
+
+ byte[] soName = map.get(digest);
+ if (soName)
+ {
+ String SharedLibraryName = new String(soName);
+
+BUGS/FEATURES:
+ remove() isn't written yet.
+
+ we can't change the capacity of a PersistentByteMap.
+
+ 0x12345678 is a bad choice for the magic number.
+
+ capacity is fixed once the map has been created.
+
+ We use linear probing to resolve collisions. It might be
+ better to use a scheme that results in fewer probes to
+ determine that an item isn't found. However, even when the
+ table is half full there are only on average 1.5 probes for a
+ successful search and 2.5 probes for an unsuccessful one.
+
+ We don't use unique strings. This wastes space.
+
+ capacity should probably be prime, but we don't check that.
+
+ we don't do any locking at all: adding to a PersistentByteMap
+ at runtime is possible, but it requires filesystem locks
+ around get(), put(), and remove().
+*/
+
+package gnu.gcj.runtime;
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.security.MessageDigest;
+
+public class PersistentByteMap
+{
+ private MappedByteBuffer buf;
+
+ static private final int MAGIC = 0;
+ static private final int VERSION = 4;
+ static private final int CAPACITY = 8;
+ static private final int TABLE_BASE = 12;
+ static private final int STRING_BASE = 16;
+ static private final int STRING_SIZE = 20;
+ static private final int FILE_SIZE = 24;
+ static private final int ELEMENTS = 28;
+
+ static private final int INT_SIZE = 4;
+
+ static private final int TABLE_ENTRY_SIZE = 2 * INT_SIZE;
+
+ private int capacity; // number of entries
+ private int table_base; // offset from start of file, in bytes
+ private int string_base; // offset from start of file, in bytes
+ private int string_size; // size of string table, in bytes
+ private int file_size; // size of file, in bytes;
+ private int elements; // number of elements in table
+
+ private long length; // the length of the underlying file
+
+ static private final int UNUSED_ENTRY = -1;
+
+ static public final int KEYS = 0;
+ static public final int VALUES = 1;
+ static public final int ENTRIES = 2;
+
+ static final public class AccessMode
+ {
+ private final FileChannel.MapMode mapMode;
+
+ static
+ {
+ READ_ONLY = new AccessMode(FileChannel.MapMode.READ_ONLY);
+ READ_WRITE = new AccessMode(FileChannel.MapMode.READ_WRITE);
+ }
+
+ public static final AccessMode READ_ONLY;
+ public static final AccessMode READ_WRITE;
+
+ private AccessMode(FileChannel.MapMode mode)
+ {
+ this.mapMode = mode;
+ }
+ }
+
+ private PersistentByteMap()
+ {
+ }
+
+ public PersistentByteMap(String filename, AccessMode mode)
+ throws IOException
+ {
+ this(new File(filename), mode);
+ }
+
+ public PersistentByteMap(File f, AccessMode mode)
+ throws IOException
+ {
+ FileChannel fc;
+
+ if (mode == AccessMode.READ_ONLY)
+ {
+ FileInputStream fis = new FileInputStream(f);
+ fc = fis.getChannel();
+ }
+ else
+ {
+ RandomAccessFile fos = new RandomAccessFile(f, "rw");
+ fc = fos.getChannel();
+ }
+
+ length = fc.size();
+ buf = fc.map(mode.mapMode, 0, length);
+
+ int magic = getWord (MAGIC);
+ if (magic != 0x12345678)
+ throw new IllegalArgumentException(f.getName());
+
+ table_base = getWord (TABLE_BASE);
+ capacity = getWord (CAPACITY);
+ string_base = getWord (STRING_BASE);
+ string_size = getWord (STRING_SIZE);
+ file_size = getWord (FILE_SIZE);
+ elements = getWord (ELEMENTS);
+
+ // FIXME: Insert a bunch of sanity checks here
+ }
+
+ private void init (PersistentByteMap m, File f, int capacity, int strtabSize)
+ throws IOException
+ {
+ f.createNewFile();
+ RandomAccessFile raf = new RandomAccessFile(f, "rw");
+
+ this.capacity = capacity;
+ table_base = 64;
+ string_base = table_base + capacity * TABLE_ENTRY_SIZE;
+ string_size = 0;
+ file_size = string_base;
+ elements = 0;
+
+ int totalFileSize = string_base + strtabSize;
+
+ // Create the file; this rounds up the size of the file to a fixed
+ // number of 4k pages.
+ byte[] _4k = new byte[4096];
+ for (long i = 0; i < totalFileSize; i+= 4096)
+ raf.write(_4k);
+
+ FileChannel fc = raf.getChannel();
+ buf = fc.map(FileChannel.MapMode.READ_WRITE, 0, raf.length());
+
+ for (int i = 0; i < capacity; i++)
+ putKeyPos(UNUSED_ENTRY, i);
+
+ putWord(0x12345678, MAGIC);
+ putWord(0x01, VERSION);
+ putWord(capacity, CAPACITY);
+ putWord(table_base, TABLE_BASE);
+ putWord(string_base, STRING_BASE);
+ putWord(file_size, FILE_SIZE);
+ putWord(elements, ELEMENTS);
+ buf.force();
+ }
+
+ static public PersistentByteMap emptyPersistentByteMap(String filename,
+ int capacity, int strtabSize)
+ throws IOException
+ {
+ File f = new File(filename);
+ PersistentByteMap m = new PersistentByteMap();
+ m.init(m, f, capacity, strtabSize);
+ return m;
+ }
+
+ private int getWord (int index)
+ {
+ buf.position(index);
+ byte[] wordBuf = new byte[4];
+ buf.get(wordBuf);
+
+ int result = (int)wordBuf[0]&0xff;
+ result += ((int)wordBuf[1]&0xff) << 8;
+ result += ((int)wordBuf[2]&0xff) << 16;
+ result += ((int)wordBuf[3]&0xff) << 24;
+ return result;
+ }
+
+ private void putWord (int word, int index)
+ {
+ buf.position(index);
+ byte[] wordBuf = new byte[4];
+ wordBuf[0] = (byte)(word);
+ wordBuf[1] = (byte)(word >>> 8);
+ wordBuf[2] = (byte)(word >>> 16);
+ wordBuf[3] = (byte)(word >>> 24);
+ buf.put(wordBuf);
+ }
+
+ public Set entrySet()
+ {
+ return null;
+ }
+
+ private int getBucket(int n)
+ {
+ return table_base + (2*n * INT_SIZE);
+ }
+
+ private int getKeyPos(int n)
+ {
+ return getWord(getBucket(n));
+ }
+
+ private int getValuePos(int n)
+ {
+ return getWord(getBucket(n) + INT_SIZE);
+ }
+
+ private void putKeyPos(int index, int n)
+ {
+ putWord(index, getBucket(n));
+ }
+
+ private void putValuePos(int index, int n)
+ {
+ putWord(index, getBucket(n) + INT_SIZE);
+ }
+
+ private byte[] getBytes(int n)
+ {
+ int len = getWord (string_base + n);
+ int base = string_base + n + INT_SIZE;
+ byte[] key = new byte[len];
+ buf.position(base);
+ buf.get(key, 0, len);
+ return key;
+ }
+
+ private int hash (byte[] b)
+ {
+ // We assume that the message digest is evenly distributed, so we
+ // only need to use a few bytes of it as the hash function.
+ long hashIndex
+ = ((b[0]&0xffL)
+ + ((b[1]&0xffL)<<8)
+ + ((b[2]&0xffL)<<16)
+ + ((b[3]&0xffL)<<24));
+ long result = hashIndex % (long)capacity;
+ return (int)result;
+ }
+
+ public byte[] get(byte[] digest)
+ {
+ int hashIndex = hash(digest);
+
+ do
+ {
+ int k = getKeyPos(hashIndex);
+ if (k == UNUSED_ENTRY)
+ return null;
+
+ if (Arrays.equals ((byte[])digest, getBytes(k)))
+ return getBytes(getValuePos(hashIndex));
+
+ // Use linear probing to resolve hash collisions. This may
+ // not be theoretically as good as open addressing, but it has
+ // good cache behviour.
+ hashIndex++;
+ hashIndex %= capacity;
+ }
+ while (true);
+ }
+
+ public void put(byte[] digest, byte[] value)
+ throws IllegalAccessException
+ {
+ int hashIndex = hash(digest);
+
+ // With the the table 2/3 full there will be on average 2 probes
+ // for a successful search and 5 probes for an unsuccessful one.
+ if (elements >= capacity * 2/3)
+ throw new IllegalAccessException("Table Full: " + elements);
+
+ do
+ {
+ int k = getKeyPos(hashIndex);
+ if (k == UNUSED_ENTRY)
+ {
+ int newKey = addBytes(digest);
+ putKeyPos(newKey, hashIndex);
+ int newValue = addBytes(value);
+ putValuePos(newValue, hashIndex);
+ elements++;
+ putWord(elements, ELEMENTS);
+ return;
+ }
+ else if (Arrays.equals (digest, getBytes(k)))
+ {
+ int newValue = addBytes((byte[])value);
+ putValuePos(newValue, hashIndex);
+ return;
+ }
+
+ hashIndex++;
+ hashIndex %= capacity;
+ }
+ while (true);
+ }
+
+ private int addBytes (byte[] data)
+ throws IllegalAccessException
+ {
+ if (data.length + INT_SIZE >= this.length)
+ throw new IllegalAccessException("String table Full");
+
+ int extent = string_base+string_size;
+ int top = extent;
+ putWord(data.length, extent);
+ extent += INT_SIZE;
+ buf.position(extent);
+ buf.put(data, 0, data.length);
+ extent += data.length;
+ extent += INT_SIZE-1;
+ extent &= ~(INT_SIZE-1); // align
+ string_size = extent - string_base;
+ file_size = extent;
+ putWord (string_size, STRING_SIZE);
+ putWord (file_size, FILE_SIZE);
+
+ return top - string_base;
+ }
+
+ public Iterator iterator(int type)
+ {
+ return new HashIterator(type);
+ }
+
+ public int size()
+ {
+ return elements;
+ }
+
+ public int capacity()
+ {
+ return capacity;
+ }
+
+ private final class HashIterator implements Iterator
+ {
+ /** Current index in the physical hash table. */
+
+ private int idx;
+ private int count;
+ private final int type;
+
+ /**
+ * Construct a new HashIterator with the supplied type.
+ * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES}
+ */
+ HashIterator(int type)
+ {
+ this.type = type;
+ count = elements;
+ idx = 0;
+ }
+
+ /**
+ * Returns true if the Iterator has more elements.
+ * @return true if there are more elements
+ * @throws ConcurrentModificationException if the HashMap was modified
+ */
+ public boolean hasNext()
+ {
+ return count > 0;
+ }
+
+ /**
+ * Returns the next element in the Iterator's sequential view.
+ * @return the next element
+ * @throws ConcurrentModificationException if the HashMap was modified
+ * @throws NoSuchElementException if there is none
+ */
+ public Object next()
+ {
+ count--;
+ for (int i = idx; i < capacity; i++)
+ if (getKeyPos(i) != UNUSED_ENTRY)
+ {
+ idx = i+1;
+ if (type == VALUES)
+ return getBytes(getValuePos(i));
+ if (type == KEYS)
+ return getBytes(getKeyPos(i));
+ return new MapEntry(i,
+ getBytes(getKeyPos(i)),
+ getBytes(getValuePos(i)));
+ }
+ return null;
+ }
+
+ /**
+ * Remove from the underlying collection the last element returned
+ * by next (optional operation). This method can be called only
+ * once after each call to <code>next()</code>. It does not affect
+ * what will be returned by subsequent calls to next.
+ *
+ * @throws IllegalStateException if next has not yet been called
+ * or remove has already been called since the last call
+ * to next.
+ * @throws UnsupportedOperationException if this Iterator does not
+ * support the remove operation.
+ */
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ static public final class MapEntry
+ {
+ private final Object key;
+ private final Object value;
+ private final int bucket;
+
+ public MapEntry(int bucket, Object newKey, Object newValue)
+ {
+ this.key = newKey;
+ this.value = newValue;
+ this.bucket = bucket;
+ }
+
+ public final Object getKey()
+ {
+ return key;
+ }
+
+ public final Object getValue()
+ {
+ return value;
+ }
+
+ public final int getBucket()
+ {
+ return bucket;
+ }
+ }
+}
diff --git a/libjava/gnu/gcj/runtime/SharedLibHelper.java b/libjava/gnu/gcj/runtime/SharedLibHelper.java
index 9e170a120be..a0bfe68b83c 100644
--- a/libjava/gnu/gcj/runtime/SharedLibHelper.java
+++ b/libjava/gnu/gcj/runtime/SharedLibHelper.java
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001, 2003 Free Software Foundation
+/* Copyright (C) 2001, 2003, 2004 Free Software Foundation
This file is part of libgcj.
@@ -13,6 +13,12 @@ import java.net.MalformedURLException;
import java.util.HashMap;
import java.security.*;
import gnu.gcj.Core;
+import java.util.Set;
+import java.util.Iterator;
+import java.util.HashSet;
+import java.util.HashMap;
+import java.nio.channels.FileChannel;
+import java.io.*;
public class SharedLibHelper
{
@@ -36,34 +42,82 @@ public class SharedLibHelper
{
synchronized (map)
{
- WeakReference ref = (WeakReference) map.get(libname);
- if (ref != null)
- return (SharedLibHelper) ref.get();
+ Set s = (Set)map.get(libname);
+ if (s == null)
+ return null;
+ for (Iterator i=s.iterator(); i.hasNext();)
+ {
+ WeakReference ref = (WeakReference)i.next();
+ if (ref != null)
+ return (SharedLibHelper) ref.get();
+ }
return null;
}
}
+ static void copyFile (File in, File out) throws IOException
+ {
+ FileChannel source = new FileInputStream(in).getChannel();
+ FileChannel destination = new FileOutputStream(out).getChannel();
+ source.transferTo(0, source.size(), destination);
+ source.close();
+ destination.close();
+ }
+
public static SharedLibHelper findHelper (ClassLoader loader, String libname,
CodeSource source)
{
synchronized (map)
{
SharedLibHelper result;
- WeakReference ref = (WeakReference) map.get(libname);
- if (ref != null)
+ Set s = (Set)map.get(libname);
+ if (s == null)
+ {
+ s = new HashSet();
+ map.put(libname, s);
+ }
+ else
{
- result = (SharedLibHelper) ref.get();
- if (result != null)
+ for (Iterator i=s.iterator(); i.hasNext();)
{
- if (result.loader != loader)
- // FIXME
- throw new UnknownError();
- return result;
+ WeakReference ref = (WeakReference)i.next();
+ if (ref != null)
+ {
+ result = (SharedLibHelper) ref.get();
+ if (result != null)
+ {
+ // A match succeeds if the library is already
+ // loaded by LOADER or any of its ancestors.
+ ClassLoader l = loader;
+ do
+ {
+ if (result.loader == l)
+ return result;
+ l = l.getParent();
+ }
+ while (l != null);
+ }
+ }
}
- }
+ // Oh dear. We've already mapped this shared library, but
+ // with a different class loader. We need to copy it.
+ try
+ {
+ File copy
+ = File.createTempFile(new File(libname).getName(),
+ ".so", new File ("/tmp"));
+ File src = new File(libname);
+ copyFile (src, copy);
+ libname = copy.getPath();
+ }
+ catch (IOException e)
+ {
+ return null;
+ }
+ }
result = new SharedLibHelper(libname, loader, source, 0);
- map.put(libname, new WeakReference(result));
+ s.add(new WeakReference(result));
return result;
}
}
@@ -73,7 +127,15 @@ public class SharedLibHelper
public Class findClass(String name)
{
ensureInit();
- return (Class) classMap.get(name);
+ Class result = (Class) classMap.get(name);
+ if (result != null)
+ {
+ // We never want to return a class without its supers linked.
+ // It isn't clear from the spec, but this is what other
+ // implementations do in practice.
+ ensureSupersLinked(result);
+ }
+ return result;
}
public URL findResource (String name)
@@ -106,6 +168,12 @@ public class SharedLibHelper
native boolean hasResource(String name);
native void init();
+ native void ensureSupersLinked(Class k);
+
+ public String toString ()
+ {
+ return "shared object " + baseName;
+ }
/** Called during dlopen's processing of the init section. */
void registerClass(String name, Class cls)
diff --git a/libjava/gnu/gcj/runtime/VMClassLoader.java b/libjava/gnu/gcj/runtime/VMClassLoader.java
index 1fc7081bb78..f9ef872b30a 100644
--- a/libjava/gnu/gcj/runtime/VMClassLoader.java
+++ b/libjava/gnu/gcj/runtime/VMClassLoader.java
@@ -105,7 +105,8 @@ public final class VMClassLoader extends java.net.URLClassLoader
/** This is overridden to search the internal hash table, which
* will only search existing linked-in classes. This will make
* the default implementation of loadClass (in ClassLoader) work right.
- * The implementation of this method is in java/lang/natClassLoader.cc.
+ * The implementation of this method is in
+ * gnu/gcj/runtime/natVMClassLoader.cc.
*/
protected native Class findClass(String name)
throws java.lang.ClassNotFoundException;
diff --git a/libjava/gnu/gcj/runtime/natSharedLibLoader.cc b/libjava/gnu/gcj/runtime/natSharedLibLoader.cc
index 46eef755a09..184692e1040 100644
--- a/libjava/gnu/gcj/runtime/natSharedLibLoader.cc
+++ b/libjava/gnu/gcj/runtime/natSharedLibLoader.cc
@@ -1,6 +1,6 @@
// natSharedLibLoader.cc - Implementation of SharedLibHelper native methods.
-/* Copyright (C) 2001, 2003 Free Software Foundation
+/* Copyright (C) 2001, 2003, 2004 Free Software Foundation
This file is part of libgcj.
@@ -12,6 +12,8 @@ details. */
#include <gcj/cni.h>
#include <jvm.h>
+#include <execution.h>
+
#include <gnu/gcj/runtime/SharedLibHelper.h>
#include <java/io/IOException.h>
#include <java/lang/UnsupportedOperationException.h>
@@ -30,9 +32,10 @@ typedef void (*CoreHookFunc) (_Jv_core_chain *);
void
_Jv_sharedlib_register_hook (jclass cls)
{
- curHelper->registerClass(cls->getName(), cls);
cls->protectionDomain = curHelper->domain;
cls->loader = curLoader;
+ cls->engine = &_Jv_soleCompiledEngine;
+ curHelper->registerClass(cls->getName(), cls);
}
static void
@@ -122,3 +125,9 @@ gnu::gcj::runtime::SharedLibHelper::finalize()
dlclose (handler);
#endif
}
+
+void
+gnu::gcj::runtime::SharedLibHelper::ensureSupersLinked(jclass k)
+{
+ _Jv_Linker::wait_for_state (k, JV_STATE_LOADING);
+}
diff --git a/libjava/gnu/gcj/tools/gcj_dbtool/Main.java b/libjava/gnu/gcj/tools/gcj_dbtool/Main.java
new file mode 100644
index 00000000000..a478f934297
--- /dev/null
+++ b/libjava/gnu/gcj/tools/gcj_dbtool/Main.java
@@ -0,0 +1,272 @@
+/* Copyright (C) 2004 Free Software Foundation
+
+ This file is part of libgcj.
+
+This software is copyrighted work licensed under the terms of the
+Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
+details. */
+
+package gnu.gcj.tools.gcj_dbtool;
+
+
+import gnu.gcj.runtime.PersistentByteMap;
+import java.io.*;
+import java.util.*;
+import java.util.jar.*;
+import java.security.MessageDigest;
+import java.math.BigInteger;
+
+public class Main
+{
+ public static void main (String[] s)
+ {
+ insist (s.length >= 1);
+ if (s[0].equals("-v"))
+ {
+ insist (s.length == 1);
+ System.out.println("jv-dbtool ("
+ + System.getProperty("java.vm.name")
+ + ") "
+ + System.getProperty("java.vm.version"));
+ System.out.println();
+ System.out.println("Copyright 2004 Free Software Foundation, Inc.");
+ System.out.println("This is free software; see the source for copying conditions. There is NO");
+ System.out.println("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.");
+ return;
+ }
+
+ if (s[0].equals("-n"))
+ {
+ insist (s.length >= 2 && s.length <= 3);
+
+ int capacity = 32749;
+
+ if (s.length == 3)
+ {
+ // The user has explicitly provided a size for the table.
+ // We're going to make that size prime. This isn't
+ // strictly necessary but it can't hurt.
+
+ BigInteger size = new BigInteger(s[2], 10);
+ BigInteger two = BigInteger.ONE.add(BigInteger.ONE);
+
+ if (size.getLowestSetBit() != 0) // A hard way to say isEven()
+ size = size.add(BigInteger.ONE);
+
+ while (! size.isProbablePrime(10))
+ size = size.add(two);
+
+ capacity = size.intValue();
+
+ if (capacity <= 2)
+ {
+ usage();
+ System.exit(1);
+ }
+ }
+
+ try
+ {
+ PersistentByteMap b
+ = PersistentByteMap.emptyPersistentByteMap (s[1], capacity, capacity*64);
+ }
+ catch (Exception e)
+ {
+ System.err.println ("error: could not create "
+ + s[1] + ": " + e.toString());
+ System.exit(2);
+ }
+ return;
+ }
+
+ if (s[0].equals("-a"))
+ {
+ try
+ {
+ insist (s.length == 4);
+ File jar = new File(s[2]);
+ PersistentByteMap b
+ = new PersistentByteMap(new File(s[1]),
+ PersistentByteMap.AccessMode.READ_WRITE);
+ File soFile = new File(s[3]);
+ if (! soFile.isFile())
+ throw new IllegalArgumentException(s[3] + " is not a file");
+
+ addJar(jar, b, soFile);
+ }
+ catch (Exception e)
+ {
+ System.err.println ("error: could not update " + s[1]
+ + ": " + e.toString());
+ System.exit(2);
+ }
+ return;
+ }
+
+ if (s[0].equals("-t"))
+ {
+ try
+ {
+ insist (s.length == 2);
+ PersistentByteMap b
+ = new PersistentByteMap(new File(s[1]),
+ PersistentByteMap.AccessMode.READ_ONLY);
+ Iterator iterator = b.iterator(PersistentByteMap.ENTRIES);
+
+ while (iterator.hasNext())
+ {
+ PersistentByteMap.MapEntry entry
+ = (PersistentByteMap.MapEntry)iterator.next();
+ byte[] key = (byte[])entry.getKey();
+ byte[] value = (byte[])b.get(key);
+ if (! Arrays.equals (value, (byte[])entry.getValue()))
+ {
+ String err
+ = ("Key " + bytesToString(key) + " at bucket "
+ + entry.getBucket());
+
+ throw new RuntimeException(err);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ System.exit(3);
+ }
+ return;
+ }
+
+ if (s[0].equals("-l"))
+ {
+ insist (s.length == 2);
+ try
+ {
+ PersistentByteMap b
+ = new PersistentByteMap(new File(s[1]),
+ PersistentByteMap.AccessMode.READ_ONLY);
+
+ System.out.println ("Capacity: " + b.capacity());
+ System.out.println ("Size: " + b.size());
+ System.out.println ();
+
+ System.out.println ("Elements: ");
+ Iterator iterator = b.iterator(PersistentByteMap.ENTRIES);
+
+ while (iterator.hasNext())
+ {
+ PersistentByteMap.MapEntry entry
+ = (PersistentByteMap.MapEntry)iterator.next();
+ byte[] digest = (byte[])entry.getKey();
+ System.out.print ("[" + entry.getBucket() + "] "
+ + bytesToString(digest)
+ + " -> ");
+ System.out.println (new String((byte[])entry.getValue()));
+ }
+ }
+ catch (Exception e)
+ {
+ System.err.println ("error: could not list "
+ + s[1] + ": " + e.toString());
+ System.exit(2);
+ }
+ return;
+ }
+
+ if (s[0].equals("-d"))
+ {
+ insist (s.length == 2);
+ try
+ {
+ MessageDigest md = MessageDigest.getInstance("MD5");
+ PersistentByteMap b
+ = new PersistentByteMap(new File(s[1]),
+ PersistentByteMap.AccessMode.READ_WRITE);
+ int N = b.capacity();
+ byte[] bytes = new byte[1];
+ byte digest[] = md.digest(bytes);
+ for (int i = 0; i < N; i++)
+ {
+ digest = md.digest(digest);
+ b.put(digest, digest);
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ System.exit(3);
+ }
+ return;
+ }
+
+ usage();
+ System.exit(1);
+ }
+
+ private static void insist(boolean ok)
+ {
+ if (! ok)
+ {
+ usage();
+ System.exit(1);
+ }
+ }
+
+ private static void usage()
+ {
+ System.err.println
+ ("jv-dbtool: Manipulate gcj map database files\n"
+ + "\n"
+ + " Usage: \n"
+ + " jv-dbtool -n file.gcjdb [size] - Create a new gcj map database\n"
+ + " jv-dbtool -a file.gcjdb file.jar file.so\n"
+ + " - Add the contents of file.jar to the database\n"
+ + " jv-dbtool -t file.gcjdb - Test a gcj map database\n"
+ + " jv-dbtool -l file.gcjdb - List a gcj map database\n");
+ }
+
+
+ private static void addJar(File f, PersistentByteMap b, File soFile)
+ throws Exception
+ {
+ MessageDigest md = MessageDigest.getInstance("MD5");
+
+ JarFile jar = new JarFile (f);
+ Enumeration entries = jar.entries();
+
+ while (entries.hasMoreElements())
+ {
+ JarEntry classfile = (JarEntry)entries.nextElement();
+ if (classfile.getName().endsWith(".class"))
+ {
+ InputStream str = jar.getInputStream(classfile);
+ long length = classfile.getSize();
+ if (length == -1)
+ throw new EOFException();
+
+ byte[] data = new byte[length];
+ int pos = 0;
+ while (length - pos > 0)
+ {
+ int len = str.read(data, pos, (int)(length - pos));
+ if (len == -1)
+ throw new EOFException("Not enough data reading from: "
+ + classfile.getName());
+ pos += len;
+ }
+ b.put(md.digest(data),
+ soFile.getCanonicalPath().getBytes());
+ }
+ }
+ }
+
+ static String bytesToString(byte[] b)
+ {
+ StringBuffer hexBytes = new StringBuffer();
+ int length = b.length;
+ for (int i = 0; i < length; ++i)
+ hexBytes.append(Integer.toHexString(b[i] & 0xff));
+ return hexBytes.toString();
+ }
+}
+