diff --git a/jode/ChangeLog b/jode/ChangeLog index 6f538ab..3b32dbd 100644 --- a/jode/ChangeLog +++ b/jode/ChangeLog @@ -1,3 +1,73 @@ +2001-08-05 Jochen Hoenicke + + Documentation updates (INSTALL, javadoc). + Added JUnit Test cases. + * build.xml: Big update. + * net/sf/jode/bytecode/BasicBlock.java: + (updateMaxStackLocals): new method to calculate maxStack and + maxLocals. + (setBlocks): fixed calculation of handlers, call updateMaxLocals. + * net/sf/jode/bytecode/BasicBlockReader.java: + (maxLocals, maxStack): new fields. + (readCode): read maxStack/Locals into private fields. + (convert): check that maxStack/Locals match what we calculate. + * net/sf/jode/bytecode/BinaryInfo.java: + (getKnownAttributeCount): renamed to... + (getAttributeCount): ... this, and also count internal attributes. + Made it protected. + (readAttribute): made protected. + (drop): made protected. + (prepareAttributes): made protected. + (writeKnownAttributes): removed. + (writeAttributes): made protected, use getAttributeCount. + Changed policy: it doesn't call writeKnownAttribute, but instead + it expects sub classes to override this method. + (getAttributeSize): made protected, subclasses should override it. + Changed all subclasses to new policy. + * net/sf/jode/bytecode/Block.java: + (lineNr): Removed, it wasn't used. + (pop,push): Removed, replaced by ... + (maxpop,maxpush,delta): ... these, with slightly changed semantics. + (stackHeight): New variable. + (Block): Default Constructor doesn't initialize fields now. + (getCatchers): Renamed to ... + (getHandlers): ... this, changed all callers. + (initCode): Calculate maxpop, maxpush, delta correctly. + (getStackPopPush): Changed accordingly to new fields. + (setCode): Removed debugging output for illegal contents. + * net/sf/jode/bytecode/Classes.java: Reworked handling of inner + classes. + (innerClasses): Field mustn't be null anymore when loaded. + (setName): Update class in classpath. + * net/sf/jode/bytecode/ClassPath.java: + (renameClassInfo): new function, should only used by ClassInfo. + * net/sf/jode/bytecode/ConstantPool.java: made public. + (getUTF8,getRef,getClassType,getClassName): Don't allow the 0 index. + (iterateClassNames): New method. + * net/sf/jode/decompiler/Main.java: + (decompileClass): Catch ClassFormatExceptions and decompile + remaining classes. + * net/sf/jode/obfuscator/ClassIdentifier.java: + Updated handling of inner/extra classes to new ClassInfo behaviour. + (initSuperClasses): Load DECLARATION of super classes. + * net/sf/jode/obfuscator/PackageIdentifier.java: + Replace deprecated methods of ClassInfo with corresponding classpath + calls. + (loadMatchingClasses): Initialize packages loaded on demand if we + are initialize. + * net/sf/jode/obfuscator/modules/ConstantAnalyzer.java: + Now extends SimpleAnalyzer. + (canonizeIfaceRef): Removed; it is now inherited. + (canonizeRef): likewise. + Big updates to handle jsr correctly. + (handleOpcode): Moved method to BlockInfo. + * net/sf/jode/obfuscator/modules/SimpleAnalyzer.java: + (canonizeIfaceRef): New method, copied from ConstantAnalyzer. + (canonizeRef): call canonizeIfaceRef for interfaces. + * net/sf/jode/util/UnifyHash.java + (iterateHashCode): iterator now supports remove(). + (remove): New method. + 2001-07-30 Jochen Hoenicke Changed compilation procedure to ant. @@ -149,8 +219,8 @@ 2001-07-15 Jochen Hoenicke Applied patch from 2001-05-08 of Jode 1.1 tree: * jode/jvm/CodeVerifier.java (doVerify): Don't check for - uninitialized objects in local or stack slots on backwards jump or - exception blocks. Sun's jdk also doesn't check it, and I never + uninitialized objects in local or stack slots on backwards jump or + exception blocks. Sun's jdk also doesn't check it, and I never understood why it is necessary. But see JVM Spec 4.9.4. 2001-07-15 Jochen Hoenicke @@ -238,12 +308,12 @@ * jode/decompiler/Options.java (setOptions): changed gnu style to include GNU_SPACING. * jode/decompiler/ClassAnalyzer.java (dumpSource): Use - open/closeBraceClass. + open/closeBraceClass. * jode/decompiler/MethodAnalyzer.java (dumpSource): Use - open/closeBraceNoIndent. Call printOptionalSpace. + open/closeBraceNoIndent. Call printOptionalSpace. * jode/decompiler/InvokeOperator.java (dumpExpression): Call printOptionalSpace, use open/closeBraceClass for inner - classes. + classes. * jode/decompiler/UnaryOperator.java (dumpExpression): Call printOptionalSpace. @@ -252,21 +322,21 @@ * jode/decompiler/TabbedPrintWriter.java (BRACE_FLUSH_LEFT): new constant. (openBrace, openBraceContinue, closeBrace, closeBraceNoSpace, - closeBraceContinue): handle flush left. + closeBraceContinue): handle flush left. * jode/type/NullType.java (intersection): Removed, since the - version in ReferenceType is more correct. Before - tNull.isOfType(tRange(X,tNull)) returned false, which lead to - incorrect behaviour in InvokeOperator.needsCast. + version in ReferenceType is more correct. Before + tNull.isOfType(tRange(X,tNull)) returned false, which lead to + incorrect behaviour in InvokeOperator.needsCast. * jode/decompiler/FieldAnalyzer.java (dumpSource): Removed the - "= null" hack for final fields; it was not correct, since the - field could be initialized in a constructor. + "= null" hack for final fields; it was not correct, since the + field could be initialized in a constructor. * jode/decompiler/TabbedPrintWriter.java (BreakPoint.endOp): Simplified the code, copy options always from child. * jode/expr/InvokeOperator.java (isGetClass): Allow the method to - be declared inside an outer class: We simply check if we can get + be declared inside an outer class: We simply check if we can get the method analyzer. (simplify): handle unifyParam. * jode/expr/PopOperator.java (getBreakPenalty): return penalty of - inner expression. (dumpExpression): Call dumpExpression of - subexpression immediately without priority. + inner expression. (dumpExpression): Call dumpExpression of + subexpression immediately without priority. diff --git a/jode/INSTALL b/jode/INSTALL index 0fdb310..dfce9a9 100644 --- a/jode/INSTALL +++ b/jode/INSTALL @@ -1,45 +1,23 @@ Before installing, make sure you have at least version 1.1 of the java developement kit installed. If you want to run this program you only need the java runtime environment. Version 1.1 is quite old, I -recommend using Java 2 (jdk1.2 or above). - -This package was designed to use the GNU standard for configuration -and makefiles. To build and install do the following: - -0). If you have downloaded the code from the CVS repository create -configure and Makefile.in with autoconf-2.13 and automake-1.4. Type -"aclocal; automake -a; autoconf". - -1). You need a java development kit (at least version 1.1), some unix -tools and some java packages. Make sure that you have all java -packages that are needed in your classpath. This are gnu.getopt, and -if you have JDK 1.1 you also need the collection classes and swing for -1.1. These packages are accessible from the following urls: - - CYGWIN (unix tools for win95/NT): - http://sourceware.cygnus.com/cygwin/ - - JDK 1.1: - http://java.sun.com/products/jdk/1.1/index.htm - - Collection classes and Swing for JDK 1.1: - http://java.sun.com/beans/infobus/#DOWNLOAD_COLLECTIONS - http://java.sun.com/products/jfc/index.html#download-swing - - JDK 1.2: - http://java.sun.com/products/jdk/1.2/index.html - - Getopt: - http://www.urbanophile.com/arenn/hacking/download.html#getopt - -2). Run the "configure" script to configure the package. There are -various options you might want to pass to configure to control how the -package is built. "configure --help" will give a complete list. - -Use the --with-java option to specify the install directory of the jdk. - -If you have jikes, you should specify it with --with-jikes. You can -give a path to the directory where it resides, otherwise it is -searched in the path. - -3). Type "make" to build the package. +recommend using Java 2 (jdk1.2 or above). You need perl if you want +to compile a 1.1 version. + +This package was designed to use the ANT from the jakarta.apache.org +tools. I assume you have installed it correctly. + +Take some time to edit config.props. There are a few options you need +to take care of. (Unfortunately ant can't test for executables). + +Now you are ready to invoke ant. There are many possible targets, here +are the most useful ones: + +all builds class files and documentation. +build builds class files only (autodetects java version). +build-1.1 builds JDK1.1 class files. +doc builds documentation. +dist creates all release files. +test does some self tests. You need to have junit installed for this. +clean cleans everything that doesn't belong to the source distribution. +cvsclean cleans everything that doesn't belong into the cvs repository. diff --git a/jode/build.xml b/jode/build.xml index 9f545bb..f15fa07 100644 --- a/jode/build.xml +++ b/jode/build.xml @@ -19,52 +19,75 @@ --> - + - - - - - - + + + + + + + + + + + + + + + - + + + + - + - + - - - - - - - - - - + + + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + @@ -93,91 +116,191 @@ value="com.sun.java.util.collections" classname="com.sun.java.util.collections.Set" classpathref="project.classpath" /> - + - - + + + + - - + - - + - - - + + + + + + + + + + + + - - - - - - - - - - - + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + destdir="${api.doc}" + use="yes"> + + - + + + - - - + + + + + - - - + + + + + + - - - + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + - - - - @@ -185,13 +308,30 @@ + - + + + + diff --git a/jode/scripts/jcpp.pl b/jode/scripts/jcpp.pl index cef96f4..7f886f8 100755 --- a/jode/scripts/jcpp.pl +++ b/jode/scripts/jcpp.pl @@ -1,6 +1,6 @@ #!/usr/bin/perl -w # -# jcpp Copyright (C) 1999 Jochen Hoenicke. +# jcpp Copyright (C) 1999-2001 Jochen Hoenicke. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -39,8 +39,8 @@ # After running jcpp the false branch is commented out. If the true # branch was commented out it will get commented in. # -# jcpp also definitions, useful for package renaming. The java file -# must look like this: +# jcpp can also change definitions, useful for package renaming. The +# java file should look like this: # # ///#def COLLECTIONS java.util # import java.util.Vector diff --git a/jode/src/net/sf/jode/bytecode/BasicBlockReader.java b/jode/src/net/sf/jode/bytecode/BasicBlockReader.java index 8c6db86..7b65077 100644 --- a/jode/src/net/sf/jode/bytecode/BasicBlockReader.java +++ b/jode/src/net/sf/jode/bytecode/BasicBlockReader.java @@ -75,6 +75,9 @@ class BasicBlockReader implements Opcodes { BasicBlocks bb; Block[] blocks; + int maxStack; + int maxLocals; + public BasicBlockReader(BasicBlocks bb) { this.bb = bb; } @@ -298,13 +301,20 @@ class BasicBlockReader implements Opcodes { if (start != -1) convertBlock(start, count); bb.setBlocks(blocks, getSuccBlock(0), convertHandlers()); + if (bb.maxStack > maxStack) + throw new ClassFormatException("Only allocated "+maxStack + +" stack slots for method, needs " + +bb.maxStack); + if (bb.maxLocals > maxLocals) + throw new ClassFormatException("Only allocated "+maxLocals + +" local slots for method, needs " + +bb.maxLocals); } public void readCode(ConstantPool cp, DataInputStream input) throws IOException { - bb.maxStack = input.readUnsignedShort(); - int maxLocals = input.readUnsignedShort(); - bb.maxLocals = maxLocals; + maxStack = input.readUnsignedShort(); + maxLocals = input.readUnsignedShort(); int codeLength = input.readInt(); infos = new InstrInfo[codeLength]; diff --git a/jode/src/net/sf/jode/bytecode/BasicBlockWriter.java b/jode/src/net/sf/jode/bytecode/BasicBlockWriter.java index 0a7ec37..b820929 100644 --- a/jode/src/net/sf/jode/bytecode/BasicBlockWriter.java +++ b/jode/src/net/sf/jode/bytecode/BasicBlockWriter.java @@ -76,8 +76,10 @@ class BasicBlockWriter implements Opcodes { new LocalVariableInfo[blocks.length][]; int startBlockNr = startBlock.getBlockNr(); atStart[startBlockNr] = new LocalVariableInfo[bb.getMaxLocals()]; - for (int i=0; i < bb.getParamCount(); i++) - atStart[startBlockNr][i] = bb.getParamInfo(i); + for (int i=0; i < bb.getParamCount(); i++) { + LocalVariableInfo lvi = bb.getParamInfo(i); + atStart[startBlockNr][i] = lvi.getName() != null ? lvi : null; + } /* We currently ignore the jsr/ret issue. Should be okay, * though, since it can only generate a bit too much local @@ -94,36 +96,47 @@ class BasicBlockWriter implements Opcodes { if (instrs[i].hasLocal()) { LocalVariableInfo lvi = instrs[i].getLocalInfo(); int slot = lvi.getSlot(); - if (life[slot] != null - && life[slot] != lvi) - life[slot] = null; - - if (life[slot] == null - && lvi.getName() != null) - life[slot] = lvi; + life[slot] = lvi.getName() != null ? lvi : null; } } Block[] succs = block.getSuccs(); - if (succs != null) { - for (int j = 0; j < succs.length; j++) { - if (succs[j] == null) - continue; - int succNr = succs[j].getBlockNr(); - if (atStart[succNr] == null) { - atStart[succNr] = (LocalVariableInfo[]) life.clone(); + for (int j = 0; j < succs.length; j++) { + if (succs[j] == null) + continue; + int succNr = succs[j].getBlockNr(); + if (atStart[succNr] == null) { + atStart[succNr] = (LocalVariableInfo[]) life.clone(); + todo.push(succs[j]); + } else { + boolean changed = false; + for (int k = 0; k < life.length; k++) { + if (atStart[succNr][k] != life[k] + && atStart[succNr][k] != null) { + atStart[succNr][k] = null; + changed = true; + } + } + if (changed && !todo.contains(succs[j])) todo.push(succs[j]); - } else { - boolean changed = false; - for (int k = 0; k < life.length; k++) { - if (atStart[succNr][k] != life[k] - && atStart[succNr][k] != null) { - atStart[succNr][k] = null; - changed = true; - } + } + } + Handler[] handlers = block.getHandlers(); + for (int j = 0; j < handlers.length; j++) { + int succNr = handlers[j].getCatcher().getBlockNr(); + if (atStart[succNr] == null) { + atStart[succNr] = (LocalVariableInfo[]) life.clone(); + todo.push(handlers[j].getCatcher()); + } else { + boolean changed = false; + for (int k = 0; k < life.length; k++) { + if (atStart[succNr][k] != life[k] + && atStart[succNr][k] != null) { + atStart[succNr][k] = null; + changed = true; } - if (changed && !todo.contains(succs[j])) - todo.push(succs[j]); } + if (changed && !todo.contains(handlers[j].getCatcher())) + todo.push(handlers[j].getCatcher()); } } } @@ -137,6 +150,7 @@ class BasicBlockWriter implements Opcodes { current[slot] = new LVTEntry(); current[slot].startAddr = 0; current[slot].lvi = lvi; + System.err.println("lvi at init,"+slot+": "+lvi); } } @@ -158,6 +172,7 @@ class BasicBlockWriter implements Opcodes { current[slot] = new LVTEntry(); current[slot].startAddr = addr; current[slot].lvi = atStart[i][slot]; + System.err.println("lvi at "+i+","+slot+": "+current[slot].lvi); } } @@ -178,6 +193,7 @@ class BasicBlockWriter implements Opcodes { current[slot] = new LVTEntry(); current[slot].startAddr = addr; current[slot].lvi = lvi; + System.err.println("lvi at "+i+","+k+","+slot+": "+current[slot].lvi); } } addr += instrLength[i][k]; @@ -222,8 +238,8 @@ class BasicBlockWriter implements Opcodes { gotos[0] = startBlock.getBlockNr(); } - next_block: for (int i = 0; i < blocks.length; i++) { + boolean hasDefaultSucc = true; blockAddr[i] = addr; Instruction[] instrs = blocks[i].getInstructions(); instrLength[i] = new int[instrs.length]; @@ -265,7 +281,7 @@ class BasicBlockWriter implements Opcodes { } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { length = 3; - break switch_opc; + break switch_opc; } } if (gcp.putConstant(constant) < 256) { @@ -304,30 +320,28 @@ class BasicBlockWriter implements Opcodes { length = 2; else length = 4; - gotos[i+1] = -2; + hasDefaultSucc = false; break; } case opc_lookupswitch: { - length = 3-(addr % 4); + length = (~addr) & 3; /* padding */ int[] values = instr.getValues(); int npairs = values.length; for (int k=0; k< succs.length; k++) { if (succs[k] == null) needRet = true; } - if (npairs > 0) { - int tablesize = values[npairs-1] - values[0] + 1; - if (4 + tablesize * 4 < 8 * npairs) { - // Use a table switch - length += 13 + 4 * tablesize; - break; - } + if (npairs > 0 + && 4 + 4 * (values[npairs-1] - values[0] + 1) + <= 8 * npairs) { + // Use a table switch + length += 13 + 4 * (values[npairs-1] - values[0] + 1); + } else { + // Use a lookup switch + length += 9 + 8 * npairs; } - // Use a lookup switch - length += 9 + 8 * npairs; - // The goto is inclusive through the default part. - gotos[i+1] = -2; - continue next_block; + hasDefaultSucc = false; + break; } case opc_jsr: conds[i+1] = succs[0].getBlockNr(); @@ -391,8 +405,8 @@ class BasicBlockWriter implements Opcodes { case opc_freturn: case opc_dreturn: case opc_areturn: case opc_return: case opc_athrow: - gotos[i+1] = -2; length = 1; + hasDefaultSucc = false; break; case opc_nop: case opc_iaload: case opc_laload: case opc_faload: @@ -434,20 +448,26 @@ class BasicBlockWriter implements Opcodes { instrLength[i][j] = length; addr += length; } - Block defaultSucc = succs[succs.length-1]; - if (defaultSucc == null) { - // This is a return - gotos[i+1] = -1; - isRet.set(i+1); - lastRetAddr = addr; - addr++; - } else if (defaultSucc.getBlockNr() == i + 1) { - // no need for any jump - gotos[i+1] = succs[succs.length-1].getBlockNr(); + if (hasDefaultSucc) { + Block defaultSucc = succs[succs.length-1]; + if (defaultSucc == null) { + // This is a return + gotos[i+1] = -1; + isRet.set(i+1); + lastRetAddr = addr; + hasRet = true; + addr++; + } else if (defaultSucc.getBlockNr() == i + 1) { + // no need for any jump + gotos[i+1] = succs[succs.length-1].getBlockNr(); + } else { + // Reserve space for a normal goto. + gotos[i+1] = succs[succs.length-1].getBlockNr(); + addr += 3; + } } else { - // Reserve space for a normal goto. - gotos[i+1] = succs[succs.length-1].getBlockNr(); - addr += 3; + // No goto needed for this block + gotos[i+1] = -2; } } if (needRet && !hasRet) { @@ -470,9 +490,9 @@ class BasicBlockWriter implements Opcodes { int from = blockAddr[i] - 3; if (gotoNr != i + 1) from -= isRet.get(i) ? 1 : isWide.get(i) ? 5 : 3; - int dist; + int dist = Integer.MAX_VALUE; if (condNr == -1) { - if (!retAtEnd) { + if (retAtEnd) { dist = blockAddr[blockAddr.length-1] - 1 - from; } else { for (int j = 0; j < gotos.length; j++) { @@ -483,7 +503,8 @@ class BasicBlockWriter implements Opcodes { break; } } - throw new InternalError(); + if (dist == Integer.MAX_VALUE) + throw new InternalError(); } } else { dist = blockAddr[condNr] - from; @@ -502,7 +523,7 @@ class BasicBlockWriter implements Opcodes { blockAddr[j] += diff; changed = true; } - } + } if (!isWide.get(i) && gotoNr >= 0) { int dist = blockAddr[gotoNr] - blockAddr[i] + 3; if (dist < Short.MIN_VALUE || dist > Short.MAX_VALUE) { @@ -565,6 +586,7 @@ class BasicBlockWriter implements Opcodes { gcp.putUTF8("LocalVariableTable"); int count = lvt.length; for (int i=0; i < count; i++) { + System.err.println("lvt: "+lvt[i].lvi); gcp.putUTF8(lvt[i].lvi.getName()); gcp.putUTF8(lvt[i].lvi.getType()); } @@ -590,13 +612,12 @@ class BasicBlockWriter implements Opcodes { output.writeShort(lvt[i].lvi.getSlot()); } } - if (lnt != null) { + if (lntCount > 0) { output.writeShort(gcp.putUTF8("LineNumberTable")); - int count = lnt.length / 2; - int length = 2 + 4 * count; + int length = 2 + 4 * lntCount; output.writeInt(length); - output.writeShort(count); - for (int i=0; i < count; i++) { + output.writeShort(lntCount); + for (int i = 0; i < lntCount; i++) { output.writeShort(lnt[2*i]); output.writeShort(lnt[2*i+1]); } @@ -629,11 +650,11 @@ class BasicBlockWriter implements Opcodes { } int lntPtr = 0; - next_block: for (int i = 0; i< blocks.length; i++) { + boolean hasDefaultSucc = true; Block[] succs = blocks[i].getSuccs(); if (addr != blockAddr[i]) - throw new InternalError("Address calculation broken!"); + throw new InternalError("Address calculation broken for "+i+": "+blockAddr[i]+"!="+addr+"!"); Instruction[] instructions = blocks[i].getInstructions(); int size = instructions.length; for (int j = 0; j < size; j++) { @@ -679,7 +700,8 @@ class BasicBlockWriter implements Opcodes { output.writeByte(opcode); output.writeShort(slot); } - continue next_block; + hasDefaultSucc = false; + break; } case opc_ldc: case opc_ldc2_w: { @@ -782,18 +804,19 @@ class BasicBlockWriter implements Opcodes { } else { int dist; if (dest == null) { - if (!retAtEnd) { + if (retAtEnd) { dist = blockAddr[blocks.length] - 1 - addr; } else { - for (int k = 0; k < blocks.length + 1; k++) { + for (int k = 0; ; k++) { if (isRet.get(k)) { dist = blockAddr[k] - 1 - addr; if (dist >= Short.MIN_VALUE && dist <= Short.MAX_VALUE) break; } + if (k == blocks.length) + throw new InternalError(); } - throw new InternalError(); } } else { dist = blockAddr[dest.getBlockNr()] - addr; @@ -814,7 +837,7 @@ class BasicBlockWriter implements Opcodes { if (npairs > 0) { int tablesize = values[npairs-1] - values[0] + 1; - if (4 + tablesize * 4 < 8 * npairs) { + if (4 + tablesize * 4 <= 8 * npairs) { // Use a table switch output.writeByte(opc_tableswitch); output.write(new byte[align]); @@ -832,7 +855,8 @@ class BasicBlockWriter implements Opcodes { : blockAddr[succs[k].getBlockNr()]; output.writeInt(dest - addr); } - continue next_block; + hasDefaultSucc = false; + break; } } // Use a lookup switch @@ -847,7 +871,8 @@ class BasicBlockWriter implements Opcodes { : blockAddr[succs[k].getBlockNr()]; output.writeInt(dest - addr); } - continue next_block; + hasDefaultSucc = false; + break; } case opc_getstatic: @@ -903,7 +928,9 @@ class BasicBlockWriter implements Opcodes { case opc_ireturn: case opc_lreturn: case opc_freturn: case opc_dreturn: case opc_areturn: case opc_athrow: case opc_return: - continue next_block; + output.writeByte(opcode); + hasDefaultSucc = false; + break; case opc_nop: case opc_iaload: case opc_laload: case opc_faload: @@ -944,19 +971,23 @@ class BasicBlockWriter implements Opcodes { } addr += instrLength[i][j]; } - // Check which type of goto we should use at end of this block. - Block defaultSucc = succs[succs.length - 1]; - if (isRet.get(i+1)) { - output.writeByte(opc_return); - addr++; - } else if (isWide.get(i+1)) { - output.writeByte(opc_goto_w); - output.writeInt(blockAddr[defaultSucc.getBlockNr()] - addr); - addr+=5; - } else if (defaultSucc.getBlockNr() != i+1) { - output.writeByte(opc_goto); - output.writeShort(blockAddr[defaultSucc.getBlockNr()] - addr); - addr+=3; + if (hasDefaultSucc) { + // Check which type of goto we should use at end of this block. + Block defaultSucc = succs[succs.length - 1]; + if (isRet.get(i+1)) { + output.writeByte(opc_return); + addr++; + } else if (isWide.get(i+1)) { + output.writeByte(opc_goto_w); + output.writeInt(blockAddr[defaultSucc.getBlockNr()] + - addr); + addr+=5; + } else if (defaultSucc.getBlockNr() != i+1) { + output.writeByte(opc_goto); + output.writeShort(blockAddr[defaultSucc.getBlockNr()] + - addr); + addr+=3; + } } } if (retAtEnd) { @@ -977,4 +1008,3 @@ class BasicBlockWriter implements Opcodes { } } } - diff --git a/jode/src/net/sf/jode/bytecode/BasicBlocks.java b/jode/src/net/sf/jode/bytecode/BasicBlocks.java index dd0812b..8975096 100644 --- a/jode/src/net/sf/jode/bytecode/BasicBlocks.java +++ b/jode/src/net/sf/jode/bytecode/BasicBlocks.java @@ -26,6 +26,8 @@ import java.io.DataOutputStream; import java.io.IOException; import java.io.PrintWriter; +import java.util.BitSet; +import java.util.Stack; ///#def COLLECTIONS java.util import java.util.ArrayList; import java.util.Arrays; @@ -68,8 +70,9 @@ import java.lang.UnsupportedOperationException; * necessary, you don't have to care about that.

* * @see net.sf.jode.bytecode.Block - * @see net.sf.jode.bytecode.Instruction */ -public class BasicBlocks extends BinaryInfo { + * @see net.sf.jode.bytecode.Instruction + */ +public class BasicBlocks extends BinaryInfo implements Opcodes { /** * The method info which contains the basic blocks. @@ -153,12 +156,84 @@ public class BasicBlocks extends BinaryInfo { return paramInfos.length; } - public void setMaxStack(int ms) { - maxStack = ms; - } + /** + * Updates the maxStack and maxLocals according to the current code. + * Call this every time you change the code. + */ + public void updateMaxStackLocals() { + maxLocals = getParamCount(); + maxStack = 0; - public void setMaxLocals(int ml) { - maxLocals = ml; + if (startBlock == null) + return; + + BitSet visited = new BitSet(); + Stack todo = new Stack(); + int[] poppush = new int[2]; + + startBlock.stackHeight = 0; + todo.push(startBlock); + while (!todo.isEmpty()) { + Block block = (Block) todo.pop(); + int stackHeight = block.stackHeight; + if (stackHeight + block.maxpush > maxStack) + maxStack = stackHeight + block.maxpush; + stackHeight += block.delta; + + Block[] succs = block.getSuccs(); + Instruction[] instr = block.getInstructions(); + for (int i = 0; i < instr.length; i++) { + if (instr[i].hasLocal()) { + int slotlimit = instr[i].getLocalSlot() + 1; + int opcode = instr[i].getOpcode(); + if (opcode == opc_lstore || opcode == opc_dstore + || opcode == opc_lload || opcode == opc_dload) + slotlimit++; + if (slotlimit > maxLocals) + maxLocals = slotlimit; + } + } + if (instr.length > 0 + && instr[instr.length-1].getOpcode() == opc_jsr) { + if (!visited.get(succs[0].blockNr)) { + succs[0].stackHeight = stackHeight + 1; + todo.push(succs[0]); + visited.set(succs[0].blockNr); + } else if (succs[0].stackHeight != stackHeight + 1) + throw new IllegalArgumentException + ("Block has two different stack heights."); + + if (succs[1] != null && !visited.get(succs[1].blockNr)) { + succs[1].stackHeight = stackHeight; + todo.push(succs[1]); + visited.set(succs[1].blockNr); + } else if ((succs[1] == null ? 0 : succs[1].stackHeight) + != stackHeight) + throw new IllegalArgumentException + ("Block has two different stack heights."); + } else { + for (int i = 0; i < succs.length; i++) { + if (succs[i] != null && !visited.get(succs[i].blockNr)) { + succs[i].stackHeight = stackHeight; + todo.push(succs[i]); + visited.set(succs[i].blockNr); + } else if ((succs[i] == null ? 0 : succs[i].stackHeight) + != stackHeight) + throw new IllegalArgumentException + ("Block has two different stack heights."); + } + } + Handler[] handler = block.getHandlers(); + for (int i = 0; i < handler.length; i++) { + if (!visited.get(handler[i].getCatcher().blockNr)) { + handler[i].getCatcher().stackHeight = 1; + todo.push(handler[i].getCatcher()); + visited.set(handler[i].getCatcher().blockNr); + } else if (handler[i].getCatcher().stackHeight != 1) + throw new IllegalArgumentException + ("Block has two different stack heights."); + } + } } public void setBlocks(Block[] blocks, Block startBlock, @@ -173,15 +248,18 @@ public class BasicBlocks extends BinaryInfo { for (int j = 0; j < handlers.length; j++) { if (handlers[j].getStart() == blocks[i]) activeHandlers.add(handlers[j]); - if (handlers[j].getEnd() == blocks[i]) - activeHandlers.remove(handlers[j]); } if (activeHandlers.size() == 0) blocks[i].catchers = Handler.EMPTY; else blocks[i].catchers = (Handler[]) activeHandlers.toArray(Handler.EMPTY); + for (int j = 0; j < handlers.length; j++) { + if (handlers[j].getEnd() == blocks[i]) + activeHandlers.remove(handlers[j]); + } } + updateMaxStackLocals(); // TransformSubroutine.createSubroutineInfo(this); } @@ -212,7 +290,7 @@ public class BasicBlocks extends BinaryInfo { dumpCode(GlobalOptions.err); } - void readAttribute(String name, int length, ConstantPool cp, + protected void readAttribute(String name, int length, ConstantPool cp, DataInputStream input, int howMuch) throws IOException { if (howMuch >= ClassInfo.ALMOSTALL @@ -256,15 +334,17 @@ public class BasicBlocks extends BinaryInfo { BasicBlockWriter bbw; void prepareWriting(GrowableConstantPool gcp) { bbw = new BasicBlockWriter(this, gcp); + prepareAttributes(gcp); } - int getKnownAttributeCount() { - return bbw.getAttributeCount(); + protected int getAttributeCount() { + return super.getAttributeCount() + bbw.getAttributeCount(); } - void writeKnownAttributes(GrowableConstantPool gcp, - DataOutputStream output) + protected void writeAttributes(GrowableConstantPool gcp, + DataOutputStream output) throws IOException { + super.writeAttributes(gcp, output); bbw.writeAttributes(gcp, output); } diff --git a/jode/src/net/sf/jode/bytecode/BinaryInfo.java b/jode/src/net/sf/jode/bytecode/BinaryInfo.java index 8989219..9ffa1fd 100644 --- a/jode/src/net/sf/jode/bytecode/BinaryInfo.java +++ b/jode/src/net/sf/jode/bytecode/BinaryInfo.java @@ -45,8 +45,15 @@ import java.util.Iterator; * this package as appropriate. This methods are only useful for non * standard attributes.

* - *

One application of this attributes are installation classes. - * These classes have a special attribute containing a zip of the + *

You can provide new attributes by overriding the protected + * methods of this class. This makes it possible to use constant pool + * entries in the attributes.

+ * + *

Another possibility is to add the attributes with the public + * method. This way you don't need to extend the classes, but you + * can't use a constant pool for the contents of the attributes. One + * possible application of this are installation classes. These + * classes have a special attribute containing a zip archive of the * files that should be installed. There are other possible uses, * e.g. putting native machine code for some architectures into the * class.

@@ -70,14 +77,22 @@ public class BinaryInfo { } } - int getKnownAttributeCount() { - return 0; - } - - void readAttribute(String name, int length, - ConstantPool constantPool, - DataInputStream input, - int howMuch) throws IOException { + /** + * Reads in an attributes of this class. Overwrite this method if + * you want to handle your own attributes. If you don't know how + * to handle an attribute call this method for the super class. + * @param name the attribute name. + * @param length the length of the attribute. + * @param constantPool the constant pool of the class. + * @param input a data input stream where you can read the attribute + * from. It will protect you to read more over the attribute boundary. + * @param howMuch the constant that was given to the {@link + * ClassInfo#load} function when loading this class. + */ + protected void readAttribute(String name, int length, + ConstantPool constantPool, + DataInputStream input, + int howMuch) throws IOException { byte[] data = new byte[length]; input.readFully(data); if (howMuch >= ClassInfo.ALL) { @@ -156,12 +171,37 @@ public class BinaryInfo { } } - void drop(int keep) { + /** + * Drops information from this info. Override this to drop your + * own info and don't forget to call the method of the super class. + * @param howMuch the constant that was given to the {@link + * ClassInfo#drop} function when loading this class. + */ + protected void drop(int keep) { if (keep < ClassInfo.ALL) unknownAttributes = null; } - void prepareAttributes(GrowableConstantPool gcp) { + /** + * Returns the number of attributes of this class. Overwrite this + * method if you want to add your own attributes by providing a + * writeAttributes method. You should call this method for the + * super class and add the number of your own attributes to the + * returned value. + * @return the number of attributes of this class. + */ + protected int getAttributeCount() { + return unknownAttributes != null ? unknownAttributes.size() : 0; + } + + /** + * Prepare writing your attributes. Overwrite this method if you + * want to add your own attributes, which need constants on the + * class pool. Add the necessary constants to the constant pool + * and call this method for the super class. + * @param gcp The growable constant pool. + */ + protected void prepareAttributes(GrowableConstantPool gcp) { if (unknownAttributes == null) return; Iterator i = unknownAttributes.keySet().iterator(); @@ -169,19 +209,27 @@ public class BinaryInfo { gcp.putUTF8((String) i.next()); } - void writeKnownAttributes - (GrowableConstantPool constantPool, - DataOutputStream output) throws IOException { - } - - void writeAttributes + /** + *

Writes the attributes to the output stream. + * Overwrite this method if you want to add your own attributes. + * All constants you need from the growable constant pool must + * have been previously registered by the {@link #prepareAttributes} + * method.

+ * + * First call the method of the super class. Afterwrites write + * each of your own attributes including the attribute header + * (name and length entry). + * + * @param gcp The growable constant pool, which is not growable anymore. + * @param output the data output stream. You must write exactly + * as many bytes to it as you have told with the {@link + * #getAttributeSize} method. + */ + protected void writeAttributes (GrowableConstantPool constantPool, DataOutputStream output) throws IOException { - int count = getKnownAttributeCount(); - if (unknownAttributes != null) - count += unknownAttributes.size(); + int count = getAttributeCount(); output.writeShort(count); - writeKnownAttributes(constantPool, output); if (unknownAttributes != null) { Iterator i = unknownAttributes.entrySet().iterator(); while (i.hasNext()) { @@ -195,7 +243,19 @@ public class BinaryInfo { } } - int getAttributeSize() { + /** + * Gets the total length of all attributes in this binary info. + * Overwrite this method if you want to add your own attributes + * and add the size of your attributes to the value returned by + * the super class.
+ * + * Currently you only need to write this if you extend + * BasicBlocks. + * + * @return the total length of all attributes, including their + * headers and the number of attributes field. + */ + protected int getAttributeSize() { int size = 2; /* attribute count */ if (unknownAttributes != null) { Iterator i = unknownAttributes.values().iterator(); @@ -206,9 +266,12 @@ public class BinaryInfo { } /** - * Finds a non standard attribute with the given name. + * Finds a non standard attribute with the given name. You don't + * have access to the constant pool. Instead extend this class + * and override readAttribute method if you need the pool. * @param name the name of the attribute. * @return the contents of the attribute, null if not found. + * @see #readAttribute */ public byte[] findAttribute(String name) { if (unknownAttributes != null) @@ -217,7 +280,10 @@ public class BinaryInfo { } /** - * Gets all non standard attributes + * Gets all non standard attributes. + * @return an iterator for all attributes. The values returned by + * the next() method of the iterator are of byte[] type. + * @see #findAttribute */ public Iterator getAttributes() { if (unknownAttributes != null) @@ -226,19 +292,24 @@ public class BinaryInfo { } /** - * Adds a new non standard attribute or replaces an old one with the - * same name. + * Adds a new non standard attribute or replaces an old one with + * the same name. If it already exists, it will be overwritten. + * Note that there's now way to correlate the contents with a + * constant pool. If you need that extend this class and override + * the methods {@link #getAttributeCount}, {@link + * #prepareAttributes}, {@link #writeAttributes}, and {@link + * #getAttributeSize}. * @param name the name of the attribute. * @param contents the new contens. */ - public void setAttribute(String name, byte[] contents) { + public void addAttribute(String name, byte[] contents) { if (unknownAttributes == null) unknownAttributes = new SimpleMap(); unknownAttributes.put(name, contents); } /** - * Removes a new non standard attribute. + * Removes a non standard attributes. * @param name the name of the attribute. * @return the old contents of the attribute. */ @@ -249,11 +320,9 @@ public class BinaryInfo { } /** - * Removes all non standard attribute. + * Removes all non standard attributes. */ public void removeAllAttributes() { unknownAttributes = null; } } - - diff --git a/jode/src/net/sf/jode/bytecode/Block.java b/jode/src/net/sf/jode/bytecode/Block.java index 3337aee..88de266 100644 --- a/jode/src/net/sf/jode/bytecode/Block.java +++ b/jode/src/net/sf/jode/bytecode/Block.java @@ -92,28 +92,34 @@ public final class Block { int blockNr; /** - * The blockNr of this block. Set by BasicBlocks. + * The number of items this block takes from the stack with + * respect to the stack items at the beginning of the block. */ - int lineNr; - + int maxpop; /** - * The number of items this block takes from the stack. + * The maximum number of items the stack may grow. */ - int pop; + int maxpush; /** - * The number of items this block puts on the stack. + * The difference stack items after the block minus stack items + * before block. */ - int push; - + int delta; + /** + * The stack height at the beginning of this block. + * Only valid after the block was inserted in a BasicBlocks and + * the updateMaxStackLocals() of BasicBlocks was called. + */ + int stackHeight; /** - * Creates a new empty block, with a null successor array. + * Creates a new block uninitialized block. You mustn't really + * use it (except as successor for other blocks) until you have + * set the code. */ public Block() { - instrs = new Instruction[0]; - succs = null; } - + /** * Gets the list of instructions. The returned list should not be * modified, except that the instructions (but not their opcodes) @@ -134,12 +140,13 @@ public final class Block { } /** - * Gets the exception handlers which try part contains this block. - * You can't set them since they are calculated automatically. + * Gets the exception handlers whose try region contains this + * block. You can't set them since they are calculated + * automatically. * @return the exception handlers. - * @see BasicBlocks#setExceptionHandlers + * @see BasicBlocks#setBlocks */ - public Handler[] getCatchers() { + public Handler[] getHandlers() { return catchers; } @@ -158,15 +165,18 @@ public final class Block { private void initCode() { int size = instrs.length; + maxpop = maxpush = 0; int depth = 0; int poppush[] = new int[2]; boolean needGoto = true; for (int i = 0; i < size; i++) { instrs[i].getStackPopPush(poppush); - depth += poppush[0]; - if (pop < depth) - pop = depth; - depth -= poppush[1]; + depth -= poppush[0]; + if (maxpop < -depth) + maxpop = -depth; + depth += poppush[1]; + if (maxpush < depth) + maxpush = depth; int opcode = instrs[i].getOpcode(); switch (opcode) { @@ -176,7 +186,7 @@ public final class Block { case Opcodes.opc_lookupswitch: if (succs.length != instrs[i].getValues().length + 1) throw new IllegalArgumentException - ("no successors for switch"); + ("number of successors for switch doesn't match"); if (i != size - 1) throw new IllegalArgumentException ("switch in the middle!"); @@ -217,14 +227,14 @@ public final class Block { needGoto = false; } } - push = pop - depth; + delta = depth; if (needGoto && succs.length != 1) throw new IllegalArgumentException("no single successor block"); } public void getStackPopPush (int[] poppush) { - poppush[0] = pop; - poppush[1] = push; + poppush[0] = maxpop; + poppush[1] = delta + maxpop; return; } @@ -235,12 +245,7 @@ public final class Block { public void setCode(Instruction[] instrs, Block[] succs) { this.instrs = instrs; this.succs = succs; - try { - initCode(); - } catch (IllegalArgumentException ex) { - dumpCode(net.sf.jode.GlobalOptions.err); - throw ex; - } + initCode(); } public void dumpCode(PrintWriter output) { diff --git a/jode/src/net/sf/jode/bytecode/ClassInfo.java b/jode/src/net/sf/jode/bytecode/ClassInfo.java index b8fb9fd..f3dd540 100644 --- a/jode/src/net/sf/jode/bytecode/ClassInfo.java +++ b/jode/src/net/sf/jode/bytecode/ClassInfo.java @@ -59,13 +59,13 @@ import java.lang.reflect.Modifier; * now have two different possibilities to fill it with informations: * You load the class from its classpath (from which it was created) * or you build it from scratch by setting its contents with the - * various setXXX methods. + * various setSomething methods. * *

Changing a class

* Even if the class is already filled with information you can change * it. You can, for example, set another array of methods, change the * modifiers, or rename the class. Use the various - * setXXX methods. + * setSomething methods. * *

The components of a class

* A class consists of several components: @@ -79,7 +79,7 @@ import java.lang.reflect.Modifier; *
class name
* The short java name of this class, i.e. the name that appears * behind the "class" keyword in the java source file. While - * getClassName() also works for top level classes, + * getClassName() also works for package scope classes, * setClassName() must only be called on inner classes and will not * change the bytecode name.
* @@ -121,22 +121,17 @@ import java.lang.reflect.Modifier; * Fields are represented as {@link MethodInfo} objects. *
*
method scoped
- * True if this class is an anonymous or method scoped class. + * A boolean value; true if this class is an anonymous or method + * scoped class. *
*
outer class
- * the class of which this class is the inner. It returns null for - * top level classes and for method scoped classes.
+ * the class of which this class is the inner class. It returns + * null for package scoped and method scoped classes.
*
*
classes
* the inner classes which is an array of ClassInfo. This doesn't * include method scoped classes.
*
- *
extra classes
- * this field tells only, for which additional classes the class - * files contains an InnerClass Attribute. This can be the method - * scoped classes or the inner inner classes, but there is no - * promise.
- *
*
source file
* The name of source file. The JVM uses this field when a stack * trace is produced. @@ -155,16 +150,16 @@ import java.lang.reflect.Modifier; * getOuterClass() returns null and * isMethodScoped() returns false. *
- *
inner class scoped classes
+ *
class scoped classes (inner classes)
* A class is class scoped if, and only if * getOuterClass() returns not null. * * The bytecode name (getName()) of an inner class is - * most times of the form Package.Outer$Inner. But + * in most cases of the form Package.Outer$Inner. But * ClassInfo also supports differently named classes, as long as the * InnerClass attribute is present. The method * getClassName() returns the name of the inner class - * (Inner in the above example). + * (Inner in the above example). * * You can get all inner classes of a class with the * getClasses method. @@ -177,12 +172,13 @@ import java.lang.reflect.Modifier; * too.

* * The bytecode name (getName()) of an method scoped class is - * most times of the form Package.Outer$Number$Inner. But + * in most cases of the form Package.Outer$Number$Inner. But * ClassInfo also supports differently named classes, as long as the * InnerClass attribute is present.

* * There's no way to get the method scoped classes of a method, except - * by analyzing its instructions. + * by analyzing its instructions. And even that is error prone, since + * it could just be a method scoped class of an outer method. *
*
anonymous classes
* A class is an anonymous class if, and only if @@ -191,18 +187,20 @@ import java.lang.reflect.Modifier; * case getOuterClass() returns null, * too.

* - * The bytecode name (getName()) of an method scoped class is - * most times of the form Package.Outer$Number. But - * ClassInfo also supports differently named classes, as long as the - * InnerClass attribute is present.

+ * The bytecode name (getName()) of an method scoped + * class is in most cases of the form + * Package.Outer$Number. But ClassInfo also supports + * differently named classes, as long as the InnerClass attribute is + * present.

* * There's no way to get the anonymous classes of a method, except - * by analyzing its instructions. + * by analyzing its instructions. And even that is error prone, since + * it could just be an anonymous class of an outer method. *
* * *
- *

open questions...

+ *

Open Questions

* * I represent most types as java/lang/String (type * signatures); this is convenient since java bytecode does the same. @@ -211,17 +209,13 @@ import java.lang.reflect.Modifier; * method to convert to it, but I need a ClassPath for this. Should * the method be in ClassInfo (I don't think so), should an instance * of TypeSignature have a ClassPath as member variable, or should - * getClassInfo() take a ClassPath parameter? What about arrays? + * getClassInfo() take a ClassPath parameter as it is currently? + * What about arrays, shall we support special ClassInfo's for them, + * as java.lang.Class does? I think the current solution is okay. *
* - * Should load(HIERARCHY) also try to load hierarchy of super class. - * If yes, should it return an IOException if super class can't be - * found? Or should getSuperclass throw the IOException - * instead (I don't like that).
- * Current Solution: superClassOf and - * implementedBy can throw an IOException, getSuperclass not. - * - * @author Jochen Hoenicke */ + * @author Jochen Hoenicke + */ public final class ClassInfo extends BinaryInfo implements Comparable { private static ClassPath defaultClasspath; @@ -245,6 +239,8 @@ public final class ClassInfo extends BinaryInfo implements Comparable { private String sourceFile; private boolean hasInnerClassesAttr; + private final static ClassInfo[] EMPTY_INNER = new ClassInfo[0]; + /** * This constant can be used as parameter to drop. It specifies * that no information at all should be kept for the current class. @@ -419,10 +415,24 @@ public final class ClassInfo extends BinaryInfo implements Comparable { if (status >= OUTERCLASS) { if ((className == null ? this.className != null : !className.equals(this.className)) - || this.outerClass != outer) - throw new ClassFormatException("Outer information mismatch " - +name+": "+className+","+outer+","+ms+"<->"+this.className +","+this.outerClass+","+this.methodScoped); - mergeModifiers(realModifiers); + || this.outerClass != outer) { + /* Ignore errors when merging, some obfuscator may have + * stripped InnerClasses attributes + */ + if (this.className == null && this.outerClass == null + && (className != null || outer != null)) { + this.outerClass = outer; + this.className = className; + this.methodScoped = ms; + } else if (className != null || outer != null) { + GlobalOptions.err.println + ("WARNING: Outer information mismatch " + +name+": "+className+","+outer+","+ms+"<->" + +this.className +","+this.outerClass+","+this.methodScoped); + } + } + if (realModifiers != -1) + mergeModifiers(realModifiers); } else { if (realModifiers != -1) mergeModifiers(realModifiers); @@ -438,37 +448,16 @@ public final class ClassInfo extends BinaryInfo implements Comparable { throws IOException { /* The InnerClasses attribute is transformed in a special way - * so we want to taker a closer look. According to the inner - * class specification, - * - * http://java.sun.com/products/jdk/1.1/docs/guide/innerclasses/spec/innerclasses.doc10.html#18814 - * - * there are several InnerClass records in a class. We differ - * three different types: - * - * The InnerClass records for our own inner classes: They can - * easily be recognized, since this class must be mentioned in - * the outer_class_info_index field. - * - * The InnerClass records for the outer class and its outer - * classes, and so on: According to the spec, these must - * always be present if this is a class scoped class. And they - * must be in reversed order, i.e. the outer most class comes - * first. + * so we want to taker a closer look. According to the 2nd + * edition of the vm specification (InnerClasses attribute), * - * Some other InnerClass records, the extra classes. This is - * optional, but we don't want to loose this information if we - * just transform classes, so we memorize for which classes we - * have to keep the information anyway. + * http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#79996 * - * Currently we don't use all informations, since we don't - * update the information for inner/outer/extra classes or - * check it for consistency. This might be bad, since this - * means that - *
-	 * load(ALL); write(new File())
-	 * 
- * doesn't work. + * there is a InnerClass record for each non package scope + * class referenced in this class. We are only interested in + * out own entry and in the entries for our inner classes. + * The latter can easily be recognized, since this class must + * be mentioned in the outer_class_info_index field. */ hasInnerClassesAttr = true; @@ -478,14 +467,14 @@ public final class ClassInfo extends BinaryInfo implements Comparable { throw new ClassFormatException ("InnerClasses attribute has wrong length"); - int innerCount = 0, outerCount = 0, extraCount = 0; + int innerCount = 0; /** * The first part will contain the inner classes, the last * part the extra classes. */ - ClassInfo[] innerExtra = new ClassInfo[count]; + ClassInfo[] innerCIs = new ClassInfo[count]; - for (int i=0; i < count; i++) { + for (int i = 0; i < count; i++) { int innerIndex = input.readUnsignedShort(); int outerIndex = input.readUnsignedShort(); int nameIndex = input.readUnsignedShort(); @@ -497,12 +486,17 @@ public final class ClassInfo extends BinaryInfo implements Comparable { if (innername != null && innername.length() == 0) innername = null; - /* Some compilers give method scope classes a valid - * outer field, but we mustn't handle them as inner - * classes. The best way to distinguish this case - * is by the class name. + /* Some compilers give method scope and anonymous classes + * a valid outer field, but we mustn't handle them as + * inner classes. */ - if (outer != null && innername != null + if (innername == null) + outer = null; + + /* The best way to distinguish method scope classes is by thier + * class name. + */ + if (outer != null && inner.length() > outer.length() + 2 + innername.length() && inner.startsWith(outer+"$") && inner.endsWith("$"+innername) @@ -510,45 +504,13 @@ public final class ClassInfo extends BinaryInfo implements Comparable { outer = null; ClassInfo innerCI = classpath.getClassInfo(inner); - ClassInfo outerCI = null; - if (outer != null) { - outerCI = classpath.getClassInfo(outer); - /* If we didn't find an InnerClasses info for outerCI, yet, - * this means that it doesn't have an outer class. So we - * know its (empty) outer class status now. - */ - if (outerCI.status < OUTERCLASS) - outerCI.mergeOuterInfo(null, null, -1, false); - } - - if (innername == null) - /* anonymous class */ - outerCI = null; + ClassInfo outerCI = outer != null + ? classpath.getClassInfo(outer) : null; innerCI.mergeOuterInfo(innername, outerCI, access, outerCI == null); if (outerCI == this) - innerExtra[innerCount++] = innerCI; - else { - /* Remove outerCI from the extra part of innerExtra - * since it is implicit now. - */ - for (int j = count - 1; j >= count - extraCount; j--) { - if (innerExtra[j] == outerCI) { - System.arraycopy(innerExtra, count - extraCount, - innerExtra, count - extraCount + 1, - j - (count - extraCount)); - extraCount--; - break; - } - } - - /* Add innerCI to the extra classes, except if it is - * this class. - */ - if (innerCI != this) - innerExtra[count - (++extraCount)] = innerCI; - } + innerCIs[innerCount++] = innerCI; } /* Now inner classes are at the front of the array in correct @@ -557,32 +519,15 @@ public final class ClassInfo extends BinaryInfo implements Comparable { */ if (innerCount > 0) { innerClasses = new ClassInfo[innerCount]; - for (int i=0; i < innerCount; i++) - innerClasses[i] = innerExtra[i]; + System.arraycopy(innerCIs, 0, innerClasses, 0, innerCount); } else - innerClasses = null; - - /* All remaining classes that are mentioned in the constant - * pool must have an empty outer class info. This is - * specified in the 2nd edition of the JVM specification. - */ - for (int i = 1; i < cp.size(); i++) { - if (cp.tags[i] == cp.CLASS) { - String clName = cp.getUTF8(cp.indices1[i]); - if (clName.charAt(0) != '[') { - ClassInfo ci = classpath.getClassInfo - (clName.replace('/','.')); - if (ci.status < OUTERCLASS) - ci.mergeOuterInfo(null, null, -1, false); - } - } - } + innerClasses = EMPTY_INNER; } - void readAttribute(String name, int length, - ConstantPool cp, - DataInputStream input, - int howMuch) throws IOException { + protected void readAttribute(String name, int length, + ConstantPool cp, + DataInputStream input, + int howMuch) throws IOException { if (howMuch >= ClassInfo.ALMOSTALL && name.equals("SourceFile")) { if (length != 2) throw new ClassFormatException("SourceFile attribute" @@ -642,7 +587,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable { (clazz.getSuperclass().getName()); Class[] ifaces = clazz.getInterfaces(); interfaces = new ClassInfo[ifaces.length]; - for (int i=0; i= PUBLICDECLARATIONS) { @@ -707,7 +652,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable { innerClasses[i].loadFromReflection(is[i], OUTERCLASS); } } else - innerClasses = null; + innerClasses = EMPTY_INNER; } status = howMuch; } @@ -761,11 +706,12 @@ public final class ClassInfo extends BinaryInfo implements Comparable { String className = cpool.getClassName(input.readUnsignedShort()); if (!name.equals(className)) throw new ClassFormatException("wrong name " + className); - String superName = cpool.getClassName(input.readUnsignedShort()); - superclass = superName != null ? classpath.getClassInfo(superName) : null; + int superID = input.readUnsignedShort(); + superclass = superID == 0 ? null + : classpath.getClassInfo(cpool.getClassName(superID)); int count = input.readUnsignedShort(); interfaces = new ClassInfo[count]; - for (int i=0; i< count; i++) { + for (int i = 0; i < count; i++) { interfaces[i] = classpath.getClassInfo (cpool.getClassName(input.readUnsignedShort())); } @@ -775,14 +721,14 @@ public final class ClassInfo extends BinaryInfo implements Comparable { if (howMuch >= PUBLICDECLARATIONS) { int count = input.readUnsignedShort(); fields = new FieldInfo[count]; - for (int i=0; i< count; i++) { + for (int i = 0; i < count; i++) { fields[i] = new FieldInfo(); fields[i].read(cpool, input, howMuch); } } else { byte[] skipBuf = new byte[6]; int count = input.readUnsignedShort(); - for (int i=0; i< count; i++) { + for (int i = 0; i < count; i++) { input.readFully(skipBuf); // modifier, name, type skipAttributes(input); } @@ -792,29 +738,45 @@ public final class ClassInfo extends BinaryInfo implements Comparable { if (howMuch >= PUBLICDECLARATIONS) { int count = input.readUnsignedShort(); methods = new MethodInfo[count]; - for (int i=0; i< count; i++) { + for (int i = 0; i < count; i++) { methods[i] = new MethodInfo(); methods[i].read(cpool, input, howMuch); } } else { byte[] skipBuf = new byte[6]; int count = input.readUnsignedShort(); - for (int i=0; i< count; i++) { + for (int i = 0; i < count; i++) { input.readFully(skipBuf); // modifier, name, type skipAttributes(input); } } + /* initialize inner classes to empty array, in case there + * is no InnerClasses attribute. + */ + innerClasses = EMPTY_INNER; + /* attributes */ readAttributes(cpool, input, howMuch); + + /* All classes that are mentioned in the constant pool must + * have an empty outer class info. This is specified in the + * 2nd edition of the JVM specification. + */ + Iterator iter = cpool.iterateClassNames(); + while (iter.hasNext()) { + ClassInfo ci = classpath.getClassInfo((String) iter.next()); + if (ci.status < OUTERCLASS) + ci.mergeOuterInfo(null, null, -1, false); + } status = howMuch; } private void reserveSmallConstants(GrowableConstantPool gcp) { - for (int i=0; i < fields.length; i++) + for (int i = 0; i < fields.length; i++) fields[i].reserveSmallConstants(gcp); - for (int i=0; i < methods.length; i++) + for (int i = 0; i < methods.length; i++) methods[i].reserveSmallConstants(gcp); } @@ -823,16 +785,16 @@ public final class ClassInfo extends BinaryInfo implements Comparable { private void prepareWriting(GrowableConstantPool gcp) { gcp.putClassName(name); gcp.putClassName(superclass.name); - for (int i=0; i < interfaces.length; i++) + for (int i = 0; i < interfaces.length; i++) gcp.putClassName(interfaces[i].name); - for (int i=0; i < fields.length; i++) + for (int i = 0; i < fields.length; i++) fields[i].prepareWriting(gcp); - for (int i=0; i < methods.length; i++) + for (int i = 0; i < methods.length; i++) methods[i].prepareWriting(gcp); - for (int i=0; i < innerClasses.length; i++) + for (int i = 0; i < innerClasses.length; i++) gcp.putClassName(innerClasses[i].name); if (sourceFile != null) { @@ -845,40 +807,29 @@ public final class ClassInfo extends BinaryInfo implements Comparable { * edition of the JVM specification. */ hasInnerClassesAttr = false; - for (int i = 1; i < gcp.size(); i++) { - if (gcp.tags[i] == gcp.CLASS) { - String clName; - try { - clName = gcp.getUTF8(gcp.indices1[i]); - } catch (ClassFormatException ex) { - throw new InternalError(ex.getMessage()); - } - if (clName.charAt(0) != '[') { - ClassInfo ci = classpath.getClassInfo - (clName.replace('/','.')); - if (ci.status < OUTERCLASS) { - GlobalOptions.err.println - ("WARNING: "+ ci.name - + "'s outer class isn't known."); - } else { - if ((ci.outerClass != null || ci.methodScoped) - && ! hasInnerClassesAttr) { - gcp.putUTF8("innerClasses"); - hasInnerClassesAttr = true; - } - if (ci.outerClass != null) - gcp.putClassName(ci.outerClass.name); - if (ci.className != null) - gcp.putUTF8(ci.className); - } + Iterator iter = gcp.iterateClassNames(); + while (iter.hasNext()) { + ClassInfo ci = classpath.getClassInfo((String) iter.next()); + if (ci.status < OUTERCLASS) { + GlobalOptions.err.println + ("WARNING: " + ci.name + "'s outer class isn't known."); + } else { + if ((ci.outerClass != null || ci.methodScoped) + && ! hasInnerClassesAttr) { + gcp.putUTF8("InnerClasses"); + hasInnerClassesAttr = true; } + if (ci.outerClass != null) + gcp.putClassName(ci.outerClass.name); + if (ci.className != null) + gcp.putUTF8(ci.className); } } prepareAttributes(gcp); } - int getKnownAttributeCount() { - int count = 0; + protected int getAttributeCount() { + int count = super.getAttributeCount(); if (sourceFile != null) count++; if (hasInnerClassesAttr) @@ -886,9 +837,10 @@ public final class ClassInfo extends BinaryInfo implements Comparable { return count; } - void writeKnownAttributes(GrowableConstantPool gcp, - DataOutputStream output) + protected void writeAttributes(GrowableConstantPool gcp, + DataOutputStream output) throws IOException { + super.writeAttributes(gcp, output); if (sourceFile != null) { output.writeShort(gcp.putUTF8("SourceFile")); output.writeInt(2); @@ -896,28 +848,19 @@ public final class ClassInfo extends BinaryInfo implements Comparable { } List outers = new ArrayList(); - for (int i = 0; i < gcp.size(); i++) { - if (gcp.tags[i] == gcp.CLASS) { - String clName; - try { - clName = gcp.getUTF8(gcp.indices1[i]); - } catch (ClassFormatException ex) { - throw new InternalError(ex.getMessage()); - } - if (clName.charAt(0) != '[') { - ClassInfo ci = classpath.getClassInfo - (clName.replace('/','.')); - while (ci.status >= OUTERCLASS - && ci.outerClass != null || ci.methodScoped) { - /* Order is important so remove ci if it - * already exists and add it to the end. This - * way the outermost classes go to the end. - */ - outers.remove(ci); - outers.add(ci); - ci = ci.outerClass; - } - } + Iterator iter = gcp.iterateClassNames(); + while (iter.hasNext()) { + ClassInfo ci = classpath.getClassInfo((String) iter.next()); + while (ci != null + && ci.status >= OUTERCLASS + && (ci.outerClass != null || ci.methodScoped)) { + /* Order is important so remove ci if it + * already exists and add it to the end. This + * way the outermost classes go to the end. + */ + outers.remove(ci); + outers.add(ci); + ci = ci.outerClass; } } if (hasInnerClassesAttr) { @@ -926,9 +869,9 @@ public final class ClassInfo extends BinaryInfo implements Comparable { output.writeInt(2 + count * 8); output.writeShort(count); - ListIterator iter = outers.listIterator(count); - while (iter.hasPrevious()) { - ClassInfo ci = (ClassInfo) iter.previous(); + ListIterator listiter = outers.listIterator(count); + while (listiter.hasPrevious()) { + ClassInfo ci = (ClassInfo) listiter.previous(); output.writeShort(gcp.putClassName(ci.name)); output.writeShort(ci.outerClass == null ? 0 : @@ -951,7 +894,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable { * @exception IllegalStateException if not enough information is set. */ public void write(DataOutputStream out) throws IOException { - if (status <= ALL) + if (status < ALL) throw new IllegalStateException("state is "+status); GrowableConstantPool gcp = new GrowableConstantPool(); @@ -967,15 +910,15 @@ public final class ClassInfo extends BinaryInfo implements Comparable { out.writeShort(gcp.putClassName(name)); out.writeShort(gcp.putClassName(superclass.getName())); out.writeShort(interfaces.length); - for (int i=0; i < interfaces.length; i++) + for (int i = 0; i < interfaces.length; i++) out.writeShort(gcp.putClassName(interfaces[i].getName())); out.writeShort(fields.length); - for (int i=0; i < fields.length; i++) + for (int i = 0; i < fields.length; i++) fields[i].write(gcp, out); out.writeShort(methods.length); - for (int i=0; i < methods.length; i++) + for (int i = 0; i < methods.length; i++) methods[i].write(gcp, out); writeAttributes(gcp, out); @@ -1018,11 +961,6 @@ public final class ClassInfo extends BinaryInfo implements Comparable { * Guess the contents of a class. It * @param howMuch The amount of information that should be read, e.g. * HIERARCHY. - * @exception ClassFormatException if the file doesn't denote a - * valid class. - * @exception FileNotFoundException if class wasn't found in classpath. - * @exception IOException if an io exception occured. - * @exception IllegalStateException if this ClassInfo was modified. * @see #OUTERCLASS * @see #HIERARCHY * @see #PUBLICDECLARATIONS @@ -1074,7 +1012,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable { if (howMuch >= PUBLICDECLARATIONS) { methods = new MethodInfo[0]; fields = new FieldInfo[0]; - innerClasses = new ClassInfo[0]; + innerClasses = EMPTY_INNER; } status = howMuch; } @@ -1117,9 +1055,9 @@ public final class ClassInfo extends BinaryInfo implements Comparable { */ keep = DECLARATIONS; - for (int i=0; i < fields.length; i++) + for (int i = 0; i < fields.length; i++) fields[i].drop(keep); - for (int i=0; i < methods.length; i++) + for (int i = 0; i < methods.length; i++) methods[i].drop(keep); } @@ -1201,7 +1139,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable { public FieldInfo findField(String name, String typeSig) { if (status < PUBLICDECLARATIONS) throw new IllegalStateException("status is "+status); - for (int i=0; i< fields.length; i++) + for (int i = 0; i < fields.length; i++) if (fields[i].getName().equals(name) && fields[i].getType().equals(typeSig)) return fields[i]; @@ -1211,7 +1149,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable { public MethodInfo findMethod(String name, String typeSig) { if (status < PUBLICDECLARATIONS) throw new IllegalStateException("status is "+status); - for (int i=0; i< methods.length; i++) + for (int i = 0; i < methods.length; i++) if (methods[i].getName().equals(name) && methods[i].getType().equals(typeSig)) return methods[i]; @@ -1260,6 +1198,12 @@ public final class ClassInfo extends BinaryInfo implements Comparable { return methodScoped; } + /** + * Returns the inner classes declared in this class. + * This needs at least PUBLICDECLARATION information loaded. + * @return an array containing the inner classes, guaranteed != null. + * @exception IllegalStateException if PUBLICDECLARATIONS information + * wasn't loaded yet. */ public ClassInfo[] getClasses() { if (status < PUBLICDECLARATIONS) throw new IllegalStateException("status is "+status); @@ -1270,53 +1214,73 @@ public final class ClassInfo extends BinaryInfo implements Comparable { return sourceFile; } + /** + * Sets the name of this class info. Note that by changing the + * name you may overwrite an already loaded class. This can have + * ugly effects, as that overwritten class may still exist, but + * can't be loaded via the classPath. + */ public void setName(String newName) { + /* The class name is used as index in the hash table. We have + * to update the class path and tell it about the name change. + */ + classpath.renameClassInfo(this, newName); name = newName.intern(); + status = ALL; modified = true; } public void setSuperclass(ClassInfo newSuper) { superclass = newSuper; + status = ALL; modified = true; } public void setInterfaces(ClassInfo[] newIfaces) { interfaces = newIfaces; + status = ALL; modified = true; } public void setModifiers(int newModifiers) { modifiers = newModifiers; + status = ALL; modified = true; } public void setMethods(MethodInfo[] mi) { methods = mi; + status = ALL; modified = true; } public void setFields(FieldInfo[] fi) { fields = fi; + status = ALL; modified = true; } public void setOuterClass(ClassInfo oc) { outerClass = oc; + status = ALL; modified = true; } public void setMethodScoped(boolean ms) { methodScoped = ms; + status = ALL; modified = true; } public void setClasses(ClassInfo[] ic) { - innerClasses = ic; + innerClasses = ic.length == 0 ? EMPTY_INNER : ic; + status = ALL; modified = true; } public void setSourceFile(String newSource) { sourceFile = newSource; + status = ALL; modified = true; } @@ -1363,12 +1327,12 @@ public final class ClassInfo extends BinaryInfo implements Comparable { ClassInfo[] interfaces = (ClassInfo[]) this.interfaces.clone(); Arrays.sort(interfaces); - for (int i=0; i < interfaces.length; i++) + for (int i = 0; i < interfaces.length; i++) out.writeUTF(interfaces[i].name); FieldInfo[] fields = (FieldInfo[]) this.fields.clone(); Arrays.sort(fields); - for (int i=0; i < fields.length; i++) { + for (int i = 0; i < fields.length; i++) { modifs = fields[i].getModifiers(); if ((modifs & Modifier.PRIVATE) != 0 && (modifs & (Modifier.STATIC @@ -1383,7 +1347,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable { MethodInfo[] methods = (MethodInfo[]) this.methods.clone(); Arrays.sort(methods); - for (int i=0; i < methods.length; i++) { + for (int i = 0; i < methods.length; i++) { modifs = methods[i].getModifiers(); /* The modifiers of should be just static, * but jikes also marks it final. @@ -1405,7 +1369,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable { byte[] sha = md.digest(); long result = 0; - for (int i=0; i < 8; i++) { + for (int i = 0; i < 8; i++) { result += (long)(sha[i] & 0xFF) << (8 * i); } return result; @@ -1461,7 +1425,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable { if (clazz.status < HIERARCHY) clazz.load(HIERARCHY); ClassInfo[] ifaces = clazz.getInterfaces(); - for (int i=0; i< ifaces.length; i++) { + for (int i = 0; i < ifaces.length; i++) { if (implementedBy(ifaces[i])) return true; } diff --git a/jode/src/net/sf/jode/bytecode/ClassPath.java b/jode/src/net/sf/jode/bytecode/ClassPath.java index 2ccea9e..b2f2af8 100644 --- a/jode/src/net/sf/jode/bytecode/ClassPath.java +++ b/jode/src/net/sf/jode/bytecode/ClassPath.java @@ -684,6 +684,26 @@ public class ClassPath { return clazz; } + /** + * Updates the classes unify hash for a class renaming. This + * should be only called by {@link ClassInfo#setName}. + */ + void renameClassInfo(ClassInfo classInfo, String classname) { + classes.remove(classInfo.getName().hashCode(), classInfo); + /* Now remove any class already loaded with that name, just + * in case we're overwriting one. + */ + Iterator iter = classes.iterateHashCode(classname.hashCode()); + while (iter.hasNext()) { + ClassInfo clazz = (ClassInfo) iter.next(); + if (clazz.getName().equals(classname)) { + iter.remove(); + break; + } + } + classes.put(classname.hashCode(), classInfo); + } + /** * Checks, if a class with the given name exists somewhere in this * path. @@ -752,7 +772,7 @@ public class ClassPath { * @param fqn the full qualified name. The components should be dot * separated. * @return true, if filename exists and is a package, false otherwise. - * @see isDirectory + * @see #isDirectory */ public boolean isPackage(String fqn) { return isDirectory(fqn.replace('.', '/')); diff --git a/jode/src/net/sf/jode/bytecode/ConstantPool.java b/jode/src/net/sf/jode/bytecode/ConstantPool.java index 867b2b2..f90b7ce 100644 --- a/jode/src/net/sf/jode/bytecode/ConstantPool.java +++ b/jode/src/net/sf/jode/bytecode/ConstantPool.java @@ -21,12 +21,22 @@ package net.sf.jode.bytecode; import java.io.DataInputStream; import java.io.IOException; +import java.util.NoSuchElementException; +///#def COLLECTIONS java.util +import java.util.Iterator; +///#enddef +///#def COLLECTIONEXTRA java.lang +import java.lang.UnsupportedOperationException; +///#enddef + /** - * This class represent the constant pool. + * This class represent the constant pool. You normally don't need to + * access this class, except if you want to add your own custom + * attributes that use the constant pool. * * @author Jochen Hoenicke */ -class ConstantPool { +public class ConstantPool { public final static int CLASS = 7; public final static int FIELDREF = 9; public final static int METHODREF = 10; @@ -106,16 +116,12 @@ class ConstantPool { } public String getUTF8(int i) throws ClassFormatException { - if (i == 0) - return null; if (tags[i] != UTF8) throw new ClassFormatException("Tag mismatch"); return (String)constants[i]; } public Reference getRef(int i) throws ClassFormatException { - if (i == 0) - return null; if (tags[i] != FIELDREF && tags[i] != METHODREF && tags[i] != INTERFACEMETHODREF) throw new ClassFormatException("Tag mismatch"); @@ -156,8 +162,6 @@ class ConstantPool { } public String getClassType(int i) throws ClassFormatException { - if (i == 0) - return null; if (tags[i] != CLASS) throw new ClassFormatException("Tag mismatch"); String clName = getUTF8(indices1[i]); @@ -169,13 +173,10 @@ class ConstantPool { } catch (IllegalArgumentException ex) { throw new ClassFormatException(ex.getMessage()); } - return clName; } public String getClassName(int i) throws ClassFormatException { - if (i == 0) - return null; if (tags[i] != CLASS) throw new ClassFormatException("Tag mismatch"); String clName = getUTF8(indices1[i]); @@ -187,6 +188,43 @@ class ConstantPool { return clName.replace('/','.').intern(); } + /** + * Iterates through all class entries in the class pool and returns + * their (dot seperated) class name. + */ + public Iterator iterateClassNames() { + return new Iterator() + { + int entry = 1; + public boolean hasNext() { + try { + while (entry < count + && (tags[entry] != CLASS + || getUTF8(indices1[entry]) + .charAt(0) == '[')) + entry++; + } catch (ClassFormatException ex) { + throw new InternalError(ex.getMessage()); + } + return entry < count; + } + + public Object next() { + if (!hasNext()) + throw new NoSuchElementException(); + try { + return getClassName(entry++); + } catch (ClassFormatException ex) { + throw new InternalError(ex.getMessage()); + } + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + public String toString(int i) { switch (tags[i]) { case CLASS: @@ -225,7 +263,7 @@ class ConstantPool { } public String toString() { - StringBuffer result = new StringBuffer("[ null"); + StringBuffer result = new StringBuffer("ConstantPool[ null"); for (int i=1; i< count; i++) { result.append(", ").append(i).append(" = ").append(toString(i)); } diff --git a/jode/src/net/sf/jode/bytecode/FieldInfo.java b/jode/src/net/sf/jode/bytecode/FieldInfo.java index 82b5fdd..a257d00 100644 --- a/jode/src/net/sf/jode/bytecode/FieldInfo.java +++ b/jode/src/net/sf/jode/bytecode/FieldInfo.java @@ -44,10 +44,10 @@ public final class FieldInfo extends BinaryInfo implements Comparable { this.modifier = modifier; } - void readAttribute(String name, int length, - ConstantPool cp, - DataInputStream input, - int howMuch) throws IOException { + protected void readAttribute(String name, int length, + ConstantPool cp, + DataInputStream input, + int howMuch) throws IOException { if (howMuch >= ClassInfo.DECLARATIONS && name.equals("ConstantValue")) { if (length != 2) @@ -97,8 +97,8 @@ public final class FieldInfo extends BinaryInfo implements Comparable { prepareAttributes(gcp); } - protected int getKnownAttributeCount() { - int count = 0; + protected int getAttributeCount() { + int count = super.getAttributeCount(); if (constant != null) count++; if (syntheticFlag) @@ -108,9 +108,10 @@ public final class FieldInfo extends BinaryInfo implements Comparable { return count; } - void writeKnownAttributes(GrowableConstantPool gcp, - DataOutputStream output) + protected void writeAttributes(GrowableConstantPool gcp, + DataOutputStream output) throws IOException { + super.writeAttributes(gcp, output); if (constant != null) { output.writeShort(gcp.putUTF8("ConstantValue")); output.writeInt(2); @@ -140,7 +141,7 @@ public final class FieldInfo extends BinaryInfo implements Comparable { writeAttributes(constantPool, output); } - void drop(int keep) { + protected void drop(int keep) { if (keep < ClassInfo.DECLARATIONS) constant = null; super.drop(keep); diff --git a/jode/src/net/sf/jode/bytecode/Handler.java b/jode/src/net/sf/jode/bytecode/Handler.java index d67bd94..5367f06 100644 --- a/jode/src/net/sf/jode/bytecode/Handler.java +++ b/jode/src/net/sf/jode/bytecode/Handler.java @@ -66,7 +66,7 @@ public class Handler { } /** - * Gets the type signature of the exception. + * Gets the class name of the exception type. */ public String getType() { return type; @@ -85,7 +85,7 @@ public class Handler { } /** - * Sets the type signature of the exception. + * Sets the class name of the exception type. */ public void setType(String type) { this.type = type; diff --git a/jode/src/net/sf/jode/bytecode/MethodInfo.java b/jode/src/net/sf/jode/bytecode/MethodInfo.java index 47ea39d..ec5ec0f 100644 --- a/jode/src/net/sf/jode/bytecode/MethodInfo.java +++ b/jode/src/net/sf/jode/bytecode/MethodInfo.java @@ -26,6 +26,38 @@ import java.lang.reflect.Modifier; import java.lang.Comparable; ///#enddef +/** + *

Represents a java bytecode method. A method consists of the following + * parts:

+ * + *
+ * + *
name
The method's name
+ * + *
type
The method's {@link TypeSignature type signature} + * in bytecode format.
+ * + *
modifiers
The modifiers of the method like private, public etc. + * These are created by or-ing the constants defined in + * {@link java.lang.reflect.Modifier}. + * + *
basicblocks
the bytecode of the method in form of + * {@link BasicBlocks basic blocks}, null if it is native or + * abstract.
+ * + *
synthetic
true if this method is synthetic
+ * + *
deprecated
true if this method is deprecated
+ * + *
exceptions
the exceptions that the method declared in + * its throws clause
+ * + *
+ * + * @author Jochen Hoenicke + * @see net.sf.jode.bytecode.TypeSignature + * @see net.sf.jode.bytecode.BasicBlocks + */ public final class MethodInfo extends BinaryInfo implements Comparable { int modifier; String name; @@ -45,8 +77,9 @@ public final class MethodInfo extends BinaryInfo implements Comparable { this.modifier = modifier; } - void readAttribute(String name, int length, ConstantPool cp, - DataInputStream input, int howMuch) throws IOException { + protected void readAttribute + (String name, int length, ConstantPool cp, + DataInputStream input, int howMuch) throws IOException { if (howMuch >= ClassInfo.NODEBUG && name.equals("Code")) { basicblocks = new BasicBlocks(this); basicblocks.read(cp, input, howMuch); @@ -105,8 +138,8 @@ public final class MethodInfo extends BinaryInfo implements Comparable { prepareAttributes(gcp); } - int getKnownAttributeCount() { - int count = 0; + protected int getAttributeCount() { + int count = super.getAttributeCount(); if (basicblocks != null) count++; if (exceptions != null) @@ -118,9 +151,10 @@ public final class MethodInfo extends BinaryInfo implements Comparable { return count; } - void writeKnownAttributes(GrowableConstantPool gcp, - DataOutputStream output) + protected void writeAttributes(GrowableConstantPool gcp, + DataOutputStream output) throws IOException { + super.writeAttributes(gcp, output); if (basicblocks != null) { output.writeShort(gcp.putUTF8("Code")); basicblocks.write(gcp, output); @@ -144,14 +178,14 @@ public final class MethodInfo extends BinaryInfo implements Comparable { } void write(GrowableConstantPool constantPool, - DataOutputStream output) throws IOException { + DataOutputStream output) throws IOException { output.writeShort(modifier); output.writeShort(constantPool.putUTF8(name)); output.writeShort(constantPool.putUTF8(typeSig)); writeAttributes(constantPool, output); } - void drop(int keep) { + protected void drop(int keep) { if (keep < ClassInfo.DECLARATIONS) exceptions = null; if (keep < ClassInfo.NODEBUG) @@ -260,8 +294,12 @@ public final class MethodInfo extends BinaryInfo implements Comparable { return result; } + /** + * Returns a string representation of this method. It consists + * of the method's name and type signature. + * @return a string representation of this method. + */ public String toString() { - return "Method "+Modifier.toString(modifier)+" "+ - typeSig + " " + name; + return "MethodInfo[name="+name+",type="+typeSig+"]"; } } diff --git a/jode/src/net/sf/jode/bytecode/Subroutine.java b/jode/src/net/sf/jode/bytecode/Subroutine.java deleted file mode 100644 index 5b91bbe..0000000 --- a/jode/src/net/sf/jode/bytecode/Subroutine.java +++ /dev/null @@ -1,73 +0,0 @@ -/* Subroutine Copyright (C) 2000 Jochen Hoenicke. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -package net.sf.jode.bytecode; - -import java.io.PrintWriter; -///#def COLLECTIONS java.util -import java.util.Collection; -import java.util.Arrays; -import java.util.List; -import java.util.Iterator; -///#enddef - -/** - *

Represents a jsr-Subroutine.

- * - *

In my representation a subroutine consists of all blocks from - * which the ret instruction is reachable. Every subroutine must have - * a reachable ret instruction, or the jsr is replaced by a simple goto. - *

- * - * @author Jochen Hoenicke - * @see net.sf.jode.bytecode.BasicBlocks - * @see net.sf.jode.bytecode.Block - */ -public final class Subroutine { - /** - * Subroutines may be nested. This points to the outer subroutine - * or to null if this doesn't have an outer. - */ - private Subroutine outer; - - /** - * Each subroutine has exactly one ret instruction, which is the - * last instruction in the retBlock. The local of the ret - * instruction must equal the local where the first instruction of - * the subroutine stores to. - */ - private Block retBlock; - - /** - * The set of locals that are accessed inside this subroutine. - */ - private BitSet accessedLocals; - - public Block getRetBlock() { - return retBlock; - } - - public Subroutine getOuter() { - return outer; - } - - public boolean isAccessed(int slot) { - return accessedLocals.get(slot); - } -} diff --git a/jode/src/net/sf/jode/bytecode/TransformSubroutine.java b/jode/src/net/sf/jode/bytecode/TransformSubroutine.java deleted file mode 100644 index b5cb075..0000000 --- a/jode/src/net/sf/jode/bytecode/TransformSubroutine.java +++ /dev/null @@ -1,228 +0,0 @@ -/* TransformSubroutine Copyright (C) 1999-2000 Jochen Hoenicke. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -package net.sf.jode.bytecode; -import java.util.BitSet; - -/** - *

This class contains some code to transform the subroutines of - * a method into a normal form. The normal form is as following.

- * - *

Each subroutine block, meaning a block where some jsr - * instructions may jump to, must store the return address in a local - * variable immediately. There must be exactly one block with the - * corresponding opc_ret instruction and this block must - * be reachable from all blocks that belong to this subroutine. - * - *

The JVM spec allows a subroutine, to leave the return address on - * stack for a while, even longer than the subroutine actually exists. - * One can also pop that value instead of storing it in a local - * variable. And finally its possible to store it in a variable, but - * there exists no reachable opc_ret instruction that returns - * to that address.

- * - *

If the return address is not used by the subroutine, we simply - * replace the jsr by a jump and remove the pop/store instruction that - * popped the return address.

- * - *

If the return address is used, but not immediately stored, we simply - * move the corresponding astore to the start of the subroutine.

- * - * @see net.sf.jode.bytecode.Block - * @see net.sf.jode.bytecode.Instruction - */ -public class TransformSubroutine implements Opcodes { - private final static int SUBSTATUS_SUBROUTINE = 1; - private final static int SUBSTATUS_REMOVEDSUB = 2; - private final static int SUBSTATUS_NOTSUB = 3; - private final static int SUBSTATUS_INPROGRESS = 4; - - BasicBlocks bb; - Blocks[] blocks; - byte[] substatus; - Subroutine[] subInfo; - -// { -// for (int i=0; i < blocks.length; i++) { -// Instructions[] instrs = blocks[i].getInstructions(); -// if (instrs[instrs.length-1].getOpcode() == opc_jsr) { -// int srBlock = instrs.getSuccs()[0].getBlockNr(); -// if (substatus[srBlock] == 0) -// analyzeSubroutine(srBlock); -// if (substatus[srBlock] == SUBSTATUS_REMOVED) { -// Instructions[] newInstrs -// = new Instruction[instrs.length-1]; -// System.arraycopy(instrs, 0, newInstrs, 0, -// newInstrs.length); -// Block[] newSuccs = new Block[1] { instrs.getSuccs()[1]; }; -// blocks[i].setCode(newInstrs, newSuccs); -// } -// } -// } -// } - - class SubroutineInfo { - int retPosition; - BitSet accessedLocals; - SubroutineInfo outer; - - SubroutineInfo(int retPos, Bitset accLocals, SubroutineInfo outer) { - this.outer = outer; - this.retPosition = retPos; - this.accessedLocals = accLocals; - } - - boolean merge(int retPos, BitSet accLocals, SubroutineInfo outer) { - if ((retPos < 0 || this.retPosition < 0) - && retPos != this.retPosition) - throw new - } - } - - public TransformSubroutine(BasicBlocks bb) - throws ClassFormatException - { - if (bb.getStartBlock() == null) - return; - - blocks = bb.getBlocks(); - substatus = new byte[blocks.length]; - analyzeBlock(blockNr, SUBSTATUS_NOTSUB, null); - } - - public void analyzeBlock(int blockNr, int status, SubroutineInfo outer, - BitSet retsOnStack) { - Block block = blocks[blockNr]; - if (status == SUBSTATUS_INPROGRESS) { - - } - - } - - public void analyzeBlock(int blockNr, int status, SubroutineInfo outer) { - substatus[blockNr] = status; - accessedLocals[blockNr] = accessed; - Stack todo = new Stack(); - - todo.add(new BlockInfo(startBlockNr, 0, null)); - while (!todo.isEmpty()) { - BlockInfo info = todo.pop(); - Block block = blocks[info.blockNr]; - Instruction[] instrs = block.getInstructions(); - Instruction[] newInstrs = null; - Block[] succs = block.getSuccessors(); - - - if (substatus[info.blockNr] - == SUBSTATUS_INPROGRESS) { - int retPosition = info.retPosition; - BitSet = - retPosition < 0 ? info.accessedLocals.clone() : null; - - for (int i = 0; i < instrs.length; i++) { - Instruction instr = instrs[i]; - if (instr instanceof SlotInstruction) { - if (instr.getOpcode() == opc_astore - && retPosition == -1) { - - /* We found the store operation. At least - * a candidate, since there may not be a - * corresponding ret. - */ - - retPosition = instr.getLocalSlot(); - accessedLocals = null; - /* remove store Instruction. - */ - newInstrs = new Instruction[instrs.length - 1]; - System.arraycopy(instrs, 0, newInstrs, 0, i); - System.arraycopy(instrs, i+1, newInstrs, i, - newInstrs.length - i); - - } else { - if (retPosition < 0) { - accessedLocals.add(instr.getLocalSlot()); - switch (instr.getOpcode()) { - case opc_lload: - case opc_dload: - case opc_lstore: - case opc_dstore: - accessedLocals.add(instr.getLocalSlot()+1); - } - } - } - } else if (instr.getOpcode() == opc_pop - && retPosition == -1) { - /* We spontanously left the subroutine by popping. - * Remove the pop Instruction. - */ - newInstrs = new Instruction[instrs.length - 1]; - System.arraycopy(instrs, 0, newInstrs, 0, i); - System.arraycopy(instrs, i+1, newInstrs, i, - newInstrs.length - i); - substatus[info.blockNr] = SUBSTATUS_NOTSUB; - break; - } else if ((instr.getOpcode() == opc_pop2 && - (retPosition == -1 || retPosition == -2))) { - /* We spontanously left the subroutine by popping. - * Replace the pop2 with a pop. - */ - newInstrs = new Instruction[instrs.length]; - System.arraycopy(instrs, 0, newInstrs, 0, - instrs.length); - newInstrs[i] = Instruction.forOpcode(opc_pop); - substatus[info.blockNr] = SUBSTATUS_NOTSUB; - break; - } else if (instr.getOpcode() == opc_jsr) { - /* A recursive subroutine (or have we already - * spontanously left this subroutine?) - */ - int srBlock = instrs.getSuccs()[0].getBlockNr(); - if (substatus[srBlock] == 0) - analyzeSubroutine(srBlock); - if (substatus[srBlock] == SUBSTATUS_INPROGRESS) { - /* We spontanously left this subroutine! */ - if (retPosition < 0) - /* This can't happen in valid code. - */ - throw new CodeFormatException - ("Can't merge return instr on Stack."); - substatus[info.blockNr] = SUBSTATUS_NOTSUB; - } - leftSub = true; - break; - } else if (substatus[srBlock] == SUBSTATUS_REMOVED) { - Instructions[] newInstrs - = new Instruction[instrs.length-1]; - System.arraycopy(instrs, 0, newInstrs, 0, - newInstrs.length); - Block[] newSuccs = new Block[1] { instrs.getSuccs()[1]; }; - blocks[i].setCode(newInstrs, newSuccs); - } - } - } - if (!leftSub) { - - - } - } - } -} - - diff --git a/jode/src/net/sf/jode/bytecode/package.html b/jode/src/net/sf/jode/bytecode/package.html index 64409b9..758de1d 100644 --- a/jode/src/net/sf/jode/bytecode/package.html +++ b/jode/src/net/sf/jode/bytecode/package.html @@ -18,7 +18,7 @@ along with this program; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. --> - JODE Bytecode Package +JODE Bytecode Package @@ -29,13 +29,9 @@ class and get a ClassInfo object. As third step you can actually load the class.

Please notify me if you want to use this package. I will inform you -about updates, help you with problems, etc. WARNING: This -package may change in the future in incompatible ways. Ask me for -more information.

- -BIG FAT WARNING: Some of the things documented here aren't even -implemented. Some of this is only vapor ware. But feel free to -comment on this and point me to design errors!

+about updates, help you with problems, etc. WARNING: Some +parts of this package may change in the future in incompatible ways. +Ask me for more information.

Here is a short example, how you can use this package, see the documentation of the classes for more details. @@ -55,7 +51,7 @@ documentation of the classes for more details. } MethodInfo[] methods = clazz.getMethods(); - for (int i=0; i< methods.length; i++) { + for (int i = 0; i < methods.length; i++) { String type = methods[i].getType(); if (TypeSignature.getReturnType(type) == TypeSignature.INT_TYPE) System.out.println("Found integer method: "+method.getName()); @@ -79,7 +75,7 @@ You can also use this package to create and write new classes:
Jochen Hoenicke
-Last modified: Mon Jun 26 09:46:24 MET DST 2000 +Last modified: Sun Aug 5 17:53:03 MEST 2001 diff --git a/jode/src/net/sf/jode/decompiler/Decompiler.java b/jode/src/net/sf/jode/decompiler/Decompiler.java index 8476c95..7173ad4 100644 --- a/jode/src/net/sf/jode/decompiler/Decompiler.java +++ b/jode/src/net/sf/jode/decompiler/Decompiler.java @@ -203,9 +203,6 @@ public class Decompiler { * @param progress A progress listener (see below). Null if you * don't need information about progress. * @exception IllegalArgumentException if className isn't correct. - * @exception net.sf.jode.jvm.VerifyException The code - * isn't valid or a dependent class, needed for type - * guessing, couldn't be found. * @exception IOException if writer throws an exception. * @exception RuntimeException If jode has a bug ;-) */ diff --git a/jode/src/net/sf/jode/decompiler/Main.java b/jode/src/net/sf/jode/decompiler/Main.java index d9c1e52..c549430 100644 --- a/jode/src/net/sf/jode/decompiler/Main.java +++ b/jode/src/net/sf/jode/decompiler/Main.java @@ -20,6 +20,7 @@ package net.sf.jode.decompiler; import net.sf.jode.bytecode.ClassInfo; import net.sf.jode.bytecode.ClassPath; +import net.sf.jode.bytecode.ClassFormatException; import net.sf.jode.GlobalOptions; import java.io.BufferedOutputStream; @@ -172,6 +173,10 @@ public class Main extends Options { GlobalOptions.err.println ("Check the class path ("+classPathStr+ ") and check that you use the java class name."); + } catch (ClassFormatException ex) { + GlobalOptions.err.println + ("Error while reading "+className+"."); + ex.printStackTrace(GlobalOptions.err); } catch (IOException ex) { GlobalOptions.err.println ("Can't write source of "+className+"."); diff --git a/jode/src/net/sf/jode/decompiler/ProgressListener.java b/jode/src/net/sf/jode/decompiler/ProgressListener.java index 3a7d454..9be0575 100644 --- a/jode/src/net/sf/jode/decompiler/ProgressListener.java +++ b/jode/src/net/sf/jode/decompiler/ProgressListener.java @@ -22,7 +22,7 @@ package net.sf.jode.decompiler; /** * This interface is used by jode to tell about its progress. You * supply an instance of this interface to the - * {@link Decompiler.decompile} method.
+ * {@link Decompiler#decompile} method.
* * @author Jochen Hoenicke * @version 1.0 */ diff --git a/jode/src/net/sf/jode/flow/FlowBlock.java b/jode/src/net/sf/jode/flow/FlowBlock.java index f874678..03dc7c4 100644 --- a/jode/src/net/sf/jode/flow/FlowBlock.java +++ b/jode/src/net/sf/jode/flow/FlowBlock.java @@ -150,7 +150,7 @@ public class FlowBlock { /** * The stack map. This tells how many objects are on stack at * begin of the flow block, and to what locals they are maped. - * @see mapStackToLocal + * @see #mapStackToLocal */ VariableStack stackMap; diff --git a/jode/src/net/sf/jode/flow/Jump.java b/jode/src/net/sf/jode/flow/Jump.java index 3207451..d381ff5 100644 --- a/jode/src/net/sf/jode/flow/Jump.java +++ b/jode/src/net/sf/jode/flow/Jump.java @@ -42,7 +42,7 @@ public class Jump { /** * The stack map. This tells how many objects are on stack at * begin of the flow block, and to what locals they are maped. - * @see FlowBlock.mapStackToLocal + * @see FlowBlock#mapStackToLocal */ VariableStack stackMap; diff --git a/jode/src/net/sf/jode/flow/VariableStack.java b/jode/src/net/sf/jode/flow/VariableStack.java index b37173f..6bee9be 100644 --- a/jode/src/net/sf/jode/flow/VariableStack.java +++ b/jode/src/net/sf/jode/flow/VariableStack.java @@ -29,8 +29,8 @@ import net.sf.jode.expr.Operator; * of the PUSH / stack_i statements.

* * This class is immutable, but note, that the local infos can get merged. - * @see FlowBlock.mapStackToLocal - * @see FlowBlock.removePush + * @see FlowBlock#mapStackToLocal + * @see FlowBlock#removePush */ public class VariableStack { public final static VariableStack EMPTY = new VariableStack(); diff --git a/jode/src/net/sf/jode/jvm/CodeVerifier.java b/jode/src/net/sf/jode/jvm/CodeVerifier.java index 9ea169d..3dd33c2 100644 --- a/jode/src/net/sf/jode/jvm/CodeVerifier.java +++ b/jode/src/net/sf/jode/jvm/CodeVerifier.java @@ -1286,19 +1286,19 @@ public class CodeVerifier implements Opcodes { Block block = blocks[blockNr]; VerifyInfo info = (VerifyInfo) verifyInfos[blockNr].clone(); - Handler[] catchers = block.getCatchers(); - if (catchers.length > 0) { + Handler[] handlers = block.getHandlers(); + if (handlers.length > 0) { VerifyInfo excInfo = (VerifyInfo) info.clone(); excInfo.stackHeight = 1; - for (int i=0; i < catchers.length; i++) { - String type = catchers[i].getType(); + for (int i=0; i < handlers.length; i++) { + String type = handlers[i].getType(); if (type != null) excInfo.stack[0] = tType("L" + type.replace('.', '/') + ";"); else excInfo.stack[0] = tType("Ljava/lang/Throwable;"); - Block catcher = catchers[i].getCatcher(); + Block catcher = handlers[i].getCatcher(); if (mergeInfo(catcher, excInfo)) todoSet.set(catcher.getBlockNr()); } @@ -1311,10 +1311,10 @@ public class CodeVerifier implements Opcodes { instr = (Instruction) iter.next(); modelEffect(instr, info); - if (catchers.length > 0 && instr.isStore()) { - for (int i=0; i < catchers.length; i++) { + if (handlers.length > 0 && instr.isStore()) { + for (int i=0; i < handlers.length; i++) { int slot = instr.getLocalSlot(); - Block catcher = catchers[i].getCatcher(); + Block catcher = handlers[i].getCatcher(); int catcherNr = catcher.getBlockNr(); VerifyInfo oldInfo = verifyInfos[catcherNr]; Type newType = oldInfo.locals[slot] diff --git a/jode/src/net/sf/jode/jvm/Interpreter.java b/jode/src/net/sf/jode/jvm/Interpreter.java index dcbdf58..0959a74 100644 --- a/jode/src/net/sf/jode/jvm/Interpreter.java +++ b/jode/src/net/sf/jode/jvm/Interpreter.java @@ -108,7 +108,7 @@ public class Interpreter implements Opcodes { return Void.TYPE; iter = Arrays.asList(nextBlock.getInstructions()).iterator(); succs = nextBlock.getSuccs(); - handlers = nextBlock.getCatchers(); + handlers = nextBlock.getHandlers(); nextBlock = succs.length > 0 ? succs[succs.length - 1] : null; } try { diff --git a/jode/src/net/sf/jode/obfuscator/ClassIdentifier.java b/jode/src/net/sf/jode/obfuscator/ClassIdentifier.java index ad4c54a..e8713de 100644 --- a/jode/src/net/sf/jode/obfuscator/ClassIdentifier.java +++ b/jode/src/net/sf/jode/obfuscator/ClassIdentifier.java @@ -357,6 +357,15 @@ public class ClassIdentifier extends Identifier { } } else { // all methods and fields in superclass are preserved! + try { + superclass.load(ClassInfo.DECLARATIONS); + } catch (IOException ex) { + throw new RuntimeException + ("Can't read declarations of class " + + superclass.getName() + + ": " + ex.getMessage()); + } + MethodInfo[] topmethods = superclass.getMethods(); for (int i=0; i< topmethods.length; i++) { // all virtual methods in superclass may be @@ -433,14 +442,12 @@ public class ClassIdentifier extends Identifier { info.setSourceFile(null); } if ((Main.stripping & Main.STRIP_INNERINFO) != 0) { - info.setClasses(null); + info.setClasses(new ClassInfo[0]); info.setOuterClass(null); - info.setExtraClasses(null); } // load inner classes ClassInfo outerClass = info.getOuterClass(); ClassInfo[] innerClasses = info.getClasses(); - ClassInfo[] extraClasses = info.getExtraClasses(); if (outerClass != null) Main.getClassBundle().getClassIdentifier(outerClass.getName()); @@ -450,13 +457,6 @@ public class ClassIdentifier extends Identifier { .getClassIdentifier(innerClasses[i].getName()); } } - if (extraClasses != null) { - for (int i=0; i < extraClasses.length; i++) { - System.err.println("ec["+i+"]:"+extraClasses[i].getName()); - Main.getClassBundle() - .getClassIdentifier(extraClasses[i].getName()); - } - } } /** @@ -515,6 +515,7 @@ public class ClassIdentifier extends Identifier { .getClassIdentifier(outerClass.getName()); if (outerIdent != null && outerIdent.isReachable()) break; + outerClass = outerClass.getOuterClass(); } } info.setOuterClass(outerClass); @@ -533,44 +534,13 @@ public class ClassIdentifier extends Identifier { } } - if (newInnerCount == 0) { - info.setClasses(null); - } else { - ClassInfo[] newInners = new ClassInfo[newInnerCount]; - int pos = 0; - for (int i=0; i 0) ? fullName + "."+ component : component; subFull = subFull.intern(); - if (ClassInfo.isPackage(subFull)) { + if (bundle.getClassPath().isPackage(subFull)) { ident = new PackageIdentifier(bundle, this, subFull, component); loadedClasses.put(component, ident); swappedClasses = null; if (loadOnDemand) ((PackageIdentifier) ident).setLoadOnDemand(); - } else if (ClassInfo.exists(subFull)) { + if (initialized) + ((PackageIdentifier) ident).initialize(); + } else if (bundle.getClassPath().existsClass(subFull)) { if (GlobalOptions.verboseLevel > 1) GlobalOptions.err.println("loading Class " +subFull); ident = new ClassIdentifier(this, subFull, component, @@ -162,7 +164,7 @@ public class PackageIdentifier extends Identifier { (fullName.length() > 0) ? fullName + "." : ""; /* Load all matching classes and packages */ Enumeration enum = - ClassInfo.getClassesAndPackages(getFullName()); + bundle.getClassPath().listClassesAndPackages(getFullName()); while (enum.hasMoreElements()) { String subclazz = ((String)enum.nextElement()).intern(); if (loadedClasses.containsKey(subclazz)) @@ -170,7 +172,7 @@ public class PackageIdentifier extends Identifier { String subFull = (fullNamePrefix + subclazz).intern(); if (matcher.matchesSub(this, subclazz)) { - if (ClassInfo.isPackage(subFull)) { + if (bundle.getClassPath().isPackage(subFull)) { if (GlobalOptions.verboseLevel > 0) GlobalOptions.err.println("loading Package " + subFull); @@ -180,6 +182,8 @@ public class PackageIdentifier extends Identifier { swappedClasses = null; if (loadOnDemand || matcher.matches(ident)) ident.setLoadOnDemand(); + if (initialized) + ((PackageIdentifier) ident).initialize(); } else { ClassIdentifier ident = new ClassIdentifier (this, subFull, subclazz, @@ -250,14 +254,14 @@ public class PackageIdentifier extends Identifier { String subFull = (fullName.length() > 0) ? fullName + "."+ name : name; subFull = subFull.intern(); - if (ClassInfo.isPackage(subFull)) { + if (bundle.getClassPath().isPackage(subFull)) { PackageIdentifier pack = new PackageIdentifier(bundle, this, subFull, name); loadedClasses.put(name, pack); swappedClasses = null; pack.setLoadOnDemand(); ident = pack; - } else if (!ClassInfo.exists(subFull)) { + } else if (!bundle.getClassPath().existsClass(subFull)) { GlobalOptions.err.println("Warning: Can't find class " + subFull); Thread.dumpStack(); @@ -280,7 +284,7 @@ public class PackageIdentifier extends Identifier { String subFull = (fullName.length() > 0) ? fullName + "."+ subpack : subpack; subFull = subFull.intern(); - if (ClassInfo.isPackage(subFull)) { + if (bundle.getClassPath().isPackage(subFull)) { pack = new PackageIdentifier(bundle, this, subFull, subpack); loadedClasses.put(subpack, pack); diff --git a/jode/src/net/sf/jode/obfuscator/modules/ConstantAnalyzer.java b/jode/src/net/sf/jode/obfuscator/modules/ConstantAnalyzer.java index 5ec0d1b..c388ea2 100644 --- a/jode/src/net/sf/jode/obfuscator/modules/ConstantAnalyzer.java +++ b/jode/src/net/sf/jode/obfuscator/modules/ConstantAnalyzer.java @@ -66,7 +66,7 @@ import java.util.ListIterator; * * * @author Jochen Hoenicke */ -public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { +public class ConstantAnalyzer extends SimpleAnalyzer { private static ConstantRuntimeEnvironment runtime = new ConstantRuntimeEnvironment(); @@ -188,7 +188,7 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { value = VOLATILE; for (Iterator i = listeners.iterator(); i.hasNext(); ) { ConstantListener l = (ConstantListener) i.next(); - System.err.println(" notifying: "+ l); +// System.err.println(" notifying: "+ l); l.constantChanged(); } listeners = null; @@ -217,130 +217,106 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { } } + /** + * This class handles information necessary for jsr analysis. + * Jsr is probably the most difficult opcode to handle, we have + * to keep track of changed locals, of nested jsrs, if there is + * a corresponding ret instruction and much more. + */ private final static class JsrInfo implements Cloneable { - BitSet usedLocals; BlockInfo jsrTarget; Collection callers; /** - * The dependent entries, that want to know if the bit set changed. - * This is either a StackLocalInfo (the ret info) or a single - * JsrInfo or a Collection of JsrInfos. + * The number of outer subroutines */ - Object dependent; + int jsrDepth; - public JsrInfo(BlockInfo target) { - jsrTarget = target; - callers = new ArrayList(); - usedLocals = new BitSet(); - } + /** + * The outer jsr info, or null if this is a top level jsr. + */ + JsrInfo outerJsr; - public JsrInfo copy() { - try { - JsrInfo result = (JsrInfo) clone(); - result.usedLocals = (BitSet) usedLocals.clone(); - addDependent(result); - return result; - } catch (CloneNotSupportedException ex) { - throw new IncompatibleClassChangeError(ex.getMessage()); - } - } + /** + * The locals used in the outer subroutine, or null if this is + * a top level jsr. + * The locals used inside this jsr are in the BlockInfo itself. + */ + BitSet usedInOuter; - private void addDependent(JsrInfo result) { - if (dependent == null || dependent == result) - dependent = result; - else if (dependent instanceof JsrInfo) { - Set newDeps = new HashSet(); - newDeps.add(dependent); - newDeps.add(result); - } else if (dependent instanceof Collection) { - ((Collection) dependent).add(result); - } - } + /** + * The info for the ret block of this subroutine. + */ + BlockInfo retInfo; - public boolean uses(int localSlot) { - return usedLocals.get(localSlot); + public JsrInfo(BlockInfo target, JsrInfo outer, BitSet used) { + jsrTarget = target; + if (outer != null) { + outerJsr = outer; + usedInOuter = (BitSet) used.clone(); + } + jsrDepth = (outer != null ? outer.jsrDepth + 1 : 0); + callers = new ArrayList(); } public void setRetInfo(BlockInfo retInfo) { - dependent = retInfo; + this.retInfo = retInfo; for (Iterator i = callers.iterator(); i.hasNext(); ) - retInfo.mergeRetLocals(this, (BlockInfo)i.next()); + ((BlockInfo)i.next()).mergeRetLocals(this, retInfo); } public void addCaller(BlockInfo caller) { if (callers.contains(caller)) return; callers.add(caller); - - if (dependent instanceof BlockInfo) { - BlockInfo retInfo = ((BlockInfo) dependent); - for (Iterator i = callers.iterator(); i.hasNext(); ) - retInfo.mergeRetLocals(this, caller); - } else if (dependent instanceof JsrInfo) { - ((JsrInfo) dependent).addCaller(caller); - } else if (dependent instanceof Collection) { - Iterator iter = ((Collection) dependent).iterator(); - while (iter.hasNext()) { - JsrInfo dep = (JsrInfo) iter.next(); - dep.addCaller(caller); - } - } + if (retInfo != null) + caller.mergeRetLocals(this, retInfo); } - public boolean hasRetInfo() { - if (dependent instanceof BlockInfo) - return true; - else if (dependent instanceof JsrInfo) - return ((JsrInfo) dependent).hasRetInfo(); - else if (dependent instanceof Collection) { - Iterator iter = ((Collection) dependent).iterator(); - while (iter.hasNext()) { - JsrInfo dep = (JsrInfo) iter.next(); - if (dep.hasRetInfo()) - return true; - } - } - return false; - } + public JsrInfo intersect(JsrInfo other, BitSet used) { + int otherDepth = other != null ? other.jsrDepth : -1; + JsrInfo isect = this; + int myDepth = jsrDepth; - public void addUsed(int localSlot) { - if (usedLocals.get(localSlot)) - return; - usedLocals.set(localSlot); - - if (dependent instanceof BlockInfo) { - BlockInfo retInfo = ((BlockInfo) dependent); - for (Iterator i = callers.iterator(); i.hasNext(); ) - retInfo.mergeRetLocals(this, (BlockInfo)i.next()); - } else if (dependent instanceof JsrInfo) { - ((JsrInfo) dependent).addUsed(localSlot); - } else if (dependent instanceof Collection) { - Iterator iter = ((Collection) dependent).iterator(); - while (iter.hasNext()) { - JsrInfo dep = (JsrInfo) iter.next(); - dep.addUsed(localSlot); + while (otherDepth > myDepth) { + if (other.usedInOuter != null) + used.or(other.usedInOuter); + other = other.outerJsr; + otherDepth--; + } + while (myDepth > otherDepth) { + if (isect.usedInOuter != null) + used.or(isect.usedInOuter); + isect = isect.outerJsr; + myDepth--; + } + while (isect != other) { + if (other.usedInOuter != null) { + used.or(other.usedInOuter); + used.or(isect.usedInOuter); } + other = other.outerJsr; + isect = isect.outerJsr; } + return isect; } - public void merge(JsrInfo o) { - o.addDependent(this); - for (int slot = 0; slot < o.usedLocals.size(); slot++) { - if (o.usedLocals.get(slot)) - addUsed(slot); - } - for (Iterator i = o.callers.iterator(); i.hasNext();) { - addCaller((BlockInfo) i.next()); - } + public void merge(JsrInfo outer, BitSet used) { + if (outerJsr != null) + outerJsr = outerJsr.intersect(outer, used); + if (outerJsr == null) + usedInOuter = null; + else + usedInOuter.or(used); } public String toString() { - StringBuffer sb = new StringBuffer(String.valueOf(jsrTarget)); - if (dependent instanceof BlockInfo) - sb.append("->").append(((BlockInfo) dependent).nr); - return sb.append(usedLocals) - .append('_').append(hashCode()).toString(); + StringBuffer sb = new StringBuffer(String.valueOf(jsrTarget.nr)); + if (retInfo != null) + sb.append("->").append(retInfo.nr); + if (outerJsr != null) + sb.append("used="+usedInOuter+",outer=["+outerJsr+"]"); + return sb.toString(); } } @@ -381,6 +357,7 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { locals[i] = original.locals[i].copy(); } stack = new ConstValue[original.stack.length]; + stackDepth = original.stackDepth; for (int i=0; i< stackDepth; i++) { if (original.stack[i] != null) stack[i] = original.stack[i].copy(); @@ -388,15 +365,17 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { } public StackLocalInfo poppush(int pops, ConstValue push) { - for (int i = pops; i>= 0; i--) + for (int i = pops; i > 0; i--) stack[--stackDepth] = null; + if (push == null) + throw new NullPointerException(); stack[stackDepth] = push; stackDepth += push.stackSize; return this; } public StackLocalInfo pop(int pops) { - for (int i = pops; i>= 0; i--) + for (int i = pops; i > 0; i--) stack[--stackDepth] = null; return this; } @@ -433,7 +412,7 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { } public ConstValue getStack(int depth) { - return stack[stack.length - depth]; + return stack[stackDepth - depth]; } public StackLocalInfo setLocal(int slot, ConstValue value) { @@ -468,8 +447,8 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { } public String toString() { - return "Locals: "+Arrays.asList(locals) - +"Stack: "+Arrays.asList(stack); + return "StackLocalInfo[locals="+Arrays.asList(locals) + +",stack="+Arrays.asList(stack)+",stackDepth="+stackDepth+"]"; } } @@ -530,10 +509,18 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { StackLocalInfo after; /** - * The JsrInfo of jsrs that must be visited to reach - * this block. + * The JsrInfo of the innermost surrounding subroutine. If + * before is null, this value is null and means unknown. If + * before is not null, a value of null means no surrounding + * subroutine. + */ + JsrInfo jsrInfo; + + /** + * The locals used between the jsr instruction for jsrInfo and + * the end of the current block; null if jsrInfo is null. */ - JsrInfo[] jsrInfos; + BitSet usedLocals; public BlockInfo(int nr, Block block) { this.nr = nr; @@ -549,36 +536,24 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { } public void mergeBefore(StackLocalInfo info, - JsrInfo[] newJsrInfos) { + JsrInfo newJsrInfo, + BitSet usedLocals) { if (before == null) { - System.err.println("mergeBefore:::"+info); +// System.err.println("mergeBefore:::"+info); before = new StackLocalInfo(info); - this.jsrInfos = new JsrInfo[newJsrInfos.length]; - for (int i = 0; i < newJsrInfos.length; i++) - jsrInfos[i] = newJsrInfos[i].copy(); + this.jsrInfo = newJsrInfo; + if (usedLocals != null) + this.usedLocals = (BitSet) usedLocals.clone(); modifiedQueue.enqueue(this); } else { - System.err.println("merging:::: "+before+":::AND:::"+info); +// System.err.println("merging:::: "+before+":::AND:::"+info); before.merge(info); - int newJsrCount = 0; - int ptr = 0; - for (int i = 0; i < jsrInfos.length; i++) { - for (int j = ptr; j < newJsrInfos.length; j++) { - if (newJsrInfos[j].jsrTarget - == jsrInfos[i].jsrTarget) { - jsrInfos[newJsrCount] = jsrInfos[i]; - jsrInfos[newJsrCount].merge(newJsrInfos[j]); - newJsrCount++; - ptr = j+1; - break; - } - } - } - if (newJsrCount < jsrInfos.length) { - JsrInfo[] newJsr = new JsrInfo[newJsrCount]; - System.arraycopy(jsrInfos, 0, newJsr, 0, newJsrCount); - jsrInfos = newJsr; - } + if (jsrInfo != null) + jsrInfo = jsrInfo.intersect(newJsrInfo, usedLocals); + if (jsrInfo == null) + this.usedLocals = null; + else + this.usedLocals.or(usedLocals); if (constantFlow >= 0) propagateAfter(); } @@ -590,17 +565,19 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { } public void useLocal(int slot) { - for (int i=0; i< jsrInfos.length; i++) - jsrInfos[i].addUsed(slot); + if (usedLocals != null) + usedLocals.set(slot); } - public void mergeRetLocals(JsrInfo myJsrInfo, BlockInfo caller) { + public void mergeRetLocals(JsrInfo myJsrInfo, BlockInfo retBlock) { if (constantFlow == 0) { Instruction[] instrs = block.getInstructions(); // remove the constantFlow info constantInfos.remove(instrs[instrs.length-1]); + constantFlow = -1; } - Block nextBlock = caller.block.getSuccs()[1]; + + Block nextBlock = block.getSuccs()[1]; if (nextBlock == null) { /* The calling jsr is just before a return. We don't * have to fuzz around, since nobody is interested in @@ -610,21 +587,29 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { } ConstValue[] newLocals = (ConstValue[]) after.locals.clone(); + BitSet nextUsed = new BitSet(); for (int slot = 0; slot < newLocals.length; slot++) { - if (!myJsrInfo.uses(slot)) - newLocals[slot] = caller.after.locals[slot]; - else - caller.useLocal(slot); + if (retBlock.usedLocals.get(slot)) { + newLocals[slot] = retBlock.after.locals[slot]; + if (nextUsed != null) + nextUsed.set(slot); + } } StackLocalInfo nextInfo = new StackLocalInfo(after.stack, newLocals, after.stackDepth); - int newCount = jsrInfos.length - 1; - while (jsrInfos[newCount].jsrTarget != myJsrInfo.jsrTarget) - newCount--; - JsrInfo[] retJsrInfos = new JsrInfo[newCount]; - System.arraycopy(jsrInfos, 0, - retJsrInfos, 0, newCount); - infos[nextBlock.getBlockNr()].mergeBefore(nextInfo, retJsrInfos); + JsrInfo nextJsrInfo = null; + if (usedLocals != null) + nextUsed.or(usedLocals); + if (myJsrInfo.usedInOuter != null) + nextUsed.or(myJsrInfo.usedInOuter); + if (jsrInfo != null) + nextJsrInfo = jsrInfo.intersect(myJsrInfo.outerJsr, + nextUsed); + if (nextJsrInfo == null) + nextUsed = null; + + infos[nextBlock.getBlockNr()].mergeBefore(nextInfo, + nextJsrInfo, nextUsed); } public void propagateAfter() { @@ -692,7 +677,7 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { Block constantSucc = block.getSuccs()[constantFlow]; if (constantSucc != null) infos[constantSucc.getBlockNr()] - .mergeBefore(nextInfo, jsrInfos); + .mergeBefore(nextInfo, jsrInfo, usedLocals); } else { constantInfos.remove(instr); for (int i=0; i < 2; i++) { @@ -700,7 +685,8 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { Block succ = block.getSuccs()[i]; if (succ != null) infos[succ.getBlockNr()] - .mergeBefore(nextInfo, jsrInfos); + .mergeBefore(nextInfo, + jsrInfo, usedLocals); } } constantFlow = -1; @@ -727,7 +713,7 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { Block constantSucc = block.getSuccs()[constantFlow]; if (constantSucc != null) infos[constantSucc.getBlockNr()] - .mergeBefore(nextInfo, jsrInfos); + .mergeBefore(nextInfo, jsrInfo, usedLocals); } else { constantInfos.remove(instr); Block[] succs = block.getSuccs(); @@ -735,7 +721,8 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { if (i != constantFlow) { if (succs[i] != null) infos[succs[i].getBlockNr()] - .mergeBefore(nextInfo, jsrInfos); + .mergeBefore(nextInfo, jsrInfo, + usedLocals); } } constantFlow = -1; @@ -743,40 +730,51 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { break; } case opc_jsr: { - // XXXXXXXXXXXX System.err - dumpBlockInfo(GlobalOptions.err); - GlobalOptions.err.println(instr); - - BlockInfo target = infos[block.getSuccs()[0].getBlockNr()]; - ConstValue result = new ConstValue(target); - int jsrInfosCount = jsrInfos.length; - for (int i=0; i< jsrInfosCount; i++) { - if (jsrInfos[i].jsrTarget == target) { - // recursive jsrs are forbidden. But maybe the jsr - // just terminated without a ret. - jsrInfosCount = i; - } - } - JsrInfo[] newJsrInfos = new JsrInfo[jsrInfosCount + 1]; - System.arraycopy(jsrInfos, 0, newJsrInfos, 0, jsrInfosCount); - newJsrInfos[jsrInfosCount] = new JsrInfo(target); - newJsrInfos[jsrInfosCount].addCaller(this); + /* Assume there is no ret for this jsr. If the ret + * was already found this info will be corrected + * immediately by the jsrInfo.addCaller. + */ constantFlow = 0; ConstantInfo constInfo = new ConstantInfo (CONSTANTFLOW, new Integer(0)); constantInfos.put(instr, constInfo); + + + BlockInfo target = infos[block.getSuccs()[0].getBlockNr()]; + ConstValue result = new ConstValue(target); + + JsrInfo newJsrInfo; + BitSet newUsed; + if (target.before == null) { + /* The info for this jsr target wasn't created before. */ + newJsrInfo = new JsrInfo(target, jsrInfo, usedLocals); + newJsrInfo.addCaller(this); + newUsed = new BitSet(); + } else if (target.jsrInfo == null + || target.jsrInfo.jsrTarget != target) { + /* The jsr exits instantenously. */ + newJsrInfo = jsrInfo; + newUsed = usedLocals; + } else { + /* Normal case, merge jsr infos. */ + newJsrInfo = target.jsrInfo; + newJsrInfo.merge(jsrInfo, usedLocals); + newJsrInfo.addCaller(this); + newUsed = new BitSet(); + } + target.mergeBefore(after.copy().poppush(0, result), - newJsrInfos); + newJsrInfo, newUsed); break; } case opc_ret: { ConstValue result = after.getLocal(instr.getLocalSlot()); BlockInfo jsrTarget = (BlockInfo) result.value; - - int i = jsrInfos.length-1; - while (jsrInfos[i].jsrTarget != jsrTarget) - i--; - jsrInfos[i].setRetInfo(this); + while (jsrInfo.jsrTarget != jsrTarget) { + usedLocals.or(jsrInfo.usedInOuter); + jsrInfo = jsrInfo.outerJsr; + } + jsrInfo.setRetInfo(this); break; } case opc_ireturn: case opc_lreturn: @@ -787,15 +785,655 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { default: { Block succ = block.getSuccs()[0]; if (succ != null) - infos[succ.getBlockNr()].mergeBefore(after, jsrInfos); + infos[succ.getBlockNr()].mergeBefore + (after, jsrInfo, usedLocals); } } } + + StackLocalInfo handleOpcode(Instruction instr, StackLocalInfo info) { + constantInfos.remove(instr); + int opcode = instr.getOpcode(); + ConstValue result; + switch (opcode) { + case opc_nop: + return info.pop(0); + + case opc_ldc: + case opc_ldc2_w: + result = new ConstValue(instr.getConstant()); + return info.poppush(0, result); + + case opc_iload: case opc_lload: + case opc_fload: case opc_dload: case opc_aload: + result = info.getLocal(instr.getLocalSlot()); + if (result.value != ConstValue.VOLATILE) { + ConstantInfo constInfo + = new ConstantInfo(CONSTANT, result.value); + result.addConstantListener(constInfo); + constantInfos.put(instr, constInfo); + } + return info.poppush(0, result) + .setLocal(instr.getLocalSlot(), result); + case opc_iaload: case opc_laload: + case opc_faload: case opc_daload: case opc_aaload: + case opc_baload: case opc_caload: case opc_saload: { + result = unknownValue[(opcode == opc_laload + || opcode == opc_daload) ? 1 : 0]; + return info.poppush(2, result); + } + case opc_istore: case opc_fstore: case opc_astore: { + int slot = instr.getLocalSlot(); + useLocal(slot); + result = info.getStack(1); + return info.pop(1).setLocal(slot, result); + } + case opc_lstore: case opc_dstore: { + int slot = instr.getLocalSlot(); + useLocal(slot); + useLocal(slot + 1); + result = info.getStack(2); + return info.pop(2).setLocal(slot, result); + } + case opc_iastore: case opc_lastore: + case opc_fastore: case opc_dastore: case opc_aastore: + case opc_bastore: case opc_castore: case opc_sastore: { + int size = (opcode == opc_lastore + || opcode == opc_dastore) ? 2 : 1; + return info.pop(2+size); + } + case opc_pop: + return info.pop(1); + case opc_pop2: + return info.pop(2); + + case opc_dup: case opc_dup_x1: case opc_dup_x2: + case opc_dup2: case opc_dup2_x1: case opc_dup2_x2: + return info.dup((opcode - (opc_dup - 3)) / 3, + (opcode - (opc_dup - 3)) % 3); + case opc_swap: + return info.swap(); + + case opc_iadd: case opc_ladd: case opc_fadd: case opc_dadd: + case opc_isub: case opc_lsub: case opc_fsub: case opc_dsub: + case opc_imul: case opc_lmul: case opc_fmul: case opc_dmul: + case opc_idiv: case opc_ldiv: case opc_fdiv: case opc_ddiv: + case opc_irem: case opc_lrem: case opc_frem: case opc_drem: + case opc_iand: case opc_land: + case opc_ior : case opc_lor : + case opc_ixor: case opc_lxor: { + int size = 1 + (opcode - opc_iadd & 1); + ConstValue value1 = info.getStack(2*size); + ConstValue value2 = info.getStack(1*size); + boolean known = value1.value != ConstValue.VOLATILE + && value2.value != ConstValue.VOLATILE; + if (known) { + if (((opcode == opc_idiv || opcode == opc_irem) + && ((Integer)value2.value).intValue() == 0) + || ((opcode == opc_ldiv || opcode == opc_lrem) + && ((Long)value2.value).longValue() == 0)) + known = false; + } + if (known) { + Object newValue; + switch (opcode) { + case opc_iadd: + newValue = new Integer + (((Integer)value1.value).intValue() + + ((Integer)value2.value).intValue()); + break; + case opc_isub: + newValue = new Integer + (((Integer)value1.value).intValue() + - ((Integer)value2.value).intValue()); + break; + case opc_imul: + newValue = new Integer + (((Integer)value1.value).intValue() + * ((Integer)value2.value).intValue()); + break; + case opc_idiv: + newValue = new Integer + (((Integer)value1.value).intValue() + / ((Integer)value2.value).intValue()); + break; + case opc_irem: + newValue = new Integer + (((Integer)value1.value).intValue() + % ((Integer)value2.value).intValue()); + break; + case opc_iand: + newValue = new Integer + (((Integer)value1.value).intValue() + & ((Integer)value2.value).intValue()); + break; + case opc_ior: + newValue = new Integer + (((Integer)value1.value).intValue() + | ((Integer)value2.value).intValue()); + break; + case opc_ixor: + newValue = new Integer + (((Integer)value1.value).intValue() + ^ ((Integer)value2.value).intValue()); + break; + + case opc_ladd: + newValue = new Long + (((Long)value1.value).longValue() + + ((Long)value2.value).longValue()); + break; + case opc_lsub: + newValue = new Long + (((Long)value1.value).longValue() + - ((Long)value2.value).longValue()); + break; + case opc_lmul: + newValue = new Long + (((Long)value1.value).longValue() + * ((Long)value2.value).longValue()); + break; + case opc_ldiv: + newValue = new Long + (((Long)value1.value).longValue() + / ((Long)value2.value).longValue()); + break; + case opc_lrem: + newValue = new Long + (((Long)value1.value).longValue() + % ((Long)value2.value).longValue()); + break; + case opc_land: + newValue = new Long + (((Long)value1.value).longValue() + & ((Long)value2.value).longValue()); + break; + case opc_lor: + newValue = new Long + (((Long)value1.value).longValue() + | ((Long)value2.value).longValue()); + break; + case opc_lxor: + newValue = new Long + (((Long)value1.value).longValue() + ^ ((Long)value2.value).longValue()); + break; + + case opc_fadd: + newValue = new Float + (((Float)value1.value).floatValue() + + ((Float)value2.value).floatValue()); + break; + case opc_fsub: + newValue = new Float + (((Float)value1.value).floatValue() + - ((Float)value2.value).floatValue()); + break; + case opc_fmul: + newValue = new Float + (((Float)value1.value).floatValue() + * ((Float)value2.value).floatValue()); + break; + case opc_fdiv: + newValue = new Float + (((Float)value1.value).floatValue() + / ((Float)value2.value).floatValue()); + break; + case opc_frem: + newValue = new Float + (((Float)value1.value).floatValue() + % ((Float)value2.value).floatValue()); + break; + + case opc_dadd: + newValue = new Double + (((Double)value1.value).doubleValue() + + ((Double)value2.value).doubleValue()); + break; + case opc_dsub: + newValue = new Double + (((Double)value1.value).doubleValue() + - ((Double)value2.value).doubleValue()); + break; + case opc_dmul: + newValue = new Double + (((Double)value1.value).doubleValue() + * ((Double)value2.value).doubleValue()); + break; + case opc_ddiv: + newValue = new Double + (((Double)value1.value).doubleValue() + / ((Double)value2.value).doubleValue()); + break; + case opc_drem: + newValue = new Double + (((Double)value1.value).doubleValue() + % ((Double)value2.value).doubleValue()); + break; + default: + throw new InternalError("Can't happen."); + } + ConstantInfo constInfo = new ConstantInfo(CONSTANT, newValue); + constantInfos.put(instr, constInfo); + result = new ConstValue(newValue); + result.addConstantListener(constInfo); + value1.addConstantListener(result); + value2.addConstantListener(result); + } else + result = unknownValue[size-1]; + return info.poppush(2*size, result); + } + case opc_ineg: case opc_lneg: case opc_fneg: case opc_dneg: { + int size = 1 + (opcode - opc_ineg & 1); + ConstValue value = info.getStack(size); + if (value.value != ConstValue.VOLATILE) { + Object newValue; + switch (opcode) { + case opc_ineg: + newValue = new Integer + (-((Integer)value.value).intValue()); + break; + case opc_lneg: + newValue = new Long + (- ((Long)value.value).longValue()); + break; + case opc_fneg: + newValue = new Float + (- ((Float)value.value).floatValue()); + break; + case opc_dneg: + newValue = new Double + (- ((Double)value.value).doubleValue()); + break; + default: + throw new InternalError("Can't happen."); + } + ConstantInfo constInfo = new ConstantInfo(CONSTANT, newValue); + constantInfos.put(instr, constInfo); + result = new ConstValue(newValue); + result.addConstantListener(constInfo); + value.addConstantListener(result); + } else + result = unknownValue[size-1]; + return info.poppush(size, result); + } + case opc_ishl: case opc_lshl: + case opc_ishr: case opc_lshr: + case opc_iushr: case opc_lushr: { + int size = 1 + (opcode - opc_iadd & 1); + ConstValue value1 = info.getStack(size+1); + ConstValue value2 = info.getStack(1); + if (value1.value != ConstValue.VOLATILE + && value2.value != ConstValue.VOLATILE) { + Object newValue; + switch (opcode) { + case opc_ishl: + newValue = new Integer + (((Integer)value1.value).intValue() + << ((Integer)value2.value).intValue()); + break; + case opc_ishr: + newValue = new Integer + (((Integer)value1.value).intValue() + >> ((Integer)value2.value).intValue()); + break; + case opc_iushr: + newValue = new Integer + (((Integer)value1.value).intValue() + >>> ((Integer)value2.value).intValue()); + break; + + case opc_lshl: + newValue = new Long + (((Long)value1.value).longValue() + << ((Integer)value2.value).intValue()); + break; + case opc_lshr: + newValue = new Long + (((Long)value1.value).longValue() + >> ((Integer)value2.value).intValue()); + break; + case opc_lushr: + newValue = new Long + (((Long)value1.value).longValue() + >>> ((Integer)value2.value).intValue()); + break; + default: + throw new InternalError("Can't happen."); + } + ConstantInfo constInfo = new ConstantInfo(CONSTANT, newValue); + constantInfos.put(instr, constInfo); + result = new ConstValue(newValue); + result.addConstantListener(constInfo); + value1.addConstantListener(result); + value2.addConstantListener(result); + } else + result = unknownValue[size-1]; + return info.poppush(size+1, result); + } + case opc_iinc: { + int slot = instr.getLocalSlot(); + useLocal(slot); + ConstValue local = info.getLocal(slot); + if (local.value != ConstValue.VOLATILE) { + result = new ConstValue + (new Integer(((Integer)local.value).intValue() + + instr.getIncrement())); + local.addConstantListener(result); + } else + result = unknownValue[0]; + return info.setLocal(instr.getLocalSlot(), result); + } + case opc_i2l: case opc_i2f: case opc_i2d: + case opc_l2i: case opc_l2f: case opc_l2d: + case opc_f2i: case opc_f2l: case opc_f2d: + case opc_d2i: case opc_d2l: case opc_d2f: { + int insize = 1 + ((opcode - opc_i2l) / 3 & 1); + ConstValue stack = info.getStack(insize); + if (stack.value != ConstValue.VOLATILE) { + Object newVal; + switch(opcode) { + case opc_l2i: case opc_f2i: case opc_d2i: + newVal = new Integer(((Number)stack.value).intValue()); + break; + case opc_i2l: case opc_f2l: case opc_d2l: + newVal = new Long(((Number)stack.value).longValue()); + break; + case opc_i2f: case opc_l2f: case opc_d2f: + newVal = new Float(((Number)stack.value).floatValue()); + break; + case opc_i2d: case opc_l2d: case opc_f2d: + newVal = new Double(((Number)stack.value).doubleValue()); + break; + default: + throw new InternalError("Can't happen."); + } + ConstantInfo constInfo = new ConstantInfo(CONSTANT, newVal); + constantInfos.put(instr, constInfo); + result = new ConstValue(newVal); + result.addConstantListener(constInfo); + stack.addConstantListener(result); + } else { + switch (opcode) { + case opc_i2l: case opc_f2l: case opc_d2l: + case opc_i2d: case opc_l2d: case opc_f2d: + result = unknownValue[1]; + break; + default: + result = unknownValue[0]; + } + } + return info.poppush(insize, result); + } + case opc_i2b: case opc_i2c: case opc_i2s: { + ConstValue stack = info.getStack(1); + if (stack.value != ConstValue.VOLATILE) { + int val = ((Integer)stack.value).intValue(); + switch(opcode) { + case opc_i2b: + val = (byte) val; + break; + case opc_i2c: + val = (char) val; + break; + case opc_i2s: + val = (short) val; + break; + } + Integer newVal = new Integer(val); + ConstantInfo constInfo = new ConstantInfo(CONSTANT, newVal); + constantInfos.put(instr, constInfo); + result = new ConstValue(newVal); + stack.addConstantListener(constInfo); + stack.addConstantListener(result); + } else + result = unknownValue[0]; + return info.poppush(1, result); + } + case opc_lcmp: { + ConstValue val1 = info.getStack(4); + ConstValue val2 = info.getStack(2); + if (val1.value != ConstValue.VOLATILE + && val2.value != ConstValue.VOLATILE) { + long value1 = ((Long) val1.value).longValue(); + long value2 = ((Long) val1.value).longValue(); + Integer newVal = new Integer(value1 == value2 ? 0 + : value1 < value2 ? -1 : 1); + ConstantInfo constInfo = new ConstantInfo(CONSTANT, newVal); + constantInfos.put(instr, constInfo); + result = new ConstValue(newVal); + result.addConstantListener(constInfo); + val1.addConstantListener(result); + val2.addConstantListener(result); + } else + result = unknownValue[0]; + return info.poppush(4, result); + } + case opc_fcmpl: case opc_fcmpg: { + ConstValue val1 = info.getStack(2); + ConstValue val2 = info.getStack(1); + if (val1.value != ConstValue.VOLATILE + && val2.value != ConstValue.VOLATILE) { + float value1 = ((Float) val1.value).floatValue(); + float value2 = ((Float) val1.value).floatValue(); + Integer newVal = new Integer + (value1 == value2 ? 0 + : ( opcode == opc_fcmpg + ? (value1 < value2 ? -1 : 1) + : (value1 > value2 ? 1 : -1))); + ConstantInfo constInfo = new ConstantInfo(CONSTANT, newVal); + constantInfos.put(instr, constInfo); + result = new ConstValue(newVal); + result.addConstantListener(constInfo); + val1.addConstantListener(result); + val2.addConstantListener(result); + } else + result = unknownValue[0]; + return info.poppush(2, result); + } + case opc_dcmpl: case opc_dcmpg: { + ConstValue val1 = info.getStack(4); + ConstValue val2 = info.getStack(2); + if (val1.value != ConstValue.VOLATILE + && val2.value != ConstValue.VOLATILE) { + double value1 = ((Double) val1.value).doubleValue(); + double value2 = ((Double) val1.value).doubleValue(); + Integer newVal = new Integer + (value1 == value2 ? 0 + : ( opcode == opc_dcmpg + ? (value1 < value2 ? -1 : 1) + : (value1 > value2 ? 1 : -1))); + ConstantInfo constInfo = new ConstantInfo(CONSTANT, newVal); + constantInfos.put(instr, constInfo); + result = new ConstValue(newVal); + result.addConstantListener(constInfo); + val1.addConstantListener(result); + val2.addConstantListener(result); + } else + result = unknownValue[0]; + return info.poppush(4, result); + } + case opc_ifeq: case opc_ifne: + case opc_iflt: case opc_ifge: + case opc_ifgt: case opc_ifle: + case opc_if_icmpeq: case opc_if_icmpne: + case opc_if_icmplt: case opc_if_icmpge: + case opc_if_icmpgt: case opc_if_icmple: + case opc_if_acmpeq: case opc_if_acmpne: + case opc_ifnull: case opc_ifnonnull: + case opc_lookupswitch: + case opc_ireturn: case opc_lreturn: + case opc_freturn: case opc_dreturn: case opc_areturn: + case opc_athrow: + case opc_jsr: + case opc_ret: + case opc_goto: + case opc_return: + return info; + + case opc_putstatic: + case opc_putfield: { + final FieldIdentifier fi + = (FieldIdentifier) canonizeReference(instr); + Reference ref = instr.getReference(); + int size = TypeSignature.getTypeSize(ref.getType()); + if (fi != null && !fi.isNotConstant()) { + ConstValue stacktop = info.getStack(size); + Object fieldVal = fi.getConstant(); + if (fieldVal == null) + fieldVal = TypeSignature.getDefaultValue(ref.getType()); + if (stacktop.value == null ? fieldVal == null + : stacktop.value.equals(fieldVal)) { + stacktop.addConstantListener(new ConstantListener() { + public void constantChanged() { + fieldNotConstant(fi); + } + }); + } else { + fieldNotConstant(fi); + } + } + size += (opcode == opc_putstatic) ? 0 : 1; + return info.pop(size); + } + case opc_getstatic: + case opc_getfield: { + int size = (opcode == opc_getstatic) ? 0 : 1; + FieldIdentifier fi = (FieldIdentifier) canonizeReference(instr); + Reference ref = instr.getReference(); + int typesize = TypeSignature.getTypeSize(ref.getType()); + if (fi != null) { + if (fi.isNotConstant()) { + fi.setReachable(); + result = unknownValue[typesize - 1]; + } else { + Object obj = fi.getConstant(); + if (obj == null) + obj = TypeSignature.getDefaultValue(ref.getType()); + ConstantInfo constInfo = new ConstantInfo(CONSTANT, obj); + constantInfos.put(instr, constInfo); + result = new ConstValue(obj); + fi.addFieldListener(methodIdent); + ConstValue prev = (ConstValue) fieldDependencies.get(fi); + if (prev != null) + prev.addConstantListener(result); + else + fieldDependencies.put(fi, result); + } + } else + result = unknownValue[typesize - 1]; + return info.poppush(size, result); + } + case opc_invokespecial: + case opc_invokestatic: + case opc_invokeinterface: + case opc_invokevirtual: { + canonizeReference(instr); + Reference ref = instr.getReference(); + boolean constant = true; + int size = 0; + Object cls = null; + String[] paramTypes + = TypeSignature.getParameterTypes(ref.getType()); + Object[] args = new Object[paramTypes.length]; + ConstValue clsValue = null; + ConstValue[] argValues = new ConstValue[paramTypes.length]; + + for (int i = paramTypes.length - 1; i >= 0; i--) { + size += TypeSignature.getTypeSize(paramTypes[i]); + Object value = (argValues[i] = info.getStack(size)).value; + if (value != ConstValue.VOLATILE) + args[i] = value; + else + constant = false; + } + if (opcode != opc_invokestatic) { + size++; + clsValue = info.getStack(size); + cls = clsValue.value; + if (cls == ConstValue.VOLATILE || cls == null) + constant = false; + } + String retType = TypeSignature.getReturnType(ref.getType()); + if (retType.equals("V")) { + handleReference(ref, opcode == opc_invokevirtual + || opcode == opc_invokeinterface); + return info.pop(size); + } + if (constant && !runtime.isWhite(retType)) { + /* This is not a valid constant type */ + constant = false; + } + Object methodResult = null; + if (constant) { + try { + methodResult = runtime.invokeMethod + (ref, opcode != opc_invokespecial, cls, args); + } catch (InterpreterException ex) { + constant = false; + if (net.sf.jode.GlobalOptions.verboseLevel > 3) + GlobalOptions.err.println("Can't interpret "+ref+": " + + ex.getMessage()); + /* result is not constant */ + } catch (InvocationTargetException ex) { + constant = false; + if (net.sf.jode.GlobalOptions.verboseLevel > 3) + GlobalOptions.err.println("Method "+ref + +" throwed exception: " + + ex.getTargetException()); + /* method always throws exception ? */ + } + } + ConstValue returnVal; + if (!constant) { + handleReference(ref, opcode == opc_invokevirtual + || opcode == opc_invokeinterface); + int retsize = TypeSignature.getTypeSize(retType); + returnVal = unknownValue[retsize - 1]; + } else { + ConstantInfo constInfo = + new ConstantInfo(CONSTANT, methodResult); + constantInfos.put(instr, constInfo); + returnVal = new ConstValue(methodResult); + returnVal.addConstantListener(constInfo); + if (clsValue != null) + clsValue.addConstantListener(returnVal); + for (int i=0; i< argValues.length; i++) + argValues[i].addConstantListener(returnVal); + } + return info.poppush(size, returnVal); + } + + case opc_new: { + handleClass(instr.getClazzType()); + return info.poppush(0, unknownValue[0]); + } + case opc_arraylength: { + return info.poppush(1, unknownValue[0]); + } + case opc_checkcast: { + handleClass(instr.getClazzType()); + return info.pop(0); + } + case opc_instanceof: { + handleClass(instr.getClazzType()); + return info.poppush(1, unknownValue[0]); + } + case opc_monitorenter: + case opc_monitorexit: + return info.pop(1); + case opc_multianewarray: + handleClass(instr.getClazzType()); + return info.poppush(instr.getDimensions(), unknownValue[0]); + default: + throw new IllegalArgumentException("Invalid opcode "+opcode); + } + } + public void analyze() { - System.err.println("analyze:"); StackLocalInfo info = before.copy(); - Handler[] handlers = block.getCatchers(); + Handler[] handlers = block.getHandlers(); if (handlers.length > 0) { ConstValue[] newStack = new ConstValue[info.stack.length]; @@ -809,14 +1447,14 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { (handlers[i].getType()); infos[handlers[i].getCatcher().getBlockNr()] - .mergeBefore(catchInfo, jsrInfos); + .mergeBefore(catchInfo, jsrInfo, usedLocals); } } Instruction[] instrs = block.getInstructions(); for (int idx = 0 ; idx < instrs.length; idx++) { Instruction instr = instrs[idx]; - info = handleOpcode(instr, info, this); + info = handleOpcode(instr, info); if (instr.isStore() && handlers.length > 0) { int slot = instr.getLocalSlot(); ConstValue newValue = info.locals[slot]; @@ -830,12 +1468,15 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { } } after = info; - dumpInfo(GlobalOptions.err); propagateAfter(); } public void dumpInfo(PrintWriter output) { output.println("/-["+nr+"]-"+before); + if (constantFlow >= 0) + output.println("| constantFlow: "+constantFlow); + if (jsrInfo != null) + output.println("| used: "+usedLocals+" JSR: "+jsrInfo); block.dumpCode(output); output.println("\\-["+nr+"]-"+after); } @@ -881,74 +1522,6 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { fi.setNotConstant(); } - private ClassInfo canonizeIfaceRef(ClassInfo clazz, Reference ref) { - while (clazz != null) { - if (clazz.findMethod(ref.getName(), ref.getType()) != null) - return clazz; - ClassInfo[] ifaces = clazz.getInterfaces(); - for (int i = 0; i < ifaces.length; i++) { - ClassInfo realClass = canonizeIfaceRef(ifaces[i], ref); - if (realClass != null) - return realClass; - } - clazz = clazz.getSuperclass(); - } - return null; - } - - private Identifier canonizeReference(Instruction instr) { - Reference ref = instr.getReference(); - Identifier ident = Main.getClassBundle().getIdentifier(ref); - String clName = ref.getClazz(); - String realClazzName; - if (ident != null) { - ClassIdentifier clazz = (ClassIdentifier)ident.getParent(); - realClazzName = "L" + (clazz.getFullName() - .replace('.', '/')) + ";"; - } else { - /* We have to look at the ClassInfo's instead, to - * point to the right method. - */ - ClassInfo clazz; - if (clName.charAt(0) == '[') { - /* Arrays don't define new methods (well clone(), - * but that can be ignored). - */ - clazz = ClassInfo.forName("java.lang.Object"); - } else { - clazz = ClassInfo.forName - (clName.substring(1, clName.length()-1) - .replace('/','.')); - } - if (instr.getOpcode() == opc_invokeinterface) { - clazz = canonizeIfaceRef(clazz, ref); - } else if (instr.getOpcode() >= opc_invokevirtual) { - while (clazz != null - && clazz.findMethod(ref.getName(), - ref.getType()) == null) - clazz = clazz.getSuperclass(); - } else { - while (clazz != null - && clazz.findField(ref.getName(), - ref.getType()) == null) - clazz = clazz.getSuperclass(); - } - - if (clazz == null) { - GlobalOptions.err.println("WARNING: Can't find reference: " - +ref); - realClazzName = clName; - } else - realClazzName = "L" + clazz.getName().replace('.', '/') + ";"; - } - if (!realClazzName.equals(ref.getClazz())) { - ref = Reference.getReference(realClazzName, - ref.getName(), ref.getType()); - instr.setReference(ref); - } - return ident; - } - void handleReference(Reference ref, boolean isVirtual) { Main.getClassBundle().reachableReference(ref, isVirtual); } @@ -963,644 +1536,6 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { } } - StackLocalInfo handleOpcode(Instruction instr, - StackLocalInfo info, BlockInfo block) { - constantInfos.remove(instr); - int opcode = instr.getOpcode(); - ConstValue result; - switch (opcode) { - case opc_nop: - return info.pop(0); - - case opc_ldc: - case opc_ldc2_w: - result = new ConstValue(instr.getConstant()); - return info.poppush(0, result); - - case opc_iload: case opc_lload: - case opc_fload: case opc_dload: case opc_aload: - result = info.getLocal(instr.getLocalSlot()); - if (result.value != ConstValue.VOLATILE) { - ConstantInfo constInfo - = new ConstantInfo(CONSTANT, result.value); - result.addConstantListener(constInfo); - constantInfos.put(instr, constInfo); - } - return info.poppush(0, result) - .setLocal(instr.getLocalSlot(), result); - case opc_iaload: case opc_laload: - case opc_faload: case opc_daload: case opc_aaload: - case opc_baload: case opc_caload: case opc_saload: { - result = unknownValue[(opcode == opc_laload - || opcode == opc_daload) ? 1 : 0]; - return info.poppush(2, result); - } - case opc_istore: case opc_fstore: case opc_astore: { - int slot = instr.getLocalSlot(); - block.useLocal(slot); - return info.pop(1).setLocal(slot, info.getStack(1)); - } - case opc_lstore: case opc_dstore: { - int slot = instr.getLocalSlot(); - block.useLocal(slot); - block.useLocal(slot + 1); - return info.pop(2).setLocal(slot, info.getStack(2)); - } - case opc_iastore: case opc_lastore: - case opc_fastore: case opc_dastore: case opc_aastore: - case opc_bastore: case opc_castore: case opc_sastore: { - int size = (opcode == opc_lastore - || opcode == opc_dastore) ? 2 : 1; - return info.pop(2+size); - } - case opc_pop: - return info.pop(1); - case opc_pop2: - return info.pop(2); - - case opc_dup: case opc_dup_x1: case opc_dup_x2: - case opc_dup2: case opc_dup2_x1: case opc_dup2_x2: - return info.dup((opcode - (opc_dup - 3)) / 3, - (opcode - (opc_dup - 3)) % 3); - case opc_swap: - return info.swap(); - - case opc_iadd: case opc_ladd: case opc_fadd: case opc_dadd: - case opc_isub: case opc_lsub: case opc_fsub: case opc_dsub: - case opc_imul: case opc_lmul: case opc_fmul: case opc_dmul: - case opc_idiv: case opc_ldiv: case opc_fdiv: case opc_ddiv: - case opc_irem: case opc_lrem: case opc_frem: case opc_drem: - case opc_iand: case opc_land: - case opc_ior : case opc_lor : - case opc_ixor: case opc_lxor: { - int size = 1 + (opcode - opc_iadd & 1); - ConstValue value1 = info.getStack(2*size); - ConstValue value2 = info.getStack(1*size); - boolean known = value1.value != ConstValue.VOLATILE - && value2.value != ConstValue.VOLATILE; - if (known) { - if (((opcode == opc_idiv || opcode == opc_irem) - && ((Integer)value2.value).intValue() == 0) - || ((opcode == opc_ldiv || opcode == opc_lrem) - && ((Long)value2.value).longValue() == 0)) - known = false; - } - if (known) { - Object newValue; - switch (opcode) { - case opc_iadd: - newValue = new Integer - (((Integer)value1.value).intValue() - + ((Integer)value2.value).intValue()); - break; - case opc_isub: - newValue = new Integer - (((Integer)value1.value).intValue() - - ((Integer)value2.value).intValue()); - break; - case opc_imul: - newValue = new Integer - (((Integer)value1.value).intValue() - * ((Integer)value2.value).intValue()); - break; - case opc_idiv: - newValue = new Integer - (((Integer)value1.value).intValue() - / ((Integer)value2.value).intValue()); - break; - case opc_irem: - newValue = new Integer - (((Integer)value1.value).intValue() - % ((Integer)value2.value).intValue()); - break; - case opc_iand: - newValue = new Integer - (((Integer)value1.value).intValue() - & ((Integer)value2.value).intValue()); - break; - case opc_ior: - newValue = new Integer - (((Integer)value1.value).intValue() - | ((Integer)value2.value).intValue()); - break; - case opc_ixor: - newValue = new Integer - (((Integer)value1.value).intValue() - ^ ((Integer)value2.value).intValue()); - break; - - case opc_ladd: - newValue = new Long - (((Long)value1.value).longValue() - + ((Long)value2.value).longValue()); - break; - case opc_lsub: - newValue = new Long - (((Long)value1.value).longValue() - - ((Long)value2.value).longValue()); - break; - case opc_lmul: - newValue = new Long - (((Long)value1.value).longValue() - * ((Long)value2.value).longValue()); - break; - case opc_ldiv: - newValue = new Long - (((Long)value1.value).longValue() - / ((Long)value2.value).longValue()); - break; - case opc_lrem: - newValue = new Long - (((Long)value1.value).longValue() - % ((Long)value2.value).longValue()); - break; - case opc_land: - newValue = new Long - (((Long)value1.value).longValue() - & ((Long)value2.value).longValue()); - break; - case opc_lor: - newValue = new Long - (((Long)value1.value).longValue() - | ((Long)value2.value).longValue()); - break; - case opc_lxor: - newValue = new Long - (((Long)value1.value).longValue() - ^ ((Long)value2.value).longValue()); - break; - - case opc_fadd: - newValue = new Float - (((Float)value1.value).floatValue() - + ((Float)value2.value).floatValue()); - break; - case opc_fsub: - newValue = new Float - (((Float)value1.value).floatValue() - - ((Float)value2.value).floatValue()); - break; - case opc_fmul: - newValue = new Float - (((Float)value1.value).floatValue() - * ((Float)value2.value).floatValue()); - break; - case opc_fdiv: - newValue = new Float - (((Float)value1.value).floatValue() - / ((Float)value2.value).floatValue()); - break; - case opc_frem: - newValue = new Float - (((Float)value1.value).floatValue() - % ((Float)value2.value).floatValue()); - break; - - case opc_dadd: - newValue = new Double - (((Double)value1.value).doubleValue() - + ((Double)value2.value).doubleValue()); - break; - case opc_dsub: - newValue = new Double - (((Double)value1.value).doubleValue() - - ((Double)value2.value).doubleValue()); - break; - case opc_dmul: - newValue = new Double - (((Double)value1.value).doubleValue() - * ((Double)value2.value).doubleValue()); - break; - case opc_ddiv: - newValue = new Double - (((Double)value1.value).doubleValue() - / ((Double)value2.value).doubleValue()); - break; - case opc_drem: - newValue = new Double - (((Double)value1.value).doubleValue() - % ((Double)value2.value).doubleValue()); - break; - default: - throw new InternalError("Can't happen."); - } - ConstantInfo constInfo = new ConstantInfo(CONSTANT, newValue); - constantInfos.put(instr, constInfo); - result = new ConstValue(newValue); - result.addConstantListener(constInfo); - value1.addConstantListener(result); - value2.addConstantListener(result); - } else - result = unknownValue[size-1]; - return info.poppush(2*size, result); - } - case opc_ineg: case opc_lneg: case opc_fneg: case opc_dneg: { - int size = 1 + (opcode - opc_ineg & 1); - ConstValue value = info.getStack(size); - if (value.value != ConstValue.VOLATILE) { - Object newValue; - switch (opcode) { - case opc_ineg: - newValue = new Integer - (-((Integer)value.value).intValue()); - break; - case opc_lneg: - newValue = new Long - (- ((Long)value.value).longValue()); - break; - case opc_fneg: - newValue = new Float - (- ((Float)value.value).floatValue()); - break; - case opc_dneg: - newValue = new Double - (- ((Double)value.value).doubleValue()); - break; - default: - throw new InternalError("Can't happen."); - } - ConstantInfo constInfo = new ConstantInfo(CONSTANT, newValue); - constantInfos.put(instr, constInfo); - result = new ConstValue(newValue); - result.addConstantListener(constInfo); - value.addConstantListener(result); - } else - result = unknownValue[size-1]; - return info.poppush(size, result); - } - case opc_ishl: case opc_lshl: - case opc_ishr: case opc_lshr: - case opc_iushr: case opc_lushr: { - int size = 1 + (opcode - opc_iadd & 1); - ConstValue value1 = info.getStack(size+1); - ConstValue value2 = info.getStack(1); - if (value1.value != ConstValue.VOLATILE - && value2.value != ConstValue.VOLATILE) { - Object newValue; - switch (opcode) { - case opc_ishl: - newValue = new Integer - (((Integer)value1.value).intValue() - << ((Integer)value2.value).intValue()); - break; - case opc_ishr: - newValue = new Integer - (((Integer)value1.value).intValue() - >> ((Integer)value2.value).intValue()); - break; - case opc_iushr: - newValue = new Integer - (((Integer)value1.value).intValue() - >>> ((Integer)value2.value).intValue()); - break; - - case opc_lshl: - newValue = new Long - (((Long)value1.value).longValue() - << ((Integer)value2.value).intValue()); - break; - case opc_lshr: - newValue = new Long - (((Long)value1.value).longValue() - >> ((Integer)value2.value).intValue()); - break; - case opc_lushr: - newValue = new Long - (((Long)value1.value).longValue() - >>> ((Integer)value2.value).intValue()); - break; - default: - throw new InternalError("Can't happen."); - } - ConstantInfo constInfo = new ConstantInfo(CONSTANT, newValue); - constantInfos.put(instr, constInfo); - result = new ConstValue(newValue); - result.addConstantListener(constInfo); - value1.addConstantListener(result); - value2.addConstantListener(result); - } else - result = unknownValue[size-1]; - return info.poppush(size+1, result); - } - case opc_iinc: { - ConstValue local = info.getLocal(instr.getLocalSlot()); - if (local.value != ConstValue.VOLATILE) { - result = new ConstValue - (new Integer(((Integer)local.value).intValue() - + instr.getIncrement())); - local.addConstantListener(result); - } else - result = unknownValue[0]; - return info.setLocal(instr.getLocalSlot(), result); - } - case opc_i2l: case opc_i2f: case opc_i2d: - case opc_l2i: case opc_l2f: case opc_l2d: - case opc_f2i: case opc_f2l: case opc_f2d: - case opc_d2i: case opc_d2l: case opc_d2f: { - int insize = 1 + ((opcode - opc_i2l) / 3 & 1); - ConstValue stack = info.getStack(insize); - if (stack.value != ConstValue.VOLATILE) { - Object newVal; - switch(opcode) { - case opc_l2i: case opc_f2i: case opc_d2i: - newVal = new Integer(((Number)stack.value).intValue()); - break; - case opc_i2l: case opc_f2l: case opc_d2l: - newVal = new Long(((Number)stack.value).longValue()); - break; - case opc_i2f: case opc_l2f: case opc_d2f: - newVal = new Float(((Number)stack.value).floatValue()); - break; - case opc_i2d: case opc_l2d: case opc_f2d: - newVal = new Double(((Number)stack.value).doubleValue()); - break; - default: - throw new InternalError("Can't happen."); - } - ConstantInfo constInfo = new ConstantInfo(CONSTANT, newVal); - constantInfos.put(instr, constInfo); - result = new ConstValue(newVal); - result.addConstantListener(constInfo); - stack.addConstantListener(result); - } else { - switch (opcode) { - case opc_i2l: case opc_f2l: case opc_d2l: - case opc_i2d: case opc_l2d: case opc_f2d: - result = unknownValue[1]; - break; - default: - result = unknownValue[0]; - } - } - return info.poppush(insize, result); - } - case opc_i2b: case opc_i2c: case opc_i2s: { - ConstValue stack = info.getStack(1); - if (stack.value != ConstValue.VOLATILE) { - int val = ((Integer)stack.value).intValue(); - switch(opcode) { - case opc_i2b: - val = (byte) val; - break; - case opc_i2c: - val = (char) val; - break; - case opc_i2s: - val = (short) val; - break; - } - Integer newVal = new Integer(val); - ConstantInfo constInfo = new ConstantInfo(CONSTANT, newVal); - constantInfos.put(instr, constInfo); - result = new ConstValue(newVal); - stack.addConstantListener(constInfo); - stack.addConstantListener(result); - } else - result = unknownValue[0]; - return info.poppush(1, result); - } - case opc_lcmp: { - ConstValue val1 = info.getStack(4); - ConstValue val2 = info.getStack(2); - if (val1.value != ConstValue.VOLATILE - && val2.value != ConstValue.VOLATILE) { - long value1 = ((Long) val1.value).longValue(); - long value2 = ((Long) val1.value).longValue(); - Integer newVal = new Integer(value1 == value2 ? 0 - : value1 < value2 ? -1 : 1); - ConstantInfo constInfo = new ConstantInfo(CONSTANT, newVal); - constantInfos.put(instr, constInfo); - result = new ConstValue(newVal); - result.addConstantListener(constInfo); - val1.addConstantListener(result); - val2.addConstantListener(result); - } else - result = unknownValue[0]; - return info.poppush(4, result); - } - case opc_fcmpl: case opc_fcmpg: { - ConstValue val1 = info.getStack(2); - ConstValue val2 = info.getStack(1); - if (val1.value != ConstValue.VOLATILE - && val2.value != ConstValue.VOLATILE) { - float value1 = ((Float) val1.value).floatValue(); - float value2 = ((Float) val1.value).floatValue(); - Integer newVal = new Integer - (value1 == value2 ? 0 - : ( opcode == opc_fcmpg - ? (value1 < value2 ? -1 : 1) - : (value1 > value2 ? 1 : -1))); - ConstantInfo constInfo = new ConstantInfo(CONSTANT, newVal); - constantInfos.put(instr, constInfo); - result = new ConstValue(newVal); - result.addConstantListener(constInfo); - val1.addConstantListener(result); - val2.addConstantListener(result); - } else - result = unknownValue[0]; - return info.poppush(2, result); - } - case opc_dcmpl: case opc_dcmpg: { - ConstValue val1 = info.getStack(4); - ConstValue val2 = info.getStack(2); - if (val1.value != ConstValue.VOLATILE - && val2.value != ConstValue.VOLATILE) { - double value1 = ((Double) val1.value).doubleValue(); - double value2 = ((Double) val1.value).doubleValue(); - Integer newVal = new Integer - (value1 == value2 ? 0 - : ( opcode == opc_dcmpg - ? (value1 < value2 ? -1 : 1) - : (value1 > value2 ? 1 : -1))); - ConstantInfo constInfo = new ConstantInfo(CONSTANT, newVal); - constantInfos.put(instr, constInfo); - result = new ConstValue(newVal); - result.addConstantListener(constInfo); - val1.addConstantListener(result); - val2.addConstantListener(result); - } else - result = unknownValue[0]; - return info.poppush(4, result); - } - case opc_ifeq: case opc_ifne: - case opc_iflt: case opc_ifge: - case opc_ifgt: case opc_ifle: - case opc_if_icmpeq: case opc_if_icmpne: - case opc_if_icmplt: case opc_if_icmpge: - case opc_if_icmpgt: case opc_if_icmple: - case opc_if_acmpeq: case opc_if_acmpne: - case opc_ifnull: case opc_ifnonnull: - case opc_lookupswitch: - case opc_ireturn: case opc_lreturn: - case opc_freturn: case opc_dreturn: case opc_areturn: - case opc_athrow: - case opc_jsr: - case opc_ret: - case opc_goto: - case opc_return: - return info; - - case opc_putstatic: - case opc_putfield: { - final FieldIdentifier fi - = (FieldIdentifier) canonizeReference(instr); - Reference ref = instr.getReference(); - int size = TypeSignature.getTypeSize(ref.getType()); - if (fi != null && !fi.isNotConstant()) { - ConstValue stacktop = info.getStack(size); - Object fieldVal = fi.getConstant(); - if (fieldVal == null) - fieldVal = TypeSignature.getDefaultValue(ref.getType()); - if (stacktop.value == null ? fieldVal == null - : stacktop.value.equals(fieldVal)) { - stacktop.addConstantListener(new ConstantListener() { - public void constantChanged() { - fieldNotConstant(fi); - } - }); - } else { - fieldNotConstant(fi); - } - } - size += (opcode == opc_putstatic) ? 0 : 1; - return info.pop(size); - } - case opc_getstatic: - case opc_getfield: { - int size = (opcode == opc_getstatic) ? 0 : 1; - FieldIdentifier fi = (FieldIdentifier) canonizeReference(instr); - Reference ref = instr.getReference(); - int typesize = TypeSignature.getTypeSize(ref.getType()); - if (fi != null) { - if (fi.isNotConstant()) { - fi.setReachable(); - result = unknownValue[typesize - 1]; - } else { - Object obj = fi.getConstant(); - if (obj == null) - obj = TypeSignature.getDefaultValue(ref.getType()); - ConstantInfo constInfo = new ConstantInfo(CONSTANT, obj); - constantInfos.put(instr, constInfo); - result = new ConstValue(obj); - fi.addFieldListener(methodIdent); - ConstValue prev = (ConstValue) fieldDependencies.get(fi); - if (prev != null) - prev.addConstantListener(result); - else - fieldDependencies.put(fi, result); - } - } else - result = unknownValue[typesize - 1]; - return info.poppush(size, result); - } - case opc_invokespecial: - case opc_invokestatic: - case opc_invokeinterface: - case opc_invokevirtual: { - canonizeReference(instr); - Reference ref = instr.getReference(); - boolean constant = true; - int size = 0; - Object cls = null; - String[] paramTypes - = TypeSignature.getParameterTypes(ref.getType()); - Object[] args = new Object[paramTypes.length]; - ConstValue clsValue = null; - ConstValue[] argValues = new ConstValue[paramTypes.length]; - - for (int i = paramTypes.length - 1; i >= 0; i--) { - size += TypeSignature.getTypeSize(paramTypes[i]); - Object value = (argValues[i] = info.getStack(size)).value; - if (value != ConstValue.VOLATILE) - args[i] = value; - else - constant = false; - } - - if (opcode != opc_invokestatic) { - size++; - clsValue = info.getStack(size); - cls = clsValue.value; - if (cls == ConstValue.VOLATILE || cls == null) - constant = false; - } - String retType = TypeSignature.getReturnType(ref.getType()); - if (retType.equals("V")) { - handleReference(ref, opcode == opc_invokevirtual - || opcode == opc_invokeinterface); - return info.pop(size); - } - if (constant && !runtime.isWhite(retType)) { - /* This is not a valid constant type */ - constant = false; - } - Object methodResult = null; - if (constant) { - try { - methodResult = runtime.invokeMethod - (ref, opcode != opc_invokespecial, cls, args); - } catch (InterpreterException ex) { - constant = false; - if (net.sf.jode.GlobalOptions.verboseLevel > 3) - GlobalOptions.err.println("Can't interpret "+ref+": " - + ex.getMessage()); - /* result is not constant */ - } catch (InvocationTargetException ex) { - constant = false; - if (net.sf.jode.GlobalOptions.verboseLevel > 3) - GlobalOptions.err.println("Method "+ref - +" throwed exception: " - + ex.getTargetException()); - /* method always throws exception ? */ - } - } - ConstValue returnVal; - if (!constant) { - handleReference(ref, opcode == opc_invokevirtual - || opcode == opc_invokeinterface); - int retsize = TypeSignature.getTypeSize(retType); - returnVal = unknownValue[retsize - 1]; - } else { - ConstantInfo constInfo = - new ConstantInfo(CONSTANT, methodResult); - constantInfos.put(instr, constInfo); - returnVal = new ConstValue(methodResult); - returnVal.addConstantListener(constInfo); - if (clsValue != null) - clsValue.addConstantListener(returnVal); - for (int i=0; i< argValues.length; i++) - argValues[i].addConstantListener(returnVal); - } - return info.poppush(size, returnVal); - } - - case opc_new: { - handleClass(instr.getClazzType()); - return info.poppush(0, unknownValue[0]); - } - case opc_arraylength: { - return info.poppush(1, unknownValue[0]); - } - case opc_checkcast: { - handleClass(instr.getClazzType()); - return info.pop(0); - } - case opc_instanceof: { - handleClass(instr.getClazzType()); - return info.poppush(1, unknownValue[0]); - } - case opc_monitorenter: - case opc_monitorexit: - return info.pop(1); - case opc_multianewarray: - handleClass(instr.getClazzType()); - return info.poppush(instr.getDimensions(), unknownValue[0]); - default: - throw new IllegalArgumentException("Invalid opcode "+opcode); - } - } - - public ConstantAnalyzer() { } @@ -1610,8 +1545,7 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { infos[i].dumpInfo(output); } - public void analyzeCode(MethodIdentifier methodIdent, - BasicBlocks bb) { + public void analyzeCode(MethodIdentifier methodIdent, BasicBlocks bb) { Block[] blocks = bb.getBlocks(); this.methodIdent = methodIdent; this.bb = bb; @@ -1627,15 +1561,19 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { infos[startBlock.getBlockNr()].mergeBefore (new StackLocalInfo(bb.getMaxStack(), bb.getMaxLocals(), minfo.isStatic(), minfo.getType()), - new JsrInfo[0]); + null, null); BlockInfo info; while ((info = modifiedQueue.dequeue()) != null) { - System.err.println(info); +// dumpBlockInfo(GlobalOptions.err); +// GlobalOptions.err.println("Analyzing: "+info); info.analyze(); } } +// GlobalOptions.err.println("After Analyze"); +// dumpBlockInfo(GlobalOptions.err); + BitSet reachableBlocks = new BitSet(); for (int i=0; i< infos.length; i++) { if (infos[i].isReachable()) @@ -1730,22 +1668,16 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { Reference ref = instr.getReference(); String[] pt = TypeSignature.getParameterTypes(ref.getType()); int arg = 0; + + int i = pt.length; + while (i > 0) + newCode.add(Instruction.forOpcode(TypeSignature + .getTypeSize(pt[--i]) + + opc_pop - 1)); + if (instr.getOpcode() != opc_invokestatic) newCode.add(Instruction.forOpcode(opc_pop)); - else if (pt.length > 0) { - newCode.add(Instruction.forOpcode(TypeSignature.getTypeSize(pt[0]) - + opc_pop - 1)); - arg++; - } else { - if (replacement != null) - newCode.add(replacement); - return; - } - - for (int i=arg; i < pt.length; i++) - newCode.add(Instruction.forOpcode - (TypeSignature.getTypeSize(pt[i]) - + opc_pop - 1)); + break; } default: throw new InternalError("Unexpected opcode"); @@ -1790,7 +1722,7 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { for (int idx = 0; idx < oldCode.length; idx++) { Instruction instr = oldCode[idx]; ConstantInfo info = (ConstantInfo) constantInfos.remove(instr); - if ((info.flags & CONSTANT) != 0) { + if (info != null && (info.flags & CONSTANT) != 0) { Instruction ldcInstr = Instruction.forOpcode (info.constant instanceof Long || info.constant instanceof Double @@ -1800,7 +1732,7 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { (bb + ": Replacing " + instr + " with constant " + info.constant); replaceWith(newCode, instr, ldcInstr); - } else if ((info.flags & CONSTANTFLOW) != 0) { + } else if (info != null && (info.flags & CONSTANTFLOW) != 0) { int succnr = ((Integer)info.constant).intValue(); replaceWith(newCode, instr, null); if (GlobalOptions.verboseLevel > 2) @@ -1822,23 +1754,28 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { && (Main.stripping & Main.STRIP_UNREACH) != 0 && !fi.isReachable()) { replaceWith(newCode, instr, null); + break; } - break; + /* fall through */ } default: newCode.add(instr); } } } - blocks[i].setCode((Instruction[]) newCode.toArray(oldCode), succs); + blocks[i].setCode((Instruction[]) newCode.toArray(new Instruction[newCode.size()]), succs); newBlockCtr++; } if (newBlockCtr < blocks.length) { Block[] newBlocks = new Block[newBlockCtr]; - Handler[] newHandlers = new Handler[newHandlerCtr]; System.arraycopy(blocks, 0, newBlocks, 0, newBlockCtr); + blocks = newBlocks; + } + if (newHandlerCtr < handlers.length) { + Handler[] newHandlers = new Handler[newHandlerCtr]; System.arraycopy(handlers, 0, newHandlers, 0, newHandlerCtr); - bb.setBlocks(newBlocks, newStartBlock, newHandlers); + handlers = newHandlers; } + bb.setBlocks(blocks, newStartBlock, handlers); } } diff --git a/jode/src/net/sf/jode/obfuscator/modules/SimpleAnalyzer.java b/jode/src/net/sf/jode/obfuscator/modules/SimpleAnalyzer.java index c3b6fbb..d5a7279 100644 --- a/jode/src/net/sf/jode/obfuscator/modules/SimpleAnalyzer.java +++ b/jode/src/net/sf/jode/obfuscator/modules/SimpleAnalyzer.java @@ -19,6 +19,7 @@ package net.sf.jode.obfuscator.modules; import net.sf.jode.bytecode.Opcodes; +import net.sf.jode.bytecode.ClassPath; import net.sf.jode.bytecode.ClassInfo; import net.sf.jode.bytecode.BasicBlocks; import net.sf.jode.bytecode.Block; @@ -30,6 +31,7 @@ import net.sf.jode.obfuscator.Identifier; import net.sf.jode.obfuscator.*; import net.sf.jode.GlobalOptions; +import java.io.IOException; ///#def COLLECTIONS java.util import java.util.ArrayList; import java.util.Iterator; @@ -38,9 +40,25 @@ import java.util.ListIterator; public class SimpleAnalyzer implements CodeAnalyzer, Opcodes { - public Identifier canonizeReference(Instruction instr) { + private ClassInfo canonizeIfaceRef(ClassInfo clazz, Reference ref) { + while (clazz != null) { + if (clazz.findMethod(ref.getName(), ref.getType()) != null) + return clazz; + ClassInfo[] ifaces = clazz.getInterfaces(); + for (int i = 0; i < ifaces.length; i++) { + ClassInfo realClass = canonizeIfaceRef(ifaces[i], ref); + if (realClass != null) + return realClass; + } + clazz = clazz.getSuperclass(); + } + return null; + } + + protected Identifier canonizeReference(Instruction instr) { Reference ref = instr.getReference(); Identifier ident = Main.getClassBundle().getIdentifier(ref); + ClassPath classPath = Main.getClassBundle().getClassPath(); String clName = ref.getClazz(); String realClazzName; if (ident != null) { @@ -56,13 +74,21 @@ public class SimpleAnalyzer implements CodeAnalyzer, Opcodes { /* Arrays don't define new methods (well clone(), * but that can be ignored). */ - clazz = ClassInfo.forName("java.lang.Object"); + clazz = classPath.getClassInfo("java.lang.Object"); } else { - clazz = ClassInfo.forName + clazz = classPath.getClassInfo (clName.substring(1, clName.length()-1) .replace('/','.')); } - if (instr.getOpcode() >= opc_invokevirtual) { + try { + clazz.load(clazz.DECLARATIONS); + } catch (IOException ex) { + throw new RuntimeException("Can't get declarations of " + + clazz); + } + if (instr.getOpcode() == opc_invokeinterface) { + clazz = canonizeIfaceRef(clazz, ref); + } else if (instr.getOpcode() >= opc_invokevirtual) { while (clazz != null && clazz.findMethod(ref.getName(), ref.getType()) == null) diff --git a/jode/src/net/sf/jode/util/UnifyHash.java b/jode/src/net/sf/jode/util/UnifyHash.java index 2798456..fd277cc 100644 --- a/jode/src/net/sf/jode/util/UnifyHash.java +++ b/jode/src/net/sf/jode/util/UnifyHash.java @@ -191,6 +191,9 @@ public class UnifyHash extends AbstractCollection { ///#endif return new Iterator() { private int known = modCount; + private boolean removeOk = false; + private Bucket removeBucket = null; + private Bucket prevBucket = null; private Bucket nextBucket = buckets[Math.abs(hash % buckets.length)]; private Object nextVal; @@ -206,7 +209,7 @@ public class UnifyHash extends AbstractCollection { if (nextVal != null) return; } - + prevBucket = nextBucket; nextBucket = nextBucket.next; } } @@ -221,13 +224,26 @@ public class UnifyHash extends AbstractCollection { if (nextBucket == null) throw new NoSuchElementException(); Object result = nextVal; + removeBucket = prevBucket; + removeOk = true; + prevBucket = nextBucket; nextBucket = nextBucket.next; internalNext(); return result; } public void remove() { - throw new UnsupportedOperationException(); + if (known != modCount) + throw new ConcurrentModificationException(); + if (!removeOk) + throw new IllegalStateException(); + if (removeBucket == null) + buckets[Math.abs(hash % buckets.length)] + = buckets[Math.abs(hash % buckets.length)].next; + else + removeBucket.next = removeBucket.next.next; + known = ++modCount; + size--; } }; } @@ -248,6 +264,17 @@ public class UnifyHash extends AbstractCollection { buckets[slot] = b; } + public boolean remove(int hash, Object o) { + Iterator i = iterateHashCode(hash); + while (i.hasNext()) { + if (i.next() == o) { + i.remove(); + return true; + } + } + return false; + } + public Object unify(Object o, int hash, Comparator comparator) { ///#ifdef JDK12 cleanUp();