diff --git a/jode/jode/decompiler/MethodAnalyzer.java.in b/jode/jode/decompiler/MethodAnalyzer.java.in
index 07c7f55..7f9e830 100644
--- a/jode/jode/decompiler/MethodAnalyzer.java.in
+++ b/jode/jode/decompiler/MethodAnalyzer.java.in
@@ -56,39 +56,127 @@ import java.io.IOException;
import @COLLECTIONS@.Map;
import @COLLECTIONS@.Iterator;
+/**
+ * A method analyzer is the main class for analyzation of methods.
+ * There is exactly one MethodAnalyzer object for each method (even
+ * for abstract methods), that should be decompiled.
+ *
+ * Method analyzation is done in three passes:
+ *
+ * analyze()
+ * - the main analyzation, decompiles the code of the method
+ * analyzeInners()
+ * - This will analyze method scopes classes by calling their
+ *
analyze()
and analyzeInners()
+ * methods.
+ * makeDeclaration()
+ * - This will determine when to declare variables. For constructors
+ * it will do special transformations like field initialization.
+ */
public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
+ /**
+ * The import handler where we should register our types.
+ */
ImportHandler imports;
+ /**
+ * The class analyzer of the class that contains this method.
+ */
ClassAnalyzer classAnalyzer;
+ /**
+ * The method info structure for this method.
+ */
MethodInfo minfo;
+ /**
+ * This is the bytecode info structure, or null if this method has
+ * no code (abstract or native).
+ */
+ BytecodeInfo code;
+ /**
+ * The method name.
+ */
String methodName;
+ /**
+ * The type of this method (parameter types + return type).
+ */
MethodType methodType;
+ /**
+ * True, iff this method is a constructor, i.e. methodName == <(cl)?init>
+ */
boolean isConstructor;
+ /**
+ * The exceptions this method may throw.
+ */
Type[] exceptions;
+ /**
+ * If the method is synthetic (access$, class$, etc.), this is the
+ * synthetic analyzer describing the function of this method, otherwise
+ * this is null.
+ */
SyntheticAnalyzer synth;
+ /**
+ * This is the first flow block of the method code. If this
+ * method has no code, this is null. This is initialized at the
+ * end of the analyze()
phase.
+ */
FlowBlock methodHeader;
- BytecodeInfo code;
-
+ /**
+ * A list of all locals contained in this method.
+ */
Vector allLocals = new Vector();
+
+ /**
+ * This array contains the locals in the parameter list, including
+ * the implicit this parameter for nonstatic methods.
+ */
LocalInfo[] param;
+ /**
+ * The local variable table containing info about names and types of
+ * locals.
+ */
LocalVariableTable lvt;
+ /**
+ * True, iff this method is the special constructor, that is generated
+ * by jikes (constructor$xx)
+ */
boolean isJikesConstructor;
+ /**
+ * True, iff this method is the special constructor, and its first
+ * parameter is a reference to the outer class.
+ */
boolean hasJikesOuterValue;
+ /**
+ * True, iff this method is an implicit constructor, that may be (or even
+ * must be for anonymous classes) omitted.
+ */
boolean isImplicitAnonymousConstructor;
+ /**
+ * True, if this method is the special block$ method generated by jikes
+ * to initialize field members.
+ */
boolean isJikesBlockInitializer;
/**
- * This dictionary maps an anonymous ClassInfo to the
- * InvokeOperator that creates this class.
- */
+ * This list contains the InvokeOperator objects in the code of
+ * this method, that create method scoped classes. */
Vector anonConstructors = new Vector();
- Vector innerAnalyzers;
+ /**
+ * This list contains the class analyzers of all method scoped
+ * classes that should be declared in this method.
+ */
+ Vector innerAnalyzers;
+ /**
+ * This is the default constructor.
+ * @param cla the ClassAnalyzer of the class that contains this method.
+ * @param minfo the method info structure for this method.
+ * @param imports the import handler that should be informed about types.
+ */
public MethodAnalyzer(ClassAnalyzer cla, MethodInfo minfo,
ImportHandler imports) {
this.classAnalyzer = cla;
@@ -119,7 +207,6 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
lvt = new LocalVariableTable(code.getMaxLocals(),
localvars);
}
- initParams();
}
String[] excattr = minfo.getExceptions();
if (excattr == null) {
@@ -134,46 +221,66 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
synth = new SyntheticAnalyzer(minfo, true);
}
-
-
- public void initParams() {
- Type[] paramTypes = getType().getParameterTypes();
- int paramCount = (isStatic() ? 0 : 1) + paramTypes.length;
- param = new LocalInfo[paramCount];
- int offset = 0;
- int slot = 0;
- if (!isStatic())
- param[offset++] = getLocalInfo(0, slot++);
- for (int i=0; i < paramTypes.length; i++) {
- param[offset++] = getLocalInfo(0, slot);
- slot += paramTypes[i].stackSize();
- }
- }
-
+ /**
+ * Returns the name of this method.
+ */
public String getName() {
return methodName;
}
+ /**
+ * Returns the type of this method.
+ * @return the type of this method.
+ */
public MethodType getType() {
return methodType;
}
+ /**
+ * Returns the first flow block of the code.
+ * @return the first flow block of the code.
+ */
public FlowBlock getMethodHeader() {
return methodHeader;
}
+ /**
+ * Returns the bytecode info for this method.
+ * @return the bytecode info for this method, or null if it is
+ * abstract or native.
+ */
public final BytecodeInfo getBytecodeInfo() {
return code;
}
+ /**
+ * Returns the import handler. The import handler should be informed
+ * about all types we (or an expression in this method) use, so that
+ * the corresponding class can be imported.
+ * @return the import handler.
+ */
public final ImportHandler getImportHandler() {
return imports;
}
+ /**
+ * Registers a type at the import handler. This should be called
+ * if an expression needs to print the type name to the code. The
+ * corresponding class will be imported in that case (if used
+ * often enough).
+ * @param type the type that should be registered.
+ */
public final void useType(Type type) {
imports.useType(type);
}
+ /**
+ * Inserts a structured block to the beginning of the method.
+ * This is called by transform constructors, to move the super
+ * call from the real constructor to the constructor$xx method
+ * (the jikes constructor).
+ * @param insertBlock the structured block that should be inserted.
+ */
public void insertStructuredBlock(StructuredBlock insertBlock) {
if (methodHeader != null) {
insertBlock.setJump(new Jump(FlowBlock.NEXT_BY_ADDR));
@@ -187,48 +294,138 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
}
}
+ /**
+ * Checks if this method is a constructor, i.e. getName() returns
+ * "" or "".
+ * @return true, iff this method is a real constructor.
+ */
public final boolean isConstructor() {
return isConstructor;
}
+ /**
+ * Checks if this method is static.
+ * @return true, iff this method is static.
+ */
public final boolean isStatic() {
return minfo.isStatic();
}
+ /**
+ * Checks if this method is synthetic, i.e. a synthetic attribute is
+ * present.
+ * @return true, iff this method is synthetic.
+ */
public final boolean isSynthetic() {
return minfo.isSynthetic();
}
+ /**
+ * Tells if this method is the constructor$xx method generated by jikes.
+ * @param value true, iff this method is the jikes constructor.
+ */
public final void setJikesConstructor(boolean value) {
isJikesConstructor = value;
}
+ /**
+ * Tells if this method is the block$xx method generated by jikes.
+ * @param value true, iff this method is the jikes block initializer.
+ */
public final void setJikesBlockInitializer(boolean value) {
isJikesBlockInitializer = value;
}
+ /**
+ * Tells if this (constructor$xx) method has as first (implicit)
+ * parameter the instance of the outer class.
+ * @param value true, this method has the implicit parameter.
+ */
public final void setHasOuterValue(boolean value) {
hasJikesOuterValue = value;
}
+ /**
+ * Tells if this constructor can be omited, since it is implicit.
+ * @param value true, this method is the implicit constructor.
+ */
public final void setAnonymousConstructor(boolean value) {
isImplicitAnonymousConstructor = value;
}
+ /**
+ * Checks if this constructor can be omited, since it is implicit.
+ * @return true, this method is the implicit constructor.
+ */
public final boolean isAnonymousConstructor() {
return isImplicitAnonymousConstructor;
}
+ /**
+ * Get the synthetic analyzer for this method.
+ * @return the synthetic analyzer, or null if this method isn't
+ * synthetic.
+ */
public final SyntheticAnalyzer getSynthetic() {
return synth;
}
+ /**
+ * Get the return type of this method.
+ */
public Type getReturnType() {
return methodType.getReturnType();
}
+ /**
+ * Get the class analyzer for the class containing this method.
+ */
+ public ClassAnalyzer getClassAnalyzer() {
+ return classAnalyzer;
+ }
+
+ /**
+ * Get the class info for the class containing this method.
+ */
+ public ClassInfo getClazz() {
+ return classAnalyzer.clazz;
+ }
+
+ /**
+ * Get the local info for a parameter.
+ * @param nr the index of the parameter (start by zero and
+ * count the implicit this param for nonstatic method).
+ * @return the local info for the specified parameter.
+ * @see #getLocalInfo
+ */
+ public final LocalInfo getParamInfo(int nr) {
+ return param[nr];
+ }
+
+ /**
+ * Create a local info for a local variable located at an
+ * instruction with the given address.
+ * @param addr the address of the instruction using this local.
+ * the address of the next instruction for stores.
+ * @param slot the slot, the local variable uses.
+ * @return a new local info representing that local.
+ */
+ public LocalInfo getLocalInfo(int addr, int slot) {
+ LocalInfo li = new LocalInfo(this, slot);
+ if (lvt != null) {
+ LocalVarEntry entry = lvt.getLocal(slot, addr);
+ if (entry != null)
+ li.addHint(entry.getName(), entry.getType());
+ }
+ allLocals.addElement(li);
+ return li;
+ }
- public void analyzeCode()
+ /**
+ * Analyzes the code of this method. This creates the
+ * flow blocks (including methodHeader) and analyzes them.
+ */
+ private void analyzeCode()
{
if (GlobalOptions.verboseLevel > 0)
GlobalOptions.err.print(methodName+": ");
@@ -348,23 +545,30 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
GlobalOptions.err.println("");
}
+ /**
+ * This is the first pass of the analyzation. It will analyze the
+ * code of this method, but not the method scoped classes.
+ */
public void analyze()
throws ClassFormatError
{
- if (code == null)
- return;
+ Type[] paramTypes = getType().getParameterTypes();
+ int paramCount = (isStatic() ? 0 : 1) + paramTypes.length;
+ param = new LocalInfo[paramCount];
int offset = 0;
+ int slot = 0;
if (!isStatic()) {
ClassInfo classInfo = classAnalyzer.getClazz();
- LocalInfo thisLocal = getParamInfo(0);
+ LocalInfo thisLocal = getLocalInfo(0, slot++);
thisLocal.setExpression(new ThisOperator(classInfo, true));
- offset++;
+ param[offset++] = thisLocal;
}
- Type[] paramTypes = methodType.getParameterTypes();
for (int i=0; i< paramTypes.length; i++) {
- getParamInfo(offset).setType(paramTypes[i]);
+ param[offset] = getLocalInfo(0, slot);
+ param[offset].setType(paramTypes[i]);
+ slot += paramTypes[i].stackSize();
offset++;
}
@@ -374,33 +578,39 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
if (!isConstructor)
imports.useType(methodType.getReturnType());
- analyzeCode();
- }
-
- public final LocalInfo getParamInfo(int nr) {
- return param[nr];
+ if (code != null)
+ analyzeCode();
}
+ /**
+ * This is the second pass of the analyzation. It will analyze
+ * the method scoped classes.
+ */
public void analyzeInnerClasses()
throws ClassFormatError
{
createAnonymousClasses();
}
+ /**
+ * This is the third and last pass of the analyzation. It will analyze
+ * the types and names of the local variables and where to declare them.
+ * It will also determine where to declare method scoped local variables.
+ */
public void makeDeclaration() {
if (isConstructor() && !isStatic()
&& classAnalyzer.outerValues != null
&& (Decompiler.options & Decompiler.OPTION_CONTRAFO) != 0) {
Expression[] outerValues = classAnalyzer.outerValues;
for (int i=0; i< outerValues.length; i++) {
- LocalInfo local = getParamInfo(1+i);
+ LocalInfo local = param[1+i];
local.setExpression(outerValues[i]);
}
}
if (isJikesConstructor && hasJikesOuterValue
&& classAnalyzer.outerValues != null
&& classAnalyzer.outerValues.length > 0)
- getParamInfo(1).setExpression(classAnalyzer.outerValues[0]);
+ param[1].setExpression(classAnalyzer.outerValues[0]);
for (Enumeration enum = allLocals.elements();
enum.hasMoreElements(); ) {
@@ -408,17 +618,18 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
if (!li.isShadow())
imports.useType(li.getType());
}
- if (code != null) {
- for (int i=0; i < param.length; i++) {
- param[i].guessName();
- for (int j=0; j < i; j++) {
- if (param[j].getName().equals(param[i].getName())) {
- /* A name conflict happened. */
- param[i].makeNameUnique();
- break; /* j */
- }
+ for (int i=0; i < param.length; i++) {
+ param[i].guessName();
+ for (int j=0; j < i; j++) {
+ if (param[j].getName().equals(param[i].getName())) {
+ /* A name conflict happened. */
+ param[i].makeNameUnique();
+ break; /* j */
}
}
+ }
+
+ if (code != null) {
methodHeader.makeDeclaration(param);
methodHeader.simplify();
}
@@ -429,7 +640,7 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
ClassAnalyzer classAna = (ClassAnalyzer) enum.nextElement();
if (classAna.getParent() == this) {
Expression[] outerValues = classAna.getOuterValues();
- for (int i=0; i< outerValues.length; i++) {
+ for (int i=0; i < outerValues.length; i++) {
if (outerValues[i] instanceof OuterLocalOperator) {
LocalInfo li = ((OuterLocalOperator)
outerValues[i]).getLocalInfo();
@@ -443,6 +654,11 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
}
}
+ /**
+ * Tells if this method is synthetic or implicit or something else, so
+ * that it doesn't have to be written to the source code.
+ * @return true, iff it shouldn't be written to the source code.
+ */
public boolean skipWriting() {
if (synth != null) {
// We don't need this class anymore (hopefully?)
@@ -515,6 +731,11 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
return false;
}
+ /**
+ * Dumps the source code for this method to the specified writer.
+ * @param writer the tabbed print writer the code should be written to.
+ * @exception IOException, if writer throws an exception.
+ */
public void dumpSource(TabbedPrintWriter writer)
throws IOException
{
@@ -578,31 +799,9 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
writer.print(" " + methodName);
}
writer.print("(");
- Type[] paramTypes = methodType.getParameterTypes();
int offset = skipParams + (isStatic() ? 0 : 1);
- int start = skipParams;
-
- LocalInfo[] param = new LocalInfo[paramTypes.length];
- for (int i=start; istart)
+ for (int i = offset; i < param.length; i++) {
+ if (i > offset)
writer.print(", ");
param[i].dumpDeclaration(writer);
}
@@ -620,10 +819,7 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
if (code != null) {
writer.openBrace();
writer.tab();
- if (methodHeader != null)
- methodHeader.dumpSource(writer);
- else
- writer.println("COULDN'T DECOMPILE METHOD!");
+ methodHeader.dumpSource(writer);
writer.untab();
writer.closeBrace();
} else
@@ -631,28 +827,10 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
writer.popScope();
}
- public LocalInfo getLocalInfo(int addr, int slot) {
- LocalInfo li = new LocalInfo(this, slot);
- if (lvt != null) {
- LocalVarEntry entry = lvt.getLocal(slot, addr);
- if (entry != null)
- li.addHint(entry.getName(), entry.getType());
- }
- allLocals.addElement(li);
- return li;
- }
-
- public ClassAnalyzer getClassAnalyzer() {
- return classAnalyzer;
- }
-
- public ClassInfo getClazz() {
- return classAnalyzer.clazz;
- }
-
-
/**
* Checks if the variable set contains a local with the given name.
+ * @return the local info the has the given name, or null if it doesn't
+ * exists.
*/
public LocalInfo findLocal(String name) {
Enumeration enum = allLocals.elements();
@@ -665,7 +843,10 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
}
/**
- * Checks if an anonymous class with the given name exists.
+ * Checks if a method scoped class with the given name exists in this
+ * method (not in a parent method).
+ * @return the class analyzer with the given name, or null if it
+ * doesn' exists.
*/
public ClassAnalyzer findAnonClass(String name) {
if (innerAnalyzers != null) {
@@ -682,6 +863,11 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
return null;
}
+ /**
+ * Checks if the specified object lies in this scope.
+ * @param obj the object.
+ * @param scopeType the type of this object.
+ */
public boolean isScopeOf(Object obj, int scopeType) {
if (scopeType == METHODSCOPE
&& obj instanceof ClassInfo) {
@@ -692,6 +878,12 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
return false;
}
+ /**
+ * Checks if the specified name conflicts with an object in this scope.
+ * @param name the name to check.
+ * @param scopeType the usage type of this name, AMBIGUOUSNAME if it is
+ * ambiguous.
+ */
public boolean conflicts(String name, int usageType) {
if (usageType == AMBIGUOUSNAME || usageType == LOCALNAME)
return findLocal(name) != null;
@@ -700,11 +892,21 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
return false;
}
+ /**
+ * Gets the parent scope, i.e. the class analyzer for the class containing
+ * this method.
+ * @XXX needed?
+ */
public ClassDeclarer getParent() {
return getClassAnalyzer();
}
-
+ /**
+ * Registers an anonymous constructor invokation. This should be called
+ * in the analyze or analyzeInner pass by invoke subexpressions.
+ * @param cop the constructor invokation, that creates the method scoped
+ * class.
+ */
public void addAnonymousConstructor(InvokeOperator cop) {
anonConstructors.addElement(cop);
}