diff --git a/jode/jode/bytecode/BinaryInfo.java.in b/jode/jode/bytecode/BinaryInfo.java.in
index 612d47e..d727d58 100644
--- a/jode/jode/bytecode/BinaryInfo.java.in
+++ b/jode/jode/bytecode/BinaryInfo.java.in
@@ -40,10 +40,13 @@ public class BinaryInfo {
public static final int FIELDS = 0x02;
public static final int METHODS = 0x04;
public static final int CONSTANTS = 0x08;
- public static final int ALL_ATTRIBUTES = 0x10;
+ public static final int KNOWNATTRIBS = 0x10;
public static final int INNERCLASSES = 0x20;
public static final int OUTERCLASSES = 0x40;
+ public static final int UNKNOWNATTRIBS = 0x80;
public static final int FULLINFO = 0xff;
+ public static final int MOSTINFO = 0x7f;
+ public static final int REFLECTINFO = 0x6f;
private Map unknownAttributes = null;
@@ -71,7 +74,7 @@ public class BinaryInfo {
int howMuch) throws IOException {
byte[] data = new byte[length];
input.readFully(data);
- if ((howMuch & ALL_ATTRIBUTES) != 0) {
+ if ((howMuch & UNKNOWNATTRIBS) != 0) {
if (unknownAttributes == null)
unknownAttributes = new SimpleMap();
unknownAttributes.put(name, data);
diff --git a/jode/jode/bytecode/BytecodeInfo.java.in b/jode/jode/bytecode/BytecodeInfo.java.in
index b16e499..4c1fcbd 100644
--- a/jode/jode/bytecode/BytecodeInfo.java.in
+++ b/jode/jode/bytecode/BytecodeInfo.java.in
@@ -209,7 +209,7 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes {
protected void readAttribute(String name, int length, ConstantPool cp,
DataInputStream input,
int howMuch) throws IOException {
- if ((howMuch & ALL_ATTRIBUTES) != 0
+ if ((howMuch & KNOWNATTRIBS) != 0
&& name.equals("LocalVariableTable")) {
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_LVT) != 0)
GlobalOptions.err.println("LocalVariableTable of "+methodInfo);
@@ -271,7 +271,7 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes {
+" range "+start+" - "+end
+" slot "+slot);
}
- } else if ((howMuch & ALL_ATTRIBUTES) != 0
+ } else if ((howMuch & KNOWNATTRIBS) != 0
&& name.equals("LineNumberTable")) {
int count = input.readUnsignedShort();
if (length != 2 + count * 4) {
diff --git a/jode/jode/bytecode/ClassInfo.java.in b/jode/jode/bytecode/ClassInfo.java.in
index cf18163..b32d3fe 100644
--- a/jode/jode/bytecode/ClassInfo.java.in
+++ b/jode/jode/bytecode/ClassInfo.java.in
@@ -35,13 +35,41 @@ import java.lang.reflect.Modifier;
/**
* This class does represent a class similar to java.lang.Class. You
- * can get the super class and the interfaces.
+ * can get the super class and the interfaces.
*
* The main difference to java.lang.Class is, that the objects are builded
* from a stream containing the .class file, and that it uses the
- * Type
to represent types instead of Class itself.
+ * Type
to represent types instead of Class itself.
*
- * @author Jochen Hoenicke
+ *
The InnerClasses attribute
+ *
+ * The InnerClasses attribute is transformed in a special way by this
+ * class so we want to taker a closer look. According to the inner
+ * class specification there must be an InnerClass attribute for
+ * every non top-level class that is referenced somewhere in the
+ * bytecode. This implies that if this is an inner class, it must
+ * contain a inner class attribute for itself. Before a class is
+ * referenced as outer class in an InnerClass attribute, it must be
+ * described by another InnerClass attribute.
+ *
+ * Since every class references itself, there must be informations
+ * about the outer class for each class scoped class. If that outer
+ * class is an outer class again, there must be information about it,
+ * too. This particular chain of InnerClassInfos is returned by the
+ * getOuterClasses() method; for convenience in reverse order, i.e.
+ * current class first, then the outer classes from innermost to
+ * outermost.
+ *
+ * A valid bytecode must also contain InnerClass infos for each inner
+ * classes it declares. These information are returned by the
+ * getInnerClasses() method. The order of these classes is the same
+ * as in the bytecode attribute.
+ *
+ * All remaining attributes are returned by getExtraClasses() in the
+ * same order as in the bytecode attribute.
+ *
+ * @author Jochen Hoenicke
*/
public class ClassInfo extends BinaryInfo {
@@ -133,7 +161,7 @@ public class ClassInfo extends BinaryInfo {
ConstantPool cp,
DataInputStream input,
int howMuch) throws IOException {
- if ((howMuch & ALL_ATTRIBUTES) != 0 && name.equals("SourceFile")) {
+ if ((howMuch & KNOWNATTRIBS) != 0 && name.equals("SourceFile")) {
if (length != 2)
throw new ClassFormatException("SourceFile attribute"
+ " has wrong length");
@@ -141,6 +169,9 @@ public class ClassInfo extends BinaryInfo {
} else if ((howMuch & (OUTERCLASSES | INNERCLASSES)) != 0
&& name.equals("InnerClasses")) {
int count = input.readUnsignedShort();
+ if (length != 2 + 8 * count)
+ throw new ClassFormatException
+ ("InnerClasses attribute has wrong length");
int innerCount = 0, outerCount = 0, extraCount = 0;
InnerClassInfo[] innerClassInfo = new InnerClassInfo[count];
for (int i=0; i< count; i++) {
@@ -164,16 +195,22 @@ public class ClassInfo extends BinaryInfo {
else
innerClassInfo[count - (++extraCount)] = ici;
}
+ /* Now innerClasses are at the front of innerClassInfo array
+ * in correct order. The other InnerClassInfos are in reverse
+ * order in the rest of the innerClassInfo array.
+ */
+
+ /* We now count the outerClasses. The reverse order is the
+ * right thing for us.
+ */
{
String lastOuterName = getName();
- for (int i = count - extraCount; i < count; i++) {
+ for (int i = count - extraCount;
+ i < count && lastOuterName != null; i++) {
InnerClassInfo ici = innerClassInfo[i];
if (ici.inner.equals(lastOuterName)) {
- for (int j = i; j > count - extraCount; j--)
- innerClassInfo[j] = innerClassInfo[j-1];
- innerClassInfo[count-extraCount] = ici;
- extraCount--;
outerCount++;
+ extraCount--;
lastOuterName = ici.outer;
}
}
@@ -187,21 +224,35 @@ public class ClassInfo extends BinaryInfo {
if (outerCount > 0) {
outerClasses = new InnerClassInfo[outerCount];
- System.arraycopy(innerClassInfo, innerCount,
- outerClasses, 0, outerCount);
} else
outerClasses = null;
if (extraCount > 0) {
extraClasses = new InnerClassInfo[extraCount];
- System.arraycopy(innerClassInfo, innerCount + outerCount,
- extraClasses, 0, extraCount);
} else
extraClasses = null;
+
+ /* The last part: We split between outer and extra classes.
+ * In this step we will also revert the order of the extra
+ * classes.
+ */
+ {
+ int outerPtr = 0;
+ String lastOuterName = getName();
+ for (int i = count - extraCount - outerCount;
+ i < count && lastOuterName != null; i++) {
+ InnerClassInfo ici = innerClassInfo[i];
- if (length != 2 + 8 * count)
- throw new ClassFormatException
- ("InnerClasses attribute has wrong length");
+ /* If we counted correctly there is no NullPointer
+ * or ArrayIndexOutOfBoundsException here
+ */
+ if (ici.inner.equals(lastOuterName)) {
+ outerClasses[outerPtr++] = ici;
+ lastOuterName = ici.outer;
+ } else
+ extraClasses[--extraCount] = ici;
+ }
+ }
} else
super.readAttribute(name, length, cp, input, howMuch);
}
@@ -242,12 +293,12 @@ public class ClassInfo extends BinaryInfo {
}
/* fields */
- if ((howMuch & (FIELDS | ALL_ATTRIBUTES)) != 0) {
+ if ((howMuch & (FIELDS | KNOWNATTRIBS | UNKNOWNATTRIBS)) != 0) {
int count = input.readUnsignedShort();
- if ((howMuch & FIELDS) != 0)
+ if ((status & FIELDS) == 0)
fields = new FieldInfo[count];
for (int i=0; i< count; i++) {
- if ((howMuch & FIELDS) != 0)
+ if ((status & FIELDS) == 0)
fields[i] = new FieldInfo(this);
fields[i].read(cpool, input, howMuch);
}
@@ -261,12 +312,12 @@ public class ClassInfo extends BinaryInfo {
}
/* methods */
- if ((howMuch & (METHODS | ALL_ATTRIBUTES)) != 0) {
+ if ((howMuch & (METHODS | KNOWNATTRIBS | UNKNOWNATTRIBS)) != 0) {
int count = input.readUnsignedShort();
- if ((howMuch & METHODS) != 0)
+ if ((status & METHODS) == 0)
methods = new MethodInfo[count];
for (int i=0; i< count; i++) {
- if ((howMuch & METHODS) != 0)
+ if ((status & METHODS) == 0)
methods[i] = new MethodInfo(this);
methods[i].read(cpool, input, howMuch);
}
diff --git a/jode/jode/bytecode/FieldInfo.java b/jode/jode/bytecode/FieldInfo.java
index 19ceb9b..4caf334 100644
--- a/jode/jode/bytecode/FieldInfo.java
+++ b/jode/jode/bytecode/FieldInfo.java
@@ -49,7 +49,7 @@ public class FieldInfo extends BinaryInfo {
ConstantPool cp,
DataInputStream input,
int howMuch) throws IOException {
- if ((howMuch & ALL_ATTRIBUTES) != 0 && name.equals("ConstantValue")) {
+ if ((howMuch & KNOWNATTRIBS) != 0 && name.equals("ConstantValue")) {
if (length != 2)
throw new ClassFormatException("ConstantValue attribute"
+ " has wrong length");
@@ -161,7 +161,7 @@ public class FieldInfo extends BinaryInfo {
}
public Object getConstant() {
- clazzInfo.loadInfo(ALL_ATTRIBUTES);
+ clazzInfo.loadInfo(KNOWNATTRIBS);
return constant;
}
diff --git a/jode/jode/bytecode/InnerClassInfo.java b/jode/jode/bytecode/InnerClassInfo.java
index f931586..a96db2a 100644
--- a/jode/jode/bytecode/InnerClassInfo.java
+++ b/jode/jode/bytecode/InnerClassInfo.java
@@ -33,5 +33,10 @@ public class InnerClassInfo {
this.name = name;
this.modifiers = modif;
}
+
+ public String toString() {
+ return "InnerClassInfo["+inner+","+outer+","+name+","
+ +java.lang.reflect.Modifier.toString(modifiers)+"]";
+ }
}
diff --git a/jode/jode/bytecode/MethodInfo.java b/jode/jode/bytecode/MethodInfo.java
index cbcc98f..efedd60 100644
--- a/jode/jode/bytecode/MethodInfo.java
+++ b/jode/jode/bytecode/MethodInfo.java
@@ -51,10 +51,10 @@ public class MethodInfo extends BinaryInfo {
protected void readAttribute(String name, int length, ConstantPool cp,
DataInputStream input,
int howMuch) throws IOException {
- if ((howMuch & ALL_ATTRIBUTES) != 0 && name.equals("Code")) {
+ if ((howMuch & KNOWNATTRIBS) != 0 && name.equals("Code")) {
bytecode = new BytecodeInfo(this);
bytecode.read(cp, input);
- } else if ((howMuch & ALL_ATTRIBUTES) != 0
+ } else if ((howMuch & KNOWNATTRIBS) != 0
&& name.equals("Exceptions")) {
int count = input.readUnsignedShort();
exceptions = new String[count];
@@ -213,12 +213,12 @@ public class MethodInfo extends BinaryInfo {
}
public void setBytecode(BytecodeInfo newBytecode) {
- clazzInfo.loadInfo(ALL_ATTRIBUTES);
+ clazzInfo.loadInfo(KNOWNATTRIBS);
bytecode = newBytecode;
}
public void setExceptions(String[] newExceptions) {
- clazzInfo.loadInfo(ALL_ATTRIBUTES);
+ clazzInfo.loadInfo(KNOWNATTRIBS);
exceptions = newExceptions;
}
diff --git a/jode/jode/bytecode/SearchPath.java b/jode/jode/bytecode/SearchPath.java
index b5e4849..917b2e5 100644
--- a/jode/jode/bytecode/SearchPath.java
+++ b/jode/jode/bytecode/SearchPath.java
@@ -48,8 +48,12 @@ public class SearchPath {
/**
* We need a different pathSeparatorChar, since ':' (used for most
* UNIX System, is used a protocol separator in URLs.
+ *
+ * We currently allow both pathSeparatorChar and
+ * altPathSeparatorChar and decide if it is a protocol separator
+ * by context.
*/
- public static final char pathSeparatorChar = ',';
+ public static final char altPathSeparatorChar = ',';
URL[] bases;
byte[][] urlzips;
File[] dirs;
@@ -176,34 +180,86 @@ public class SearchPath {
* entries may also be zip or jar files.
*/
public SearchPath(String path) {
- StringTokenizer tokenizer =
- new StringTokenizer(path,
- String.valueOf(pathSeparatorChar));
- int length = tokenizer.countTokens();
-
+ // Calculate a good approximation (rounded upwards) of the tokens
+ // in this path.
+ int length = 1;
+ for (int index=path.indexOf(File.pathSeparatorChar);
+ index != -1; length++)
+ index = path.indexOf(File.pathSeparatorChar, index+1);
+ if (File.pathSeparatorChar != altPathSeparatorChar) {
+ for (int index=path.indexOf(altPathSeparatorChar);
+ index != -1; length++)
+ index = path.indexOf(altPathSeparatorChar, index+1);
+ }
bases = new URL[length];
urlzips = new byte[length][];
dirs = new File[length];
zips = new ZipFile[length];
zipEntries = new Hashtable[length];
zipDirs = new String[length];
- for (int i=0; i< length; i++) {
- String token = tokenizer.nextToken();
+ int i = 0;
+ for (int ptr=0; ptr < path.length(); ptr++, i++) {
+ int next = ptr;
+ while (next < path.length()
+ && path.charAt(next) != File.pathSeparatorChar
+ && path.charAt(next) != altPathSeparatorChar)
+ next++;
+
+ int index = ptr;
+ colon_separator:
+ while (next > ptr
+ && next < path.length()
+ && path.charAt(next) == ':') {
+ // Check if this is a URL instead of a pathSeparator
+ // Since this is a while loop it allows nested urls like
+ // jar:ftp://ftp.foo.org/pub/foo.jar!/
+
+ while (index < next) {
+ char c = path.charAt(index);
+ // According to RFC 1738 letters, digits, '+', '-'
+ // and '.' are allowed SCHEMA characters. We
+ // disallow '.' because it is a good marker that
+ // the user has specified a filename instead of a
+ // URL.
+ if ((c < 'A' || c > 'Z')
+ && (c < 'a' || c > 'z')
+ && (c < '0' || c > '9')
+ && "+-".indexOf(c) == -1) {
+ break colon_separator;
+ }
+ index++;
+ }
+ next++;
+ index++;
+ while (next < path.length()
+ && path.charAt(next) != File.pathSeparatorChar
+ && path.charAt(next) != altPathSeparatorChar)
+ next++;
+ }
+ String token = path.substring(ptr, next);
+ ptr = next;
boolean mustBeJar = false;
// We handle jar URL's ourself.
if (token.startsWith("jar:")) {
- int index = 0;
+ index = 0;
do {
index = token.indexOf('!', index);
- } while (token.charAt(index+1) != '/');
+ } while (index != -1 && index != token.length()-1
+ && token.charAt(index+1) != '/');
+
+ if (index == -1 || index == token.length()-1) {
+ GlobalOptions.err.println("Warning: Illegal jar url "
+ + token + ".");
+ continue;
+ }
zipDirs[i] = token.substring(index+2);
if (!zipDirs[i].endsWith("/"))
zipDirs[i] = zipDirs[i] + "/";
token = token.substring(4, index);
mustBeJar = true;
}
- int index = token.indexOf(':');
+ index = token.indexOf(':');
if (index != -1 && index < token.length()-2
&& token.charAt(index+1) == '/'
&& token.charAt(index+2) == '/') {
@@ -221,9 +277,9 @@ public class SearchPath {
} catch (IOException ex) {
// ignore
} catch (SecurityException ex) {
- GlobalOptions.err.println("Warning: Security exception "
- +"while accessing "
- +bases[i]+".");
+ GlobalOptions.err.println
+ ("Warning: Security exception while accessing "
+ +bases[i]+".");
}
} catch (MalformedURLException ex) {
/* disable entry */
@@ -243,8 +299,9 @@ public class SearchPath {
}
} catch (SecurityException ex) {
/* disable this entry */
- GlobalOptions.err.println("Warning: SecurityException while"
- + " accessing " + token);
+ GlobalOptions.err.println
+ ("Warning: SecurityException while accessing "
+ + token + ".");
dirs[i] = null;
}
}