From e78e8b0472723cfe5c22022595ac4b81e73f66d3 Mon Sep 17 00:00:00 2001 From: hoenicke Date: Sun, 15 Jul 2001 20:22:40 +0000 Subject: [PATCH] Applied more patches from Jode-1.1 branch git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@1329 379699f6-c40d-0410-875b-85095c16579e --- jode/ChangeLog | 117 ++++++++++++- jode/INSTALL | 13 +- jode/Makefile.am | 4 +- jode/NEWS | 9 +- jode/README | 97 ++++------- jode/TODO | 5 + jode/configure.in | 12 +- jode/doc/.cvsignore | 1 + jode/jode/decompiler/ClassAnalyzer.java | 163 +++++++++++------- jode/jode/decompiler/Decompiler.java | 6 +- jode/jode/decompiler/FieldAnalyzer.java | 4 +- jode/jode/decompiler/ImportHandler.java | 18 +- jode/jode/decompiler/LocalInfo.java | 3 +- jode/jode/decompiler/Main.java | 22 ++- jode/jode/decompiler/MethodAnalyzer.java | 41 ++++- jode/jode/decompiler/OuterValues.java | 20 ++- jode/jode/decompiler/TabbedPrintWriter.java | 15 +- jode/jode/expr/ArrayStoreOperator.java | 25 +++ jode/jode/expr/ConstOperator.java | 2 +- jode/jode/expr/ConstantArrayOperator.java | 9 +- jode/jode/expr/Expression.java | 2 +- jode/jode/expr/FieldOperator.java | 77 +++++++-- jode/jode/expr/InvokeOperator.java | 151 +++++++++++----- jode/jode/flow/CaseBlock.java | 6 +- jode/jode/flow/InstructionBlock.java | 2 +- jode/jode/flow/JsrBlock.java | 9 + jode/jode/flow/LoopBlock.java | 3 +- jode/jode/flow/Makefile.am | 1 + jode/jode/flow/SpecialBlock.java | 6 +- jode/jode/flow/TransformConstructors.java | 38 +++- .../jode/flow/TransformExceptionHandlers.java | 57 +++--- jode/jode/jvm/CodeVerifier.java | 10 -- jode/jode/jvm/SyntheticAnalyzer.java | 85 ++++++--- .../obfuscator/modules/ConstantAnalyzer.java | 4 +- jode/jode/swingui/Main.java | 20 ++- jode/jode/swingui/Resources.properties | 9 +- jode/jode/swingui/Resources_de.properties | 9 +- jode/jode/type/ArrayType.java | 27 ++- jode/jode/type/ClassType.java | 65 ++++--- jode/jode/type/RangeType.java | 13 +- 40 files changed, 815 insertions(+), 365 deletions(-) diff --git a/jode/ChangeLog b/jode/ChangeLog index 926db89..6789a6b 100644 --- a/jode/ChangeLog +++ b/jode/ChangeLog @@ -1,3 +1,118 @@ +2001-07-15 Jochen Hoenicke + * jode/decompiler/Decompiler.java (decompile): removed + setClassPath call. ClassInfo.forName() is no longer used. + * jode/decompiler/Main.java (decompile): likewise. + +2001-07-15 Jochen Hoenicke + Applied patches from 2001-05-26 of Jode 1.1 tree: + * configure.in: Set version to 1.1. + + * jode/swingui/Main.java (main): Also use bootclasspath if no + classpath given. + + * jode/decompiler/MethodAnalyzer.java (skipWriting): Don't skip + empty constructor that have a throws clause. + + * configure.in: Determine whether jdk1.1 resp. jdk1.2. Call jcpp + in config.status. + + * jode/expr/Expression.java (makeInitializer): Now takes the + type of the initialization. Changed all callers. + * jode/expr/ConstantArrayOperator.java (makeInitializer): Check + that type is our array type, otherwise we can't omit new Array[]. + + * jode/decompiler/LocalInfo.java (markFinal): Don't check that + only one write is present. If two writes are in an then and an + else branch of an if, the local can still be final. + + * jode/type/ArrayType.java (getSubType): Handle array of integer + types correctly: byte[] is something completely different than + int[]. + (getSuperType): Likewise. + + * jode/expr/FieldOperator.java (getFieldInfo): New function. + (needsCast): A cast is also needed if the field is private or + package scope and the current type can't access the field. + + * jode/expr/InvokeOperator.java (getMethodInfo): New function. + (needsCast): A cast is also needed if the method is private or + package scope and the current type can't access the method. + + * jode/expr/ArrayStoreOperator.java (dumpExpression): Check if a + cast of the array expression is needed. + + * jode/expr/TransformConstructors.java + (transformFieldInitializers): Don't allow moving method invocations + that throw a checked exception. + + * jode/bytecode/MethodInfo.java (readAttribute): Read Exceptions + attribute even when not all attributes should be read. They are + needed by TransformConstructors, see above. + + * jode/decompiler/TabbedPrintWriter.java (saveOps): Don't allow + line breaks in not completed expressions since implicit parentheses + would destroy the syntax. No need to put line break option on stack. + (restoreOps): Adapted Stack format. + + * jode/decompiler/ClassAnalyzer.java (dumpDeclaration): Moved + Code from dumpSource here. Don't put a line break after closing + brace. + (dumpSource): call dumpDeclaration and add a line break. + (dumpBlock): Moved dropInfo(ATTRIBS) here. + + * jode/decompiler/ClassAnalyzer.java (STRICTFP): New Constant. + (isStrictFP): New function. + (initialize): Set strictfp modifier if a constructor has it set. + (dumpSource): Handle strictfp modifier. + + * jode/decompiler/MethodAnalyzer.java (STRICTFP): New Constant. + (isStrictFP): New function. + (dumpSource): Handle strictfp modifier. + + * jode/jvm/SyntheticAnalyzer.java (checkAccess): Check for a + special putfield access, where the set value is returned. Allow + the modifier of field/method to be protected and the class to be + a superclass. + (checkStaticAccess): Likewise. + (ACCESSDUPPUTFIELD): New Constant. + (ACCESSDUPPUTSTATIC): New Constant. + + * jode/expr/InvokeOperator.java (simplifyAccess): Handle new + synthetics. + + * jode/flow/SpecialBlock.java (removePop): Remove pop also for + non void store instructions. + + * jode/decompiler/MethodAnalyzer.java (skipWriting): Also skip + the new synthetics. + + * jode/decompiler/Main.java (main): Call System.exit() after + everything was compiled. + + * jode/flow/TransformExceptionHandlers.java (removeJSR): + Renamed back from removeBadJSR (see patch from 2001-02-04). The + checkAndRemove* functions mustn't change the successors while they + iterate over them. Instead of removing good jsr they mark them as + good and removeJSR will finally remove them. + (checkAndRemoveJSR): See above. + (checkAndRemoveMonitorExit): See above. + + * jode/flow/JsrBlock.java (good): New variable, see above. + (setGood): New method. + (isGood): New method. + +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 + understood why it is necessary. But see JVM Spec 4.9.4. + +2001-07-15 Jochen Hoenicke + Applied patch from 2001-05-02 of Jode 1.1 tree: + * jode/obfuscator/modules/ConstantAnalyzer.java (handleOpcode): + Added divide by zero checks for opc_irem and opc_lrem. + 2001-07-15 Jochen Hoenicke Applied patches from 2001-02-27 of Jode 1.1 tree: @@ -6,7 +121,7 @@ correct anyway. Reported by Erik Modén. - * jode/swingui/Main.java.in (AreaWriter): Convert all kinds of + * jode/swingui/Main.java (AreaWriter): Convert all kinds of line breaks (CR+LF, CR, LF) to a LF character, which a JTextArea understands. diff --git a/jode/INSTALL b/jode/INSTALL index d9fbb81..0fdb310 100644 --- a/jode/INSTALL +++ b/jode/INSTALL @@ -1,8 +1,7 @@ -Before installing, make sure you have a java compiler (e.g javac or -jikes) and the java 1.1 runtime class library installed. If you want -to run this program you need at least a 1.1 compatible java virtual -machine. There are some bugs in javac included in the SUN JDK 1.1, it -won't work. +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: @@ -44,7 +43,3 @@ give a path to the directory where it resides, otherwise it is searched in the path. 3). Type "make" to build the package. - -4). Type "make install" to install the package. This doesn't work yet. - - Jochen diff --git a/jode/Makefile.am b/jode/Makefile.am index b0ee681..25734a8 100644 --- a/jode/Makefile.am +++ b/jode/Makefile.am @@ -1,5 +1,3 @@ ## Input file for automake to generate the Makefile.in used by configure -SUBDIRS = jode bin doc test - -EXTRA_DIST = TODO +SUBDIRS = bin doc jode test diff --git a/jode/NEWS b/jode/NEWS index 2d2bc84..b9f98b0 100644 --- a/jode/NEWS +++ b/jode/NEWS @@ -1,6 +1,11 @@ -New in 1.0.94 +New in 1.2 +* New bytecode interface +* Faster and better flow analyzation + +New in 1.1 * break long lines -* small bug fixes +* handle most of javac v8 constructs (jdk 1.3) +* bug fixes New in 1.0.93 * anonymous and inner class decompilation reworked. diff --git a/jode/README b/jode/README index 16c3d0a..8ebabf2 100644 --- a/jode/README +++ b/jode/README @@ -1,12 +1,17 @@ JODE (Java Optimize and Decompile Environment) -This is a decompiler and optimizer for java I have written in my spare -time. The decompiler takes class files as input and produces -something similar to the original java file. Of course this can't be -perfect: There is no way to produce the comments or the names of local -variables (except when the java files were compiled with `-g') and -there are often more ways to write the same thing. But jode does its -job quite well. +JODE is a java package containing a decompiler and an optimizer for +java. This package is freely available under the GNU General Public +License. + +The decompiler reads in class files and produces something similar to +the original java file. Of course this can't be perfect: There is no +way to produce the comments or the names of local variables (except +when the java files were compiled with `-g') and there are often more +ways to write the same thing. However, jode does its job quite well. + +The optimizer transforms class files in various ways with +can be controlled by a script file. Please note that most software licenses forbid to decompile class files. Use this decompiler only, if you have legal rights to @@ -25,6 +30,15 @@ The features of the decompilers are: Known bugs of the decompiler: + - Some jdk1.3 synthetic access functions aren't understood. The + produced source contains access$xxx functions, but it still compiles. + + - There may be other bugs, that cause Exceptions or invalid code. + If you have such a problems don't hesitate to issue a bug report. + Please include the class file if possible. + +Limitations: + - If not all dependent classes can be found, the verifier (which is run before decompilation starts) may exit with a type error. You can decompile it with --verify=off, but take the warning serious, @@ -40,18 +54,6 @@ Known bugs of the decompiler: happen when you compile with `-O' flag and javac has inlined some methods. - - Sometimes this program may exit with an Exception or produce - incorrect code. If it produced incorrect code the code probably - can't be compiled, so that the error can be easily spotted. If you - have one of these problems (they are very rare now), I would be - very interested in a bug report (including the .class file, if - possible). - - - Sometimes it generates some GOTO expression and labels. This - shouldn't happen any more with code produced by javac or jikes. - But some flow obfuscator likes Zelix Klassmaster may provoke this. - In that case you can run the Obfuscator first (to optimize away the - flow obfuscation ;-). The features of the obfuscator are: * Modular design, you can plug the obfuscation transformation you need @@ -66,62 +68,23 @@ The features of the obfuscator are: PRELIMINARIES: -You need java jdk1.2 or above, and the gnu getopt package. - -You should also have the jode-x.x.xx.jar file. Read INSTALL how to -compile it yourself. - -Jode also works with jdk1.1, but you need the sun collection classes -and swing in that case. - -Here are some URLs for the tools and packages you may need: - - 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 +See INSTALL for installation instructions. USAGE: -First set the classpath. It should contain the jar file -jode-x.x.xx.jar, the gnu getopt package and the sun collection package -for 1.1. For the swingui program you also need swing in you classpath. +First set the classpath. It should contain the jar file jode-1.1.jar, +the gnu getopt package and the sun collection package for 1.1. For +the swingui program you also need swing in you classpath. You can then decompile a class file with: - java jode.decompiler.Main --classpath jarfile1.jar,jarfile2.jar org.package.Class + java jode.decompiler.Main --classpath program.jar,libfoo.jar org.package.Class +or a complete package with + java jode.decompiler.Main --classpath libfoo.jar program.jar For a graphical user interface based on swing try: java jode.swingui.Main --classpath jarfile1.jar -The obfuscator/deobfuscator can be run with: +The obfuscator/deobfuscator can be run with a script: java jode.obfuscator.Main obfuscation.jos -See XXXX for more information about the script syntax. - -HISTORY: - -Someday I found guavad, a disassembler for java byte code (it does -similar things like `javap -c'). I used it on a class file, and found -that it was possible to reconstruct the original java code. First I -did it by hand on some small routines, but I soon realized that it was -a rather stupid task, and that I could write a perl script, that does -the same. At the end of the next day I had the first working -decompiler. - -Now while it was working, it was not easy to use. You had to -decompile the code first with a disassembler, cut the method, you -wanted to decompile and then run the perl script on it. So I decided -to get some information of the class files and do this all -automatically. I decided to write it in java now, because it suited -best. +See the web documents for more information about the script syntax. diff --git a/jode/TODO b/jode/TODO index 9ec514b..c24430e 100644 --- a/jode/TODO +++ b/jode/TODO @@ -12,6 +12,8 @@ Obfuscator: - Detect Class.forName() calls with constant parameters and rename these constants. Detect class$ methods with constant parameters. Warn about all other occurences of Class.forName() + - work around Class.forName, by creating a new version using a hash + table that maps md5 sums of old names to obfuscated names. This should be put into the constant analyzer. The simple analyzer should only do the warnings. @@ -55,3 +57,6 @@ User Interface: Internal: - clean up package hierarchy, esp. expr, flow and decompiler. + - move to net.sf.jode package. + - make the class names more precise, e.g. StructuredBlock is Statement, + FlowBlock is BasicBlock. diff --git a/jode/configure.in b/jode/configure.in index 4b131b5..1897d48 100644 --- a/jode/configure.in +++ b/jode/configure.in @@ -1,18 +1,10 @@ dnl Process this file with autoconf to produce a configure script. AC_INIT() -AM_INIT_AUTOMAKE(jode, 1.0.94) +AM_INIT_AUTOMAKE(jode, 1.1) dnl Checks for programs. -dnl AC_PROG_CXX -dnl AC_PROG_AWK -dnl AC_PROG_CC -dnl AC_PROG_CPP -dnl AC_PROG_INSTALL -dnl AC_PROG_LN_S AC_PROG_MAKE_SET -dnl AC_PROG_RANLIB -dnl AC_PATH_PROG(ZIP, zip) AC_PATH_PROG(PERL, perl) AC_PATH_PROG(CYGPATH, cygpath) @@ -190,6 +182,6 @@ test/Makefile, changequote(, )dnl if [ \$i != \${i%.java} ]; then changequote([, ])dnl - $PERL $srcdir/jcpp $JCPPFLAGS \$i + $PERL $srcdir/scripts/jcpp.pl $JCPPFLAGS \$i fi done]) diff --git a/jode/doc/.cvsignore b/jode/doc/.cvsignore index 282522d..2f083ef 100644 --- a/jode/doc/.cvsignore +++ b/jode/doc/.cvsignore @@ -1,2 +1,3 @@ Makefile Makefile.in +*.html diff --git a/jode/jode/decompiler/ClassAnalyzer.java b/jode/jode/decompiler/ClassAnalyzer.java index d3dabe4..7a638a8 100644 --- a/jode/jode/decompiler/ClassAnalyzer.java +++ b/jode/jode/decompiler/ClassAnalyzer.java @@ -59,6 +59,12 @@ public class ClassAnalyzer * The minimal visible complexity. */ private static double STEP_COMPLEXITY = 0.03; + /** + * The value of the strictfp modifier. + * JDK1.1 doesn't define it. + */ + private static int STRICTFP = 0x800; + double methodComplexity = 0.0; double innerComplexity = 0.0; @@ -149,6 +155,10 @@ public class ClassAnalyzer return Modifier.isStatic(modifiers); } + public final boolean isStrictFP() { + return (modifiers & STRICTFP) != 0; + } + public FieldAnalyzer getField(int index) { return fields[index]; } @@ -261,6 +271,17 @@ public class ClassAnalyzer staticConstructor = methods[j]; else constrVector.addElement(methods[j]); + + /* Java bytecode can't have strictfp modifier for + * classes, while java can't have strictfp modifier + * for constructors. We handle the difference here. + * + * If only a few constructors are strictfp and the + * methods aren't this would add too much strictfp, + * but that isn't really dangerous. + */ + if (methods[j].isStrictFP()) + modifiers |= STRICTFP; } methodComplexity += methods[j].getComplexity(); } @@ -435,9 +456,78 @@ public class ClassAnalyzer public void dumpDeclaration(TabbedPrintWriter writer) throws IOException { - dumpSource(writer); + dumpDeclaration(writer, null, 0.0, 0.0); } + public void dumpDeclaration(TabbedPrintWriter writer, + ProgressListener pl, double done, double scale) + throws IOException + { + if (fields == null) { + /* This means that the class could not be loaded. + * give up. + */ + return; + } + + writer.startOp(writer.NO_PAREN, 0); + /* Clear the SUPER bit, which is also used as SYNCHRONIZED bit. */ + int modifiedModifiers = modifiers & ~(Modifier.SYNCHRONIZED + | STRICTFP); + if (clazz.isInterface()) + /* interfaces are implicitily abstract */ + modifiedModifiers &= ~Modifier.ABSTRACT; + if (parent instanceof MethodAnalyzer) { + /* method scope classes are implicitly private */ + modifiedModifiers &= ~Modifier.PRIVATE; + /* anonymous classes are implicitly final */ + if (name == null) + modifiedModifiers &= ~Modifier.FINAL; + } + String modif = Modifier.toString(modifiedModifiers); + if (modif.length() > 0) + writer.print(modif + " "); + if (isStrictFP()) { + /* The STRICTFP modifier is set. + * We handle it, since java.lang.reflect.Modifier is too dumb. + */ + writer.print("strictfp "); + } + /* interface is in modif */ + if (!clazz.isInterface()) + writer.print("class "); + writer.print(name); + ClassInfo superClazz = clazz.getSuperclass(); + if (superClazz != null && + superClazz.getName() != "java.lang.Object") { + writer.breakOp(); + writer.print(" extends " + (writer.getClassString + (superClazz, Scope.CLASSNAME))); + } + ClassInfo[] interfaces = clazz.getInterfaces(); + if (interfaces.length > 0) { + writer.breakOp(); + writer.print(clazz.isInterface() ? " extends " : " implements "); + writer.startOp(writer.EXPL_PAREN, 1); + for (int i=0; i < interfaces.length; i++) { + if (i > 0) { + writer.print(", "); + writer.breakOp(); + } + writer.print(writer.getClassString + (interfaces[i], Scope.CLASSNAME)); + } + writer.endOp(); + } + writer.println(); + + writer.openBraceClass(); + writer.tab(); + dumpBlock(writer, pl, done, scale); + writer.untab(); + writer.closeBraceClass(); + } + public void dumpBlock(TabbedPrintWriter writer) throws IOException { @@ -448,6 +538,7 @@ public class ClassAnalyzer ProgressListener pl, double done, double scale) throws IOException { + double subScale = scale / getComplexity(); writer.pushScope(this); boolean needFieldNewLine = false; @@ -541,6 +632,7 @@ public class ClassAnalyzer needNewLine = true; } writer.popScope(); + clazz.drop(clazz.DECLARATIONS); } public void dumpSource(TabbedPrintWriter writer) @@ -553,64 +645,8 @@ public class ClassAnalyzer ProgressListener pl, double done, double scale) throws IOException { - if (fields == null) { - /* This means that the class could not be loaded. - * give up. - */ - return; - } - - writer.startOp(writer.NO_PAREN, 0); - /* Clear the SUPER bit, which is also used as SYNCHRONIZED bit. */ - int modifiedModifiers = modifiers & ~Modifier.SYNCHRONIZED; - if (clazz.isInterface()) - /* interfaces are implicitily abstract */ - modifiedModifiers &= ~Modifier.ABSTRACT; - if (parent instanceof MethodAnalyzer) { - /* method scope classes are implicitly private */ - modifiedModifiers &= ~Modifier.PRIVATE; - /* anonymous classes are implicitly final */ - if (name == null) - modifiedModifiers &= ~Modifier.FINAL; - } - String modif = Modifier.toString(modifiedModifiers); - if (modif.length() > 0) - writer.print(modif + " "); - /* interface is in modif */ - if (!clazz.isInterface()) - writer.print("class "); - writer.print(name); - ClassInfo superClazz = clazz.getSuperclass(); - if (superClazz != null && - superClazz.getName() != "java.lang.Object") { - writer.breakOp(); - writer.print(" extends " + (writer.getClassString - (superClazz, Scope.CLASSNAME))); - } - ClassInfo[] interfaces = clazz.getInterfaces(); - if (interfaces.length > 0) { - writer.breakOp(); - writer.print(clazz.isInterface() ? " extends " : " implements "); - writer.startOp(writer.EXPL_PAREN, 1); - for (int i=0; i < interfaces.length; i++) { - if (i > 0) { - writer.print(", "); - writer.breakOp(); - } - writer.print(writer.getClassString - (interfaces[i], Scope.CLASSNAME)); - } - writer.endOp(); - } + dumpDeclaration(writer, pl, done, scale); writer.println(); - - writer.openBraceClass(); - writer.tab(); - dumpBlock(writer, pl, done, scale); - writer.untab(); - writer.closeBraceClass(); - writer.println(); - clazz.drop(clazz.DECLARATIONS); } public void dumpJavaFile(TabbedPrintWriter writer) @@ -648,7 +684,11 @@ public class ClassAnalyzer } public boolean conflicts(String name, int usageType) { - ClassInfo info = clazz; + return conflicts(clazz, name, usageType); + } + + private static boolean conflicts(ClassInfo info, + String name, int usageType) { while (info != null) { if (usageType == NOSUPERMETHODNAME || usageType == METHODNAME) { MethodInfo[] minfos = info.getMethods(); @@ -676,6 +716,11 @@ public class ClassAnalyzer if (usageType == NOSUPERFIELDNAME || usageType == NOSUPERMETHODNAME) return false; + + ClassInfo[] ifaces = info.getInterfaces(); + for (int i = 0; i < ifaces.length; i++) + if (conflicts(ifaces[i], name, usageType)) + return true; info = info.getSuperclass(); } return false; diff --git a/jode/jode/decompiler/Decompiler.java b/jode/jode/decompiler/Decompiler.java index 745f9df..d63289b 100644 --- a/jode/jode/decompiler/Decompiler.java +++ b/jode/jode/decompiler/Decompiler.java @@ -221,11 +221,9 @@ public class Decompiler { classPath = new ClassPath(cp); } - /* XXX, comment the next line, as soon as ClassInfo.forName is - * no longer used. */ - ClassInfo.setClassPath(classPath); ClassInfo clazz = classPath.getClassInfo(className); - ImportHandler imports = new ImportHandler(importPackageLimit, + ImportHandler imports = new ImportHandler(classPath, + importPackageLimit, importClassLimit); TabbedPrintWriter tabbedWriter = new TabbedPrintWriter(writer, imports, false, diff --git a/jode/jode/decompiler/FieldAnalyzer.java b/jode/jode/decompiler/FieldAnalyzer.java index cb75705..2f20b73 100644 --- a/jode/jode/decompiler/FieldAnalyzer.java +++ b/jode/jode/decompiler/FieldAnalyzer.java @@ -59,7 +59,7 @@ public class FieldAnalyzer implements Analyzer { if (fd.getConstant() != null) { constant = new ConstOperator(fd.getConstant()); constant.setType(type); - constant.makeInitializer(); + constant.makeInitializer(type); } } @@ -109,7 +109,7 @@ public class FieldAnalyzer implements Analyzer { } analyzedSynthetic(); } else - expr.makeInitializer(); + expr.makeInitializer(type); constant = expr; return true; diff --git a/jode/jode/decompiler/ImportHandler.java b/jode/jode/decompiler/ImportHandler.java index 92ddb53..fb4754d 100644 --- a/jode/jode/decompiler/ImportHandler.java +++ b/jode/jode/decompiler/ImportHandler.java @@ -20,6 +20,7 @@ package jode.decompiler; import jode.GlobalOptions; import jode.bytecode.ClassInfo; +import jode.bytecode.ClassPath; import jode.type.Type; import jode.type.ArrayType; import jode.type.ClassInfoType; @@ -53,6 +54,7 @@ public class ImportHandler { /* Classes that doesn't need to be qualified. */ Hashtable cachedClassNames = null; ClassAnalyzer main; + ClassPath classPath; String className; String pkg; @@ -77,11 +79,13 @@ public class ImportHandler { } }; - public ImportHandler() { - this(DEFAULT_PACKAGE_LIMIT, DEFAULT_CLASS_LIMIT); + public ImportHandler(ClassPath classPath) { + this(classPath, DEFAULT_PACKAGE_LIMIT, DEFAULT_CLASS_LIMIT); } - public ImportHandler(int packageLimit, int classLimit) { + public ImportHandler(ClassPath classPath, + int packageLimit, int classLimit) { + this.classPath = classPath; importPackageLimit = packageLimit; importClassLimit = classLimit; } @@ -112,12 +116,12 @@ public class ImportHandler { if (pkg.length() != 0) { /* Does this conflict with a class in this package? */ - if (ClassInfo.exists(pkg+name)) + if (classPath.existsClass(pkg+name)) return true; } else { /* Does this conflict with a class in this unnamed * package? */ - if (ClassInfo.exists(name.substring(1))) + if (classPath.existsClass(name.substring(1))) return true; } @@ -129,7 +133,7 @@ public class ImportHandler { importName = importName.substring (0, importName.length()-2); if (!importName.equals(pkgName)) { - if (ClassInfo.exists(importName+name)) + if (classPath.existsClass(importName+name)) return true; } } else { @@ -399,5 +403,3 @@ public class ImportHandler { return 1; } } - - diff --git a/jode/jode/decompiler/LocalInfo.java b/jode/jode/decompiler/LocalInfo.java index 39a32df..9e2df66 100644 --- a/jode/jode/decompiler/LocalInfo.java +++ b/jode/jode/decompiler/LocalInfo.java @@ -388,8 +388,7 @@ public class LocalInfo implements Declarable { if (((LocalVarOperator) enum.nextElement()).isWrite()) writes++; } - if (writes > 1) - return false; + /* FIXME: Check if declaring final is okay */ li.isFinal = true; return true; } diff --git a/jode/jode/decompiler/Main.java b/jode/jode/decompiler/Main.java index 4a5c080..4e51d4b 100644 --- a/jode/jode/decompiler/Main.java +++ b/jode/jode/decompiler/Main.java @@ -171,7 +171,7 @@ public class Main extends Options { ("Can't read "+ex.getMessage()+"."); GlobalOptions.err.println ("Check the class path ("+classPathStr+ - ") and check that you use the java class name."); + ") and check that you use the java class name."); } catch (IOException ex) { GlobalOptions.err.println ("Can't write source of "+className+"."); @@ -181,6 +181,22 @@ public class Main extends Options { } public static void main(String[] params) { + try { + decompile(params); + } catch (ExceptionInInitializerError ex) { + ex.getException().printStackTrace(); + } catch (Throwable ex) { + ex.printStackTrace(); + } + /* When AWT applications are compiled with insufficient + * classpath the type guessing by reflection code can + * generate an awt thread that will prevent normal + * exiting. + */ + System.exit(0); + } + + public static void decompile(String[] params) { if (params.length == 0) { usage(); return; @@ -305,8 +321,8 @@ public class Main extends Options { if (errorInParams) return; classPath = new ClassPath(classPathStr); - ClassInfo.setClassPath(classPath); - ImportHandler imports = new ImportHandler(importPackageLimit, + ImportHandler imports = new ImportHandler(classPath, + importPackageLimit, importClassLimit); ZipOutputStream destZip = null; diff --git a/jode/jode/decompiler/MethodAnalyzer.java b/jode/jode/decompiler/MethodAnalyzer.java index 3f7e6de..7ed3c39 100644 --- a/jode/jode/decompiler/MethodAnalyzer.java +++ b/jode/jode/decompiler/MethodAnalyzer.java @@ -83,6 +83,11 @@ public class MethodAnalyzer implements Scope, ClassDeclarer { * The minimal visible complexity. */ private static double STEP_COMPLEXITY = 0.01; + /** + * The value of the strictfp modifier. + * JDK1.1 doesn't define it. + */ + private static int STRICTFP = 0x800; /** * The import handler where we should register our types. */ @@ -310,6 +315,14 @@ public class MethodAnalyzer implements Scope, ClassDeclarer { return minfo.isSynthetic(); } + /** + * Checks if this method is strictfp + * @return true, iff this method is synthetic. + */ + public final boolean isStrictFP() { + return (minfo.getModifiers() & STRICTFP) != 0; + } + /** * Tells if this method is the constructor$xx method generated by jikes. * @param value true, iff this method is the jikes constructor. @@ -681,7 +694,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer { if (synth.getKind() == synth.GETCLASS) return true; if (synth.getKind() >= synth.ACCESSGETFIELD - && synth.getKind() <= synth.ACCESSCONSTRUCTOR + && synth.getKind() <= synth.ACCESSDUPPUTSTATIC && (Options.options & Options.OPTION_INNER) != 0 && (Options.options & Options.OPTION_ANON) != 0) return true; @@ -711,10 +724,12 @@ public class MethodAnalyzer implements Scope, ClassDeclarer { if (isJikesBlockInitializer) return true; - /* The default constructor must be empty of course */ + /* The default constructor must be empty + * and mustn't throw exceptions */ if (getMethodHeader() == null || !(getMethodHeader().getBlock() instanceof jode.flow.EmptyBlock) - || !getMethodHeader().hasNoJumps()) + || !getMethodHeader().hasNoJumps() + || exceptions.length > 0) return false; if (declareAsConstructor @@ -804,7 +819,9 @@ public class MethodAnalyzer implements Scope, ClassDeclarer { * as final. */ if (isConstructor() && isStatic()) - modifiedModifiers &= ~Modifier.FINAL; + modifiedModifiers &= ~(Modifier.FINAL | Modifier.PUBLIC + | Modifier.PROTECTED | Modifier.PRIVATE); + modifiedModifiers &= ~STRICTFP; writer.startOp(writer.NO_PAREN, 0); writer.startOp(writer.NO_PAREN, 5); @@ -819,6 +836,22 @@ public class MethodAnalyzer implements Scope, ClassDeclarer { writer.print(delim + modif); if (modif.length() > 0) delim = " "; + if (isStrictFP()) { + /* The STRICTFP modifier is set. + * We handle it, since java.lang.reflect.Modifier is too dumb. + */ + + /* If STRICTFP is already set for class don't set it for method. + * And don't set STRICTFP for native methods or constructors. + */ + if (!classAnalyzer.isStrictFP() + && !isConstructor() + && (modifiedModifiers & Modifier.NATIVE) == 0) { + writer.print(delim + "strictfp"); + delim = " "; + } + } + if (isConstructor && (isStatic() || (classAnalyzer.getName() == null diff --git a/jode/jode/decompiler/OuterValues.java b/jode/jode/decompiler/OuterValues.java index 503c25c..8dd9a38 100644 --- a/jode/jode/decompiler/OuterValues.java +++ b/jode/jode/decompiler/OuterValues.java @@ -1,4 +1,4 @@ -/* OuterValues Copyright (C) 1998-1999 Jochen Hoenicke. +/* OuterValues Copyright (C) 1998-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 @@ -94,6 +94,7 @@ public class OuterValues private Expression[] head; private Vector ovListeners; private boolean jikesAnonymousInner; + private boolean implicitOuterClass; /** * The maximal number of parameters used for outer values. @@ -293,6 +294,17 @@ public class OuterValues return jikesAnonymousInner; } + /** + * Javac 1.3 doesn't give an outer class reference for anonymous + * classes that extend inner classes, provided the outer class is + * the normal this parameter. Instead it takes a normal outer + * value parameter for this. This method tells if this is such a + * class. + */ + public boolean isImplicitOuterClass() { + return implicitOuterClass; + } + public void addOuterValueListener(OuterValueListener l) { if (ovListeners == null) ovListeners = new Vector(); @@ -308,6 +320,10 @@ public class OuterValues jikesAnonymousInner = value; } + public void setImplicitOuterClass(boolean value) { + implicitOuterClass = value; + } + private static int countSlots(Expression[] exprs, int length) { int slots = 0; for (int i=0; i < length; i++) @@ -369,6 +385,8 @@ public class OuterValues } if (jikesAnonymousInner) sb.append("!jikesAnonymousInner"); + if (implicitOuterClass) + sb.append("!implicitOuterClass"); return sb.append("]").toString(); } } diff --git a/jode/jode/decompiler/TabbedPrintWriter.java b/jode/jode/decompiler/TabbedPrintWriter.java index 1e4652a..3769fdd 100644 --- a/jode/jode/decompiler/TabbedPrintWriter.java +++ b/jode/jode/decompiler/TabbedPrintWriter.java @@ -1,4 +1,4 @@ -/* TabbedPrintWriter Copyright (C) 1998-1999 Jochen Hoenicke. +/* TabbedPrintWriter Copyright (C) 1998-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 @@ -111,11 +111,8 @@ public class TabbedPrintWriter { } public void startOp(int opts, int penalty, int pos) { - if (startPos != -1) { - GlobalOptions.err.println("WARNING: missing breakOp"); - Thread.dumpStack(); - return; - } + if (startPos != -1) + throw new InternalError("missing breakOp"); startPos = pos; options = opts; breakPenalty = penalty; @@ -567,8 +564,9 @@ public class TabbedPrintWriter { Stack state = new Stack(); int pos = currentLine.length(); while (currentBP.parentBP != null) { - state.push(new Integer(currentBP.options)); state.push(new Integer(currentBP.breakPenalty)); + /* We don't want parentheses or unconventional line breaking */ + currentBP.options = DONT_BREAK; currentBP.endPos = pos; currentBP = currentBP.parentBP; } @@ -579,8 +577,7 @@ public class TabbedPrintWriter { Stack state = (Stack) s; while (!state.isEmpty()) { int penalty = ((Integer) state.pop()).intValue(); - int options = ((Integer) state.pop()).intValue(); - startOp(options, penalty); + startOp(DONT_BREAK, penalty); } } diff --git a/jode/jode/expr/ArrayStoreOperator.java b/jode/jode/expr/ArrayStoreOperator.java index 796262b..3f1e736 100644 --- a/jode/jode/expr/ArrayStoreOperator.java +++ b/jode/jode/expr/ArrayStoreOperator.java @@ -32,4 +32,29 @@ public class ArrayStoreOperator extends ArrayLoadOperator public boolean matches(Operator loadop) { return loadop instanceof ArrayLoadOperator; } + + public void dumpExpression(TabbedPrintWriter writer) + throws java.io.IOException { + Type arrType = subExpressions[0].getType().getHint(); + if (arrType instanceof ArrayType) { + Type elemType = ((ArrayType) arrType).getElementType(); + if (!elemType.isOfType(getType())) { + /* We need an explicit widening cast */ + writer.print("("); + writer.startOp(writer.EXPL_PAREN, 1); + writer.print("("); + writer.printType(Type.tArray(getType().getHint())); + writer.print(") "); + writer.breakOp(); + subExpressions[0].dumpExpression(writer, 700); + writer.print(")"); + writer.breakOp(); + writer.print("["); + subExpressions[1].dumpExpression(writer, 0); + writer.print("]"); + return; + } + } + super.dumpExpression(writer); + } } diff --git a/jode/jode/expr/ConstOperator.java b/jode/jode/expr/ConstOperator.java index bcc0561..c7fa123 100644 --- a/jode/jode/expr/ConstOperator.java +++ b/jode/jode/expr/ConstOperator.java @@ -110,7 +110,7 @@ public class ConstOperator extends NoArgOperator { return false; } - public void makeInitializer() { + public void makeInitializer(Type type) { isInitializer = true; } diff --git a/jode/jode/expr/ConstantArrayOperator.java b/jode/jode/expr/ConstantArrayOperator.java index 0a3be5a..eb9269e 100644 --- a/jode/jode/expr/ConstantArrayOperator.java +++ b/jode/jode/expr/ConstantArrayOperator.java @@ -48,7 +48,7 @@ public class ConstantArrayOperator extends Operator { empty = new ConstOperator(emptyVal); empty.setType(argType); - empty.makeInitializer(); + empty.makeInitializer(argType); initOperands(size); for (int i=0; i < subExpressions.length; i++) setSubExpressions(i, empty); @@ -74,7 +74,7 @@ public class ConstantArrayOperator extends Operator { setType(Type.tSuperType(Type.tArray(value.getType()))); subExpressions[index] = value; value.parent = this; - value.makeInitializer(); + value.makeInitializer(argType); return true; } @@ -82,8 +82,9 @@ public class ConstantArrayOperator extends Operator { return 200; } - public void makeInitializer() { - isInitializer = true; + public void makeInitializer(Type type) { + if (type.getHint().isOfType(getType())) + isInitializer = true; } public Expression simplify() { diff --git a/jode/jode/expr/Expression.java b/jode/jode/expr/Expression.java index 84c6939..59bf41f 100644 --- a/jode/jode/expr/Expression.java +++ b/jode/jode/expr/Expression.java @@ -210,7 +210,7 @@ public abstract class Expression { return null; } - public void makeInitializer() { + public void makeInitializer(Type type) { } public boolean isConstant() { diff --git a/jode/jode/expr/FieldOperator.java b/jode/jode/expr/FieldOperator.java index 0e0485e..ac55209 100644 --- a/jode/jode/expr/FieldOperator.java +++ b/jode/jode/expr/FieldOperator.java @@ -26,6 +26,7 @@ import jode.bytecode.FieldInfo; import jode.bytecode.ClassInfo; import jode.bytecode.ClassPath; import jode.bytecode.Reference; +import jode.bytecode.TypeSignature; import jode.decompiler.MethodAnalyzer; import jode.decompiler.ClassAnalyzer; import jode.decompiler.MethodAnalyzer; @@ -35,6 +36,7 @@ import jode.decompiler.TabbedPrintWriter; import jode.decompiler.Scope; import java.io.IOException; +import java.lang.reflect.Modifier; ///#def COLLECTIONS java.util import java.util.Collection; ///#enddef @@ -159,6 +161,34 @@ public abstract class FieldOperator extends Operator { return clazz.getFields(); } + private static FieldInfo getFieldInfo(ClassInfo clazz, + String name, String type) { + while (clazz != null) { + FieldInfo field = clazz.findField(name, type); + if (field != null) + return field; + + ClassInfo[] ifaces = clazz.getInterfaces(); + for (int i = 0; i < ifaces.length; i++) { + field = getFieldInfo(ifaces[i], name, type); + if (field != null) + return field; + } + + clazz = clazz.getSuperclass(); + } + return null; + } + + public FieldInfo getFieldInfo() { + ClassInfo clazz; + if (ref.getClazz().charAt(0) == '[') + clazz = classPath.getClassInfo("java.lang.Object"); + else + clazz = TypeSignature.getClassInfo(classPath, ref.getClazz()); + return getFieldInfo(clazz, ref.getName(), ref.getType()); + } + public boolean needsCast(Type type) { if (type instanceof NullType) return true; @@ -168,6 +198,37 @@ public abstract class FieldOperator extends Operator { ClassInfo clazz = ((ClassInfoType) classType).getClassInfo(); ClassInfo parClazz = ((ClassInfoType) type).getClassInfo(); + FieldInfo field = clazz.findField(ref.getName(), ref.getType()); + + find_field: + while (field == null) { + ClassInfo ifaces[] = clazz.getInterfaces(); + for (int i = 0; i < ifaces.length; i++) { + field = ifaces[i].findField(ref.getName(), ref.getType()); + if (field != null) + break find_field; + } + clazz = clazz.getSuperclass(); + if (clazz == null) + /* Weird, field not existing? */ + return false; + field = clazz.findField(ref.getName(), ref.getType()); + } + if (Modifier.isPrivate(field.getModifiers())) + return parClazz != clazz; + else if ((field.getModifiers() + & (Modifier.PROTECTED | Modifier.PUBLIC)) == 0) { + /* Field is protected. We need a cast if parClazz is in + * other package than clazz. + */ + int lastDot = clazz.getName().lastIndexOf('.'); + if (lastDot == -1 + || lastDot != parClazz.getName().lastIndexOf('.') + || !(parClazz.getName() + .startsWith(clazz.getName().substring(0,lastDot)))) + return true; + } + while (clazz != parClazz && clazz != null) { FieldInfo[] fields = parClazz.getFields(); for (int i = 0; i < fields.length; i++) { @@ -246,20 +307,8 @@ public abstract class FieldOperator extends Operator { */ getField() == null && writer.conflicts(fieldName, null, - Scope.NOSUPERFIELDNAME))) { - - ClassAnalyzer ana = methodAnalyzer.getClassAnalyzer(); - while (ana.getParent() instanceof ClassAnalyzer - && ana != scope) - ana = (ClassAnalyzer) ana.getParent(); - if (ana == scope) - // For a simple outer class we can say this - writer.print("this"); - else { - // For a class that owns a method that owns - // us, we have to give the full class name - thisOp.dumpExpression(writer, 950); - } + Scope.NOSUPERFIELDNAME))) { + thisOp.dumpExpression(writer, 950); writer.breakOp(); writer.print("."); } diff --git a/jode/jode/expr/InvokeOperator.java b/jode/jode/expr/InvokeOperator.java index e5f6f47..ad92ef3 100644 --- a/jode/jode/expr/InvokeOperator.java +++ b/jode/jode/expr/InvokeOperator.java @@ -62,6 +62,7 @@ public final class InvokeOperator extends Operator int methodFlag; MethodType methodType; String methodName; + Reference ref; int skippedArgs; ClassType classType; Type[] hints; @@ -149,6 +150,7 @@ public final class InvokeOperator extends Operator int methodFlag, Reference reference) { super(Type.tUnknown, 0); this.classPath = methodAnalyzer.getClassAnalyzer().getClassPath(); + this.ref = reference; this.methodType = Type.tMethod(classPath, reference.getType()); this.methodName = reference.getName(); this.classType = (ClassType) @@ -208,6 +210,26 @@ public final class InvokeOperator extends Operator return methodName; } + private static MethodInfo getMethodInfo(ClassInfo clazz, + String name, String type) { + while (clazz != null) { + MethodInfo method = clazz.findMethod(name, type); + if (method != null) + return method; + clazz = clazz.getSuperclass(); + } + return null; + } + + public MethodInfo getMethodInfo() { + ClassInfo clazz; + if (ref.getClazz().charAt(0) == '[') + clazz = classPath.getClassInfo("java.lang.Object"); + else + clazz = TypeSignature.getClassInfo(classPath, ref.getClazz()); + return getMethodInfo(clazz, ref.getName(), ref.getType()); + } + public Type getClassType() { return classType; } @@ -603,14 +625,20 @@ public final class InvokeOperator extends Operator synth.getReference()); break; case SyntheticAnalyzer.ACCESSPUTFIELD: + case SyntheticAnalyzer.ACCESSDUPPUTFIELD: op = new StoreInstruction (new PutFieldOperator(methodAnalyzer, false, synth.getReference())); + if (synth.getKind() == synth.ACCESSDUPPUTFIELD) + ((StoreInstruction) op).makeNonVoid(); break; case SyntheticAnalyzer.ACCESSPUTSTATIC: + case SyntheticAnalyzer.ACCESSDUPPUTSTATIC: op = new StoreInstruction (new PutFieldOperator(methodAnalyzer, true, synth.getReference())); + if (synth.getKind() == synth.ACCESSDUPPUTSTATIC) + ((StoreInstruction) op).makeNonVoid(); break; case SyntheticAnalyzer.ACCESSMETHOD: op = new InvokeOperator(methodAnalyzer, ACCESSSPECIAL, @@ -669,9 +697,35 @@ public final class InvokeOperator extends Operator Type realClassType; if (methodFlag == STATIC) realClassType = classType; - else { - if (param == 0) - return paramTypes[0] instanceof NullType; + else if (param == 0) { + if (paramTypes[0] instanceof NullType) + return true; + if (!(paramTypes[0] instanceof ClassInfoType + && classType instanceof ClassInfoType)) + return false; + + ClassInfo clazz = ((ClassInfoType) classType).getClassInfo(); + ClassInfo parClazz + = ((ClassInfoType) paramTypes[0]).getClassInfo(); + MethodInfo method = getMethodInfo(); + if (method == null) + /* This is a NoSuchMethodError */ + return false; + if (Modifier.isPrivate(method.getModifiers())) + return parClazz != clazz; + else if ((method.getModifiers() + & (Modifier.PROTECTED | Modifier.PUBLIC)) == 0) { + /* Method is protected. We need a cast if parClazz is in + * other package than clazz. + */ + int lastDot = clazz.getName().lastIndexOf('.'); + if (lastDot != parClazz.getName().lastIndexOf('.') + || !(parClazz.getName() + .startsWith(clazz.getName().substring(0,lastDot+1)))) + return true; + } + return false; + } else { realClassType = paramTypes[0]; } @@ -765,6 +819,7 @@ public final class InvokeOperator extends Operator int arg = 1; int length = subExpressions.length; boolean jikesAnonymousInner = false; + boolean implicitOuterClass = false; if ((Options.options & Options.OPTION_ANON) != 0 && clazzAna != null && clazz.isMethodScoped()) { @@ -772,6 +827,7 @@ public final class InvokeOperator extends Operator OuterValues ov = clazzAna.getOuterValues(); arg += ov.getCount(); jikesAnonymousInner = ov.isJikesAnonymousInner(); + implicitOuterClass = ov.isImplicitOuterClass(); for (int i=1; i< arg; i++) { Expression expr = subExpressions[i]; @@ -799,7 +855,9 @@ public final class InvokeOperator extends Operator if ((~Options.options & (Options.OPTION_INNER | Options.OPTION_CONTRAFO)) == 0 && clazz.getOuterClass() != null - && !Modifier.isStatic(clazz.getModifiers())) { + && !Modifier.isStatic(clazz.getModifiers()) + && !implicitOuterClass + && arg < length) { Expression outerExpr = jikesAnonymousInner ? subExpressions[--length] @@ -883,6 +941,7 @@ public final class InvokeOperator extends Operator boolean qualifiedNew = false; boolean jikesAnonymousInner = false; + boolean implicitOuterClass = false; /* clazz != null, since an array doesn't have a constructor */ @@ -894,6 +953,7 @@ public final class InvokeOperator extends Operator OuterValues ov = clazzAna.getOuterValues(); arg += ov.getCount(); jikesAnonymousInner = ov.isJikesAnonymousInner(); + implicitOuterClass = ov.isImplicitOuterClass(); if (clazz.getClassName() == null) { /* This is an anonymous class */ @@ -933,47 +993,54 @@ public final class InvokeOperator extends Operator (Options.OPTION_INNER | Options.OPTION_CONTRAFO)) == 0) { - Expression outerExpr = jikesAnonymousInner - ? subExpressions[--length] - : subExpressions[arg++]; - if (outerExpr instanceof CheckNullOperator) { - CheckNullOperator cno = (CheckNullOperator) outerExpr; - outerExpr = cno.subExpressions[0]; - } else if (!(outerExpr instanceof ThisOperator)) { - // Complain about missing checknull, but not if - // that is the known bug in jikes. - if (!jikesAnonymousInner) - writer.print("MISSING CHECKNULL "); - } + if (implicitOuterClass) { + /* Outer class is "this" and is not given + * explicitly. No need to print something. + */ + } else if (arg < length) { + Expression outerExpr = jikesAnonymousInner + ? subExpressions[--length] + : subExpressions[arg++]; + if (outerExpr instanceof CheckNullOperator) { + CheckNullOperator cno = (CheckNullOperator) outerExpr; + outerExpr = cno.subExpressions[0]; + } else { + /* We used to complain about MISSING CHECKNULL + * here except for ThisOperators. But javac + * v8 doesn't seem to create CHECKNULL ops. + */ + } - if (outerExpr instanceof ThisOperator) { - Scope scope = writer.getScope - (((ThisOperator) outerExpr).getClassInfo(), - Scope.CLASSSCOPE); - if (writer.conflicts(clazz.getClassName(), - scope, Scope.CLASSNAME)) { + if (outerExpr instanceof ThisOperator) { + Scope scope = writer.getScope + (((ThisOperator) outerExpr).getClassInfo(), + Scope.CLASSSCOPE); + if (writer.conflicts(clazz.getClassName(), + scope, Scope.CLASSNAME)) { + qualifiedNew = true; + outerExpr.dumpExpression(writer, 950); + writer.breakOp(); + writer.print("."); + } + } else { qualifiedNew = true; - outerExpr.dumpExpression(writer, 950); + if (outerExpr.getType() instanceof NullType) { + writer.print("("); + writer.startOp(writer.EXPL_PAREN, 1); + writer.print("("); + writer.printType(Type.tClass(clazz)); + writer.print(") "); + writer.breakOp(); + outerExpr.dumpExpression(writer, 700); + writer.endOp(); + writer.print(")"); + } else + outerExpr.dumpExpression(writer, 950); writer.breakOp(); writer.print("."); } - } else { - qualifiedNew = true; - if (outerExpr.getType() instanceof NullType) { - writer.print("("); - writer.startOp(writer.EXPL_PAREN, 1); - writer.print("("); - writer.printType(Type.tClass(clazz)); - writer.print(") "); - writer.breakOp(); - outerExpr.dumpExpression(writer, 700); - writer.endOp(); - writer.print(")"); - } else - outerExpr.dumpExpression(writer, 950); - writer.breakOp(); - writer.print("."); - } + } else + writer.print("MISSING OUTEREXPR "); } if (subExpressions[0] instanceof NewOperator @@ -1102,10 +1169,10 @@ public final class InvokeOperator extends Operator Scope scope = writer.getScope(thisOp.getClassInfo(), Scope.CLASSSCOPE); if (writer.conflicts(methodName, scope, Scope.METHODNAME) - || (/* This field is inherited from the parent of + || (/* This method is inherited from the parent of * an outer class, or it is inherited from the * parent of this class and there is a conflicting - * field in some outer class. + * method in some outer class. */ getMethodAnalyzer() == null && (!isThis() || diff --git a/jode/jode/flow/CaseBlock.java b/jode/jode/flow/CaseBlock.java index ab5701b..b21712d 100644 --- a/jode/jode/flow/CaseBlock.java +++ b/jode/jode/flow/CaseBlock.java @@ -19,6 +19,7 @@ package jode.flow; import jode.expr.ConstOperator; +import jode.type.Type; /** * This block represents a case instruction. A case instruction is a @@ -171,8 +172,9 @@ public class CaseBlock extends StructuredBlock { writer.untab(); } ConstOperator constOp = new ConstOperator(new Integer(value)); - constOp.setType(((SwitchBlock)outer).getInstruction().getType()); - constOp.makeInitializer(); + Type type = ((SwitchBlock)outer).getInstruction().getType(); + constOp.setType(type); + constOp.makeInitializer(type); writer.print("case " + constOp.toString() + ":"); } if (subBlock instanceof EmptyBlock diff --git a/jode/jode/flow/InstructionBlock.java b/jode/jode/flow/InstructionBlock.java index 4476a89..76e01d2 100644 --- a/jode/jode/flow/InstructionBlock.java +++ b/jode/jode/flow/InstructionBlock.java @@ -114,7 +114,6 @@ public class InstructionBlock extends InstructionContainer { * change this to a initializing variable declaration. */ isDeclaration = true; - storeOp.getSubExpressions()[1].makeInitializer(); declareSet.remove(local); } } @@ -142,6 +141,7 @@ public class InstructionBlock extends InstructionContainer { local.dumpDeclaration(writer); writer.breakOp(); writer.print(" = "); + store.getSubExpressions()[1].makeInitializer(local.getType()); store.getSubExpressions()[1].dumpExpression(writer.IMPL_PAREN, writer); writer.endOp(); diff --git a/jode/jode/flow/JsrBlock.java b/jode/jode/flow/JsrBlock.java index d1eb9e8..6f2848e 100644 --- a/jode/jode/flow/JsrBlock.java +++ b/jode/jode/flow/JsrBlock.java @@ -33,12 +33,21 @@ public class JsrBlock extends StructuredBlock { * The inner block that jumps to the subroutine. */ StructuredBlock innerBlock; + boolean good = false; public JsrBlock() { innerBlock = new EmptyBlock(); innerBlock.outer = this; } + public void setGood(boolean g) { + good = g; + } + + public boolean isGood() { + return good; + } + /** * Sets the successors of this structured block. This should be only * called once, by FlowBlock.setSuccessors(). diff --git a/jode/jode/flow/LoopBlock.java b/jode/jode/flow/LoopBlock.java index 6d52ace..66ed810 100644 --- a/jode/jode/flow/LoopBlock.java +++ b/jode/jode/flow/LoopBlock.java @@ -269,7 +269,6 @@ public class LoopBlock extends StructuredBlock implements BreakableBlock { * change this to a initializing variable declaration. */ isDeclaration = true; - storeOp.getSubExpressions()[1].makeInitializer(); declareSet.remove(local); } } @@ -336,6 +335,8 @@ public class LoopBlock extends StructuredBlock implements BreakableBlock { local.dumpDeclaration(writer); writer.breakOp(); writer.print(" = "); + store.getSubExpressions()[1] + .makeInitializer(local.getType()); store.getSubExpressions()[1].dumpExpression(writer, 100); writer.endOp(); } else diff --git a/jode/jode/flow/Makefile.am b/jode/jode/flow/Makefile.am index 4cec7ad..907f655 100644 --- a/jode/jode/flow/Makefile.am +++ b/jode/jode/flow/Makefile.am @@ -39,6 +39,7 @@ MY_JAVA_FILES = \ RetBlock.java \ ReturnBlock.java \ SequentialBlock.java \ + SlotSet.java \ SpecialBlock.java \ StructuredBlock.java \ SwitchBlock.java \ diff --git a/jode/jode/flow/SpecialBlock.java b/jode/jode/flow/SpecialBlock.java index 0912d8e..0d52e36 100644 --- a/jode/jode/flow/SpecialBlock.java +++ b/jode/jode/flow/SpecialBlock.java @@ -147,6 +147,9 @@ public class SpecialBlock extends StructuredBlock { * to: * method_invocation() * + * With java1.3 due to access$ methods the method_invocation can + * already be a non void store instruction. + * * PUSH arg1 * PUSH arg2 * POP2 @@ -173,7 +176,8 @@ public class SpecialBlock extends StructuredBlock { if (instr.getType().stackSize() == count) { StructuredBlock newBlock; - if (instr instanceof InvokeOperator) { + if (instr instanceof InvokeOperator + || instr instanceof StoreInstruction) { Expression newExpr = new PopOperator(instr.getType()).addOperand(instr); prev.setInstruction(newExpr); diff --git a/jode/jode/flow/TransformConstructors.java b/jode/jode/flow/TransformConstructors.java index 322d742..8d1e652 100644 --- a/jode/jode/flow/TransformConstructors.java +++ b/jode/jode/flow/TransformConstructors.java @@ -32,6 +32,8 @@ import jode.expr.*; import jode.type.MethodType; import jode.type.Type; import jode.bytecode.ClassInfo; +import jode.bytecode.ClassPath; +import jode.bytecode.MethodInfo; import java.io.IOException; import java.util.Vector; @@ -325,7 +327,18 @@ public class TransformConstructors { } } - if (minSuperOuter > 0) { + if (minSuperOuter == 1 + && superAna.getParent() instanceof ClassAnalyzer) { + /* Check if this is the implicit Outer Class */ + LocalLoadOperator llop = (LocalLoadOperator) subExpr[start]; + if (outerValues.getValueBySlot(llop.getLocalInfo().getSlot()) + instanceof ThisOperator) { + minSuperOuter = 0; + outerValues.setImplicitOuterClass(true); + } + } + + if (minSuperOuter > 0) { if (superOV == null || superOV.getCount() < minSuperOuter) { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) @@ -616,6 +629,29 @@ public class TransformConstructors { fo.getFieldType()) >= fieldSlot) return null; } + if (expr instanceof InvokeOperator) { + /* Don't allow method invocations that can throw a checked + * exception to leave the constructor. + */ + MethodInfo method = ((InvokeOperator) expr).getMethodInfo(); + String[] excs = method == null ? null : method.getExceptions(); + if (excs != null) { + ClassPath classPath = clazzAnalyzer.getClassPath(); + ClassInfo runtimeException + = classPath.getClassInfo("java.lang.RuntimeException"); + ClassInfo error = classPath.getClassInfo("java.lang.Error"); + for (int i = 0; i < excs.length; i++) { + ClassInfo exClass = classPath.getClassInfo(excs[i]); + try { + if (!runtimeException.superClassOf(exClass) + && !error.superClassOf(exClass)) + return null; + } catch (IOException ex) { + return null; + } + } + } + } if (expr instanceof Operator) { Operator op = (Operator) expr; Expression[] subExpr = op.getSubExpressions(); diff --git a/jode/jode/flow/TransformExceptionHandlers.java b/jode/jode/flow/TransformExceptionHandlers.java index 7cf5a26..c6b883c 100644 --- a/jode/jode/flow/TransformExceptionHandlers.java +++ b/jode/jode/flow/TransformExceptionHandlers.java @@ -1,4 +1,4 @@ -/* TransformExceptionHandlers Copyright (C) 1998-1999 Jochen Hoenicke. +/* TransformExceptionHandlers Copyright (C) 1998-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 @@ -210,7 +210,7 @@ public class TransformExceptionHandlers { * @param tryFlow the FlowBLock of the try block. * @param subRoutine the FlowBlock of the sub routine. */ - private void removeBadJSR(FlowBlock tryFlow, StructuredBlock catchBlock, + private void removeJSR(FlowBlock tryFlow, StructuredBlock catchBlock, FlowBlock subRoutine) { Jump nextJump; for (Jump jumps = tryFlow.getJumps(subRoutine); @@ -220,18 +220,27 @@ public class TransformExceptionHandlers { nextJump = jumps.next; if (prev instanceof EmptyBlock && prev.outer instanceof JsrBlock) { + JsrBlock jsr = (JsrBlock) prev.outer; if (prev.outer == catchBlock) { /* This is the mandatory jsr in the catch block */ continue; } - /* We have a JSR to the subroutine, which is badly placed. - * We complain here. - */ - DescriptionBlock msg - = new DescriptionBlock("ERROR: JSR FINALLY BLOCK!"); + tryFlow.removeSuccessor(jumps); prev.removeJump(); - msg.replace(prev.outer); + if (jsr.isGood()) { + StructuredBlock next = jsr.getNextBlock(); + jsr.removeBlock(); + if (next instanceof ReturnBlock) + removeReturnLocal((ReturnBlock) next); + } else { + /* We have a JSR to the subroutine, which is badly placed. + * We complain here. + */ + DescriptionBlock msg + = new DescriptionBlock("ERROR: JSR FINALLY BLOCK!"); + msg.replace(prev.outer); + } } else { /* We have a jump to the subroutine, that is wrong. * We complain here. @@ -330,18 +339,15 @@ public class TransformExceptionHandlers { StructuredBlock pred = skipFinExitChain(prev); if (pred instanceof JsrBlock) { - StructuredBlock jsrInner = ((JsrBlock) pred).innerBlock; + JsrBlock jsr = (JsrBlock) pred; + StructuredBlock jsrInner = jsr.innerBlock; if (jsrInner instanceof EmptyBlock && jsrInner.jump != null && jsrInner.jump.destination == subRoutine) { - /* The jump is preceeded by the right jsr. Remove - * the jsr. + /* The jump is preceeded by the right jsr. Mark the + * jsr as good. */ - tryFlow.removeSuccessor(jsrInner.jump); - jsrInner.removeJump(); - pred.removeBlock(); - if (prev instanceof ReturnBlock) - removeReturnLocal((ReturnBlock) prev); + jsr.setGood(true); continue; } } @@ -386,7 +392,8 @@ public class TransformExceptionHandlers { } } } - removeBadJSR(tryFlow, catchBlock, subRoutine); + if (tryFlow.getSuccessors().contains(subRoutine)) + removeJSR(tryFlow, catchBlock, subRoutine); } private void checkAndRemoveMonitorExit(FlowBlock tryFlow, @@ -412,7 +419,8 @@ public class TransformExceptionHandlers { } StructuredBlock pred = skipFinExitChain(prev); if (pred instanceof JsrBlock) { - StructuredBlock jsrInner = ((JsrBlock) pred).innerBlock; + JsrBlock jsr = (JsrBlock) pred; + StructuredBlock jsrInner = jsr.innerBlock; if (jsrInner instanceof EmptyBlock && jsrInner.jump != null) { FlowBlock dest = jsrInner.jump.destination; @@ -426,14 +434,10 @@ public class TransformExceptionHandlers { } if (dest == subRoutine) { - /* The jump is preceeded by the right jsr. Remove - * the jsr. + /* The jump is preceeded by the right jsr. + * Mark it as good. */ - tryFlow.removeSuccessor(jsrInner.jump); - jsrInner.removeJump(); - pred.removeBlock(); - if (prev instanceof ReturnBlock) - removeReturnLocal((ReturnBlock) prev); + jsr.setGood(true); continue; } } @@ -498,7 +502,8 @@ public class TransformExceptionHandlers { } if (subRoutine != null) { - removeBadJSR(tryFlow, catchBlock, subRoutine); + if (tryFlow.getSuccessors().contains(subRoutine)) + removeJSR(tryFlow, catchBlock, subRoutine); tryFlow.mergeBlockNr(subRoutine); } } diff --git a/jode/jode/jvm/CodeVerifier.java b/jode/jode/jvm/CodeVerifier.java index 5684e6d..f5ccbc0 100644 --- a/jode/jode/jvm/CodeVerifier.java +++ b/jode/jode/jvm/CodeVerifier.java @@ -1290,12 +1290,6 @@ public class CodeVerifier implements Opcodes { Handler[] catchers = block.getCatchers(); if (catchers.length > 0) { VerifyInfo excInfo = (VerifyInfo) info.clone(); - - for (int j = 0; j < info.locals.length; j++) { - if (info.locals[j].getTypeSig().charAt(0) == 'N') - throw new VerifyException - ("Uninitialized local in try block"); - } excInfo.stackHeight = 1; for (int i=0; i < catchers.length; i++) { String type = catchers[i].getType(); @@ -1321,10 +1315,6 @@ public class CodeVerifier implements Opcodes { if (catchers.length > 0 && instr.isStore()) { for (int i=0; i < catchers.length; i++) { int slot = instr.getLocalSlot(); - if (info.locals[slot].getTypeSig().charAt(0) == 'N') - throw new VerifyException - ("Uninitialized local in try block"); - Block catcher = catchers[i].getCatcher(); int catcherNr = catcher.getBlockNr(); VerifyInfo oldInfo = verifyInfos[catcherNr]; diff --git a/jode/jode/jvm/SyntheticAnalyzer.java b/jode/jode/jvm/SyntheticAnalyzer.java index 3cae53c..853fd66 100644 --- a/jode/jode/jvm/SyntheticAnalyzer.java +++ b/jode/jode/jvm/SyntheticAnalyzer.java @@ -33,6 +33,7 @@ import jode.type.Type; import jode.type.MethodType; import java.lang.reflect.Modifier; +import java.io.IOException; ///#def COLLECTIONS java.util import java.util.Arrays; @@ -49,6 +50,8 @@ public class SyntheticAnalyzer implements Opcodes { public final static int ACCESSPUTSTATIC = 6; public final static int ACCESSSTATICMETHOD = 7; public final static int ACCESSCONSTRUCTOR = 8; + public final static int ACCESSDUPPUTFIELD = 9; + public final static int ACCESSDUPPUTSTATIC = 10; int kind = UNKNOWN; @@ -160,8 +163,7 @@ public class SyntheticAnalyzer implements Opcodes { return true; } - private final int modifierMask = (Modifier.PRIVATE | Modifier.PROTECTED | - Modifier.PUBLIC); + private final int modifierMask = Modifier.PUBLIC; /** * Check if this is a field/method access method. We have only @@ -184,6 +186,7 @@ public class SyntheticAnalyzer implements Opcodes { (succBlocks.length == 1 && succBlocks[0] != null)) return false; Iterator iter = Arrays.asList(startBlock.getInstructions()).iterator(); + boolean dupSeen = false; if (!iter.hasNext()) return false; @@ -209,13 +212,18 @@ public class SyntheticAnalyzer implements Opcodes { if (params != 0) return false; Reference ref = instr.getReference(); - String refClazz = ref.getClazz().substring(1); - if (!(refClazz.substring(0, refClazz.length()-1) - .equals(classInfo.getName().replace('.','/')))) + ClassInfo refClazz = TypeSignature + .getClassInfo(classInfo.getClassPath(), ref.getClazz()); + try { + if (!refClazz.superClassOf(classInfo)) + return false; + } catch (IOException ex) { + /* Can't get enough info to ensure that refClazz is correct */ return false; + } FieldInfo refField - = classInfo.findField(ref.getName(), ref.getType()); - if ((refField.getModifiers() & modifierMask) != Modifier.PRIVATE) + = refClazz.findField(ref.getName(), ref.getType()); + if ((refField.getModifiers() & modifierMask) != 0) return false; if (!iter.hasNext()) return false; @@ -228,6 +236,16 @@ public class SyntheticAnalyzer implements Opcodes { kind = (isStatic ? ACCESSGETSTATIC : ACCESSGETFIELD); return true; } + if (instr.getOpcode() == (opc_dup - 3) + 3 * slot) { + /* This is probably a opc_dup or opc_dup2, + * preceding a opc_putfield + */ + instr = (Instruction) iter.next(); + if (instr.getOpcode() != opc_putstatic + && instr.getOpcode() != opc_putfield) + return false; + dupSeen = true; + } if (instr.getOpcode() == opc_putfield || instr.getOpcode() == opc_putstatic) { boolean isStatic = instr.getOpcode() == opc_putstatic; @@ -237,18 +255,30 @@ public class SyntheticAnalyzer implements Opcodes { return false; /* For valid bytecode the type of param matches automatically */ Reference ref = instr.getReference(); - String refClazz = ref.getClazz().substring(1); - if (!(refClazz.substring(0, refClazz.length()-1) - .equals(classInfo.getName().replace('.','/')))) + ClassInfo refClazz = TypeSignature + .getClassInfo(classInfo.getClassPath(), ref.getClazz()); + try { + if (!refClazz.superClassOf(classInfo)) + return false; + } catch (IOException ex) { + /* Can't get enough info to ensure that refClazz is correct */ return false; + } FieldInfo refField - = classInfo.findField(ref.getName(), ref.getType()); - if ((refField.getModifiers() & modifierMask) != Modifier.PRIVATE) - return false; - if (iter.hasNext()) + = refClazz.findField(ref.getName(), ref.getType()); + if ((refField.getModifiers() & modifierMask) != 0) return false; + if (dupSeen) { + if (instr.getOpcode() < opc_ireturn + || instr.getOpcode() > opc_areturn) + return false; + kind = (isStatic ? ACCESSDUPPUTSTATIC : ACCESSDUPPUTFIELD); + } else { + if (iter.hasNext()) + return false; + kind = (isStatic ? ACCESSPUTSTATIC : ACCESSPUTFIELD); + } reference = ref; - kind = (isStatic ? ACCESSPUTSTATIC : ACCESSPUTFIELD); return true; } if (instr.getOpcode() == opc_invokestatic @@ -257,15 +287,20 @@ public class SyntheticAnalyzer implements Opcodes { if (!isStatic) params--; Reference ref = instr.getReference(); - String refClazz = ref.getClazz().substring(1); - if (!(refClazz.substring(0, refClazz.length()-1) - .equals(classInfo.getName().replace('.','/')))) + ClassInfo refClazz = TypeSignature + .getClassInfo(classInfo.getClassPath(), ref.getClazz()); + try { + if (!refClazz.superClassOf(classInfo)) + return false; + } catch (IOException ex) { + /* Can't get enough info to ensure that refClazz is correct */ return false; + } MethodInfo refMethod - = classInfo.findMethod(ref.getName(), ref.getType()); + = refClazz.findMethod(ref.getName(), ref.getType()); MethodType refType = Type.tMethod(classInfo.getClassPath(), ref.getType()); - if ((refMethod.getModifiers() & modifierMask) != Modifier.PRIVATE + if ((refMethod.getModifiers() & modifierMask) != 0 || refType.getParameterTypes().length != params) return false; if (refType.getReturnType() == Type.tVoid) { @@ -336,15 +371,15 @@ public class SyntheticAnalyzer implements Opcodes { } if (params > 0 && instr.getOpcode() == opc_invokespecial) { Reference ref = instr.getReference(); - String refClazz = ref.getClazz().substring(1); - if (!(refClazz.substring(0, refClazz.length()-1) - .equals(classInfo.getName().replace('.','/')))) + ClassInfo refClazz = TypeSignature + .getClassInfo(classInfo.getClassPath(), ref.getClazz()); + if (refClazz != classInfo) return false; MethodInfo refMethod - = classInfo.findMethod(ref.getName(), ref.getType()); + = refClazz.findMethod(ref.getName(), ref.getType()); MethodType refType = Type.tMethod(classInfo.getClassPath(), ref.getType()); - if ((refMethod.getModifiers() & modifierMask) != Modifier.PRIVATE + if ((refMethod.getModifiers() & modifierMask) != 0 || !refMethod.getName().equals("") || unifyParam == -1 || refType.getParameterTypes().length != params - 2) diff --git a/jode/jode/obfuscator/modules/ConstantAnalyzer.java b/jode/jode/obfuscator/modules/ConstantAnalyzer.java index 8ee0444..0b585b9 100644 --- a/jode/jode/obfuscator/modules/ConstantAnalyzer.java +++ b/jode/jode/obfuscator/modules/ConstantAnalyzer.java @@ -1040,9 +1040,9 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { boolean known = value1.value != ConstValue.VOLATILE && value2.value != ConstValue.VOLATILE; if (known) { - if ((opcode == opc_idiv + if (((opcode == opc_idiv || opcode == opc_irem) && ((Integer)value2.value).intValue() == 0) - || (opcode == opc_ldiv + || ((opcode == opc_ldiv || opcode == opc_lrem) && ((Long)value2.value).longValue() == 0)) known = false; } diff --git a/jode/jode/swingui/Main.java b/jode/jode/swingui/Main.java index 5c285cd..08742b6 100644 --- a/jode/jode/swingui/Main.java +++ b/jode/jode/swingui/Main.java @@ -356,26 +356,36 @@ public class Main for (int i=0; i < numUsage ; i++) err.println(bundle.getString("usage."+i)); } - + public static void main(String[] params) { bundle = ResourceBundle.getBundle("jode.swingui.Resources"); String cp = System.getProperty("java.class.path", ""); cp = cp.replace(File.pathSeparatorChar, Decompiler.altPathSeparatorChar); - for (int i=0; i, --classpath search for classes in specified classpath. -usage.3 = \ The directories must be separated by commas. +usage.count=3 +usage.0 = usage: java jode.swingui.Main [CLASSPATH] +usage.1 = The directories in CLASSPATH should be separated by ','. +usage.2 = If no CLASSPATH is given the virtual machine classpath is used. browse.filter.description = *.jar, *.zip Archives browse.title = Browse diff --git a/jode/jode/swingui/Resources_de.properties b/jode/jode/swingui/Resources_de.properties index 95abb71..a2d33b9 100644 --- a/jode/jode/swingui/Resources_de.properties +++ b/jode/jode/swingui/Resources_de.properties @@ -1,8 +1,7 @@ -usage.count=4 -usage.0 = Syntax: java jode.swingui.Main optionen* -usage.1 = \ -h, --help gib diese Hilfe aus -usage.2 = \ -c, --cp , --classpath Suche die Klassen im gegebenen Classpath -usage.3 = \ Die Verzeichnisse werden durch Kommas abgetrennt +usage.count=3 +usage.0 = Syntax: java jode.swingui.Main [CLASSPATH] +usage.1 = Die Verzeichnisse in CLASSPATH werden durch Kommas abgetrennt. +usage.2 = Wird kein CLASSPATH angegeben, so wird Java's standard classpath verwendet. browse.filter.description = *.jar, *.zip Archive browse.title = Durchsuchen diff --git a/jode/jode/type/ArrayType.java b/jode/jode/type/ArrayType.java index 50db1b9..dd5b6df 100644 --- a/jode/jode/type/ArrayType.java +++ b/jode/jode/type/ArrayType.java @@ -64,12 +64,18 @@ public class ArrayType extends ClassType { } public Type getSuperType() { - return tRange(tObject, - (ReferenceType) tArray(elementType.getSuperType())); + if (elementType instanceof IntegerType) + return tRange(tObject, this); + else + return tRange(tObject, + (ReferenceType) tArray(elementType.getSuperType())); } public Type getSubType() { - return tArray(elementType.getSubType()); + if (elementType instanceof IntegerType) + return this; + else + return tArray(elementType.getSubType()); } public Type getHint() { @@ -113,8 +119,9 @@ public class ArrayType extends ClassType { */ public Type getSpecializedType(Type type) { /* + * tArray(x), iface -> tArray(x) iff tArray implements iface * tArray(x), tArray(y) -> tArray(x.intersection(y)) - * tArray(x), other -> tArray(x) iff tArray implements object + * tArray(x), other -> tError */ if (type.getTypeCode() == TC_RANGE) { type = ((RangeType) type).getBottom(); @@ -137,10 +144,10 @@ public class ArrayType extends ClassType { * @return the common super type. */ public Type getGeneralizedType(Type type) { - /* tArray(x), tNull -> tArray(x) - * tArray(x), tClass(y) -> common ifaces of tArray and tClass - * tArray(x), tArray(y) -> tArray(x.intersection(y)) or tObject - * tArray(x), other -> tError + /* tArray(x), tNull -> tArray(x) + * tArray(x), tClass(y) -> common ifaces of tArray and tClass + * tArray(x), tArray(y) -> tArray(x.intersection(y)) or tObject + * tArray(x), other -> tError */ if (type.getTypeCode() == TC_RANGE) { type = ((RangeType) type).getTop(); @@ -150,7 +157,9 @@ public class ArrayType extends ClassType { if (type.getTypeCode() == TC_ARRAY) { Type elType = elementType.intersection (((ArrayType)type).elementType); - return elType != tError ? tArray(elType) : tObject; + if (elType != tError) + return tArray(elType); + return MultiClassType.create(arrayIfaces); } if (!(type instanceof ReferenceType)) return tError; diff --git a/jode/jode/type/ClassType.java b/jode/jode/type/ClassType.java index 7c00aea..08f5c4b 100644 --- a/jode/jode/type/ClassType.java +++ b/jode/jode/type/ClassType.java @@ -271,36 +271,57 @@ public abstract class ClassType extends ReferenceType { private final static Hashtable keywords = new Hashtable(); static { - keywords.put("package", Boolean.TRUE); - keywords.put("import", Boolean.TRUE); + keywords.put("abstract", Boolean.TRUE); + keywords.put("default", Boolean.TRUE); keywords.put("if", Boolean.TRUE); - keywords.put("else", Boolean.TRUE); - keywords.put("for", Boolean.TRUE); - keywords.put("while", Boolean.TRUE); + keywords.put("private", Boolean.TRUE); keywords.put("throw", Boolean.TRUE); - keywords.put("return", Boolean.TRUE); - keywords.put("class", Boolean.TRUE); - keywords.put("interface", Boolean.TRUE); + keywords.put("boolean", Boolean.TRUE); + keywords.put("do", Boolean.TRUE); keywords.put("implements", Boolean.TRUE); - keywords.put("extends", Boolean.TRUE); + keywords.put("protected", Boolean.TRUE); + keywords.put("throws", Boolean.TRUE); + keywords.put("break", Boolean.TRUE); + keywords.put("double", Boolean.TRUE); + keywords.put("import", Boolean.TRUE); + keywords.put("public", Boolean.TRUE); + keywords.put("transient", Boolean.TRUE); + keywords.put("byte", Boolean.TRUE); + keywords.put("else", Boolean.TRUE); keywords.put("instanceof", Boolean.TRUE); - keywords.put("new", Boolean.TRUE); + keywords.put("return", Boolean.TRUE); + keywords.put("try", Boolean.TRUE); + keywords.put("case", Boolean.TRUE); + keywords.put("extends", Boolean.TRUE); keywords.put("int", Boolean.TRUE); - keywords.put("boolean", Boolean.TRUE); - keywords.put("long", Boolean.TRUE); - keywords.put("float", Boolean.TRUE); - keywords.put("double", Boolean.TRUE); keywords.put("short", Boolean.TRUE); - keywords.put("public", Boolean.TRUE); - keywords.put("protected", Boolean.TRUE); - keywords.put("private", Boolean.TRUE); + keywords.put("void", Boolean.TRUE); + keywords.put("catch", Boolean.TRUE); + keywords.put("final", Boolean.TRUE); + keywords.put("interface", Boolean.TRUE); keywords.put("static", Boolean.TRUE); - keywords.put("synchronized", Boolean.TRUE); - keywords.put("strict", Boolean.TRUE); - keywords.put("transient", Boolean.TRUE); - keywords.put("abstract", Boolean.TRUE); keywords.put("volatile", Boolean.TRUE); - keywords.put("final", Boolean.TRUE); + keywords.put("char", Boolean.TRUE); + keywords.put("finally", Boolean.TRUE); + keywords.put("long", Boolean.TRUE); + keywords.put("super", Boolean.TRUE); + keywords.put("while", Boolean.TRUE); + keywords.put("class", Boolean.TRUE); + keywords.put("float", Boolean.TRUE); + keywords.put("native", Boolean.TRUE); + keywords.put("switch", Boolean.TRUE); + keywords.put("const", Boolean.TRUE); + keywords.put("for", Boolean.TRUE); + keywords.put("new", Boolean.TRUE); + keywords.put("synchronized", Boolean.TRUE); + keywords.put("continue", Boolean.TRUE); + keywords.put("goto", Boolean.TRUE); + keywords.put("package", Boolean.TRUE); + keywords.put("this", Boolean.TRUE); + keywords.put("strictfp", Boolean.TRUE); + keywords.put("null", Boolean.TRUE); + keywords.put("true", Boolean.TRUE); + keywords.put("false", Boolean.TRUE); } /** diff --git a/jode/jode/type/RangeType.java b/jode/jode/type/RangeType.java index 8112b73..0a1f27e 100644 --- a/jode/jode/type/RangeType.java +++ b/jode/jode/type/RangeType.java @@ -107,13 +107,18 @@ public class RangeType extends Type { /** * Returns the hint type of this range type set. This returns the * singleton set containing only the first top type, except if it - * is null and there is a unique bottom type, in which case it returns - * the bottom type. + * is null and there is a unique bottom type, in which case it + * returns the bottom type. * @return the hint type. */ public Type getHint() { - return topType == tNull && bottomType.equals(bottomType.getHint()) - ? bottomType.getHint(): topType.getHint(); + Type bottomHint = bottomType.getHint(); + Type topHint = topType.getHint(); + + if (topType == tNull && bottomType.equals(bottomHint)) + return bottomHint; + + return topHint; } /**