From 0270969c565904bdab53e148d255d804d3d26d00 Mon Sep 17 00:00:00 2001 From: jochen Date: Sat, 30 Oct 1999 09:10:18 +0000 Subject: [PATCH] SearchPath.pathSeparatorChar -> altPathSeparatorChar SearchPath: allow both pathSeparators. BinaryInfo: ALL_ATTRIBUTES splitted in KNOWNATTRIB, UNKNOWNATTRIBS ClassInfo: innerClasses overworked, some more comments git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@1179 379699f6-c40d-0410-875b-85095c16579e --- jode/jode/bytecode/BinaryInfo.java.in | 7 +- jode/jode/bytecode/BytecodeInfo.java.in | 4 +- jode/jode/bytecode/ClassInfo.java.in | 95 +++++++++++++++++++------ jode/jode/bytecode/FieldInfo.java | 4 +- jode/jode/bytecode/InnerClassInfo.java | 5 ++ jode/jode/bytecode/MethodInfo.java | 8 +-- jode/jode/bytecode/SearchPath.java | 89 ++++++++++++++++++----- 7 files changed, 164 insertions(+), 48 deletions(-) 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; } }