simplified parameter handling.

added comments


git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@1158 379699f6-c40d-0410-875b-85095c16579e
branch_1_1
jochen 26 years ago
parent 12d8d27cdd
commit 74001822d7
  1. 374
      jode/jode/decompiler/MethodAnalyzer.java.in

@ -56,39 +56,127 @@ import java.io.IOException;
import @COLLECTIONS@.Map; import @COLLECTIONS@.Map;
import @COLLECTIONS@.Iterator; 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:
* <dl>
* <dt><code>analyze()</code></dt>
* <dd>the main analyzation, decompiles the code of the method</dd>
* <dt><code>analyzeInners()</code></dt>
* <dd>This will analyze method scopes classes by calling their
* <code>analyze()</code> and <code>analyzeInners()</code>
* methods.</dd>
* <dt><code>makeDeclaration()</code></dt>
* <dd>This will determine when to declare variables. For constructors
* it will do special transformations like field initialization.</dd>
*/
public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
/**
* The import handler where we should register our types.
*/
ImportHandler imports; ImportHandler imports;
/**
* The class analyzer of the class that contains this method.
*/
ClassAnalyzer classAnalyzer; ClassAnalyzer classAnalyzer;
/**
* The method info structure for this method.
*/
MethodInfo minfo; 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; String methodName;
/**
* The type of this method (parameter types + return type).
*/
MethodType methodType; MethodType methodType;
/**
* True, iff this method is a constructor, i.e. methodName == <(cl)?init>
*/
boolean isConstructor; boolean isConstructor;
/**
* The exceptions this method may throw.
*/
Type[] exceptions; 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; 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 <code>analyze()</code> phase.
*/
FlowBlock methodHeader; FlowBlock methodHeader;
BytecodeInfo code; /**
* A list of all locals contained in this method.
*/
Vector allLocals = new Vector(); Vector allLocals = new Vector();
/**
* This array contains the locals in the parameter list, including
* the implicit <i>this</i> parameter for nonstatic methods.
*/
LocalInfo[] param; LocalInfo[] param;
/**
* The local variable table containing info about names and types of
* locals.
*/
LocalVariableTable lvt; LocalVariableTable lvt;
/**
* True, iff this method is the special constructor, that is generated
* by jikes (constructor$xx)
*/
boolean isJikesConstructor; boolean isJikesConstructor;
/**
* True, iff this method is the special constructor, and its first
* parameter is a reference to the outer class.
*/
boolean hasJikesOuterValue; boolean hasJikesOuterValue;
/**
* True, iff this method is an implicit constructor, that may be (or even
* must be for anonymous classes) omitted.
*/
boolean isImplicitAnonymousConstructor; boolean isImplicitAnonymousConstructor;
/**
* True, if this method is the special block$ method generated by jikes
* to initialize field members.
*/
boolean isJikesBlockInitializer; boolean isJikesBlockInitializer;
/** /**
* This dictionary maps an anonymous ClassInfo to the * This list contains the InvokeOperator objects in the code of
* InvokeOperator that creates this class. * this method, that create method scoped classes. */
*/
Vector anonConstructors = new Vector(); 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, public MethodAnalyzer(ClassAnalyzer cla, MethodInfo minfo,
ImportHandler imports) { ImportHandler imports) {
this.classAnalyzer = cla; this.classAnalyzer = cla;
@ -119,7 +207,6 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
lvt = new LocalVariableTable(code.getMaxLocals(), lvt = new LocalVariableTable(code.getMaxLocals(),
localvars); localvars);
} }
initParams();
} }
String[] excattr = minfo.getExceptions(); String[] excattr = minfo.getExceptions();
if (excattr == null) { if (excattr == null) {
@ -134,46 +221,66 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
synth = new SyntheticAnalyzer(minfo, true); synth = new SyntheticAnalyzer(minfo, true);
} }
/**
* Returns the name of this method.
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();
}
}
public String getName() { public String getName() {
return methodName; return methodName;
} }
/**
* Returns the type of this method.
* @return the type of this method.
*/
public MethodType getType() { public MethodType getType() {
return methodType; return methodType;
} }
/**
* Returns the first flow block of the code.
* @return the first flow block of the code.
*/
public FlowBlock getMethodHeader() { public FlowBlock getMethodHeader() {
return methodHeader; 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() { public final BytecodeInfo getBytecodeInfo() {
return code; 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() { public final ImportHandler getImportHandler() {
return imports; 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) { public final void useType(Type type) {
imports.useType(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) { public void insertStructuredBlock(StructuredBlock insertBlock) {
if (methodHeader != null) { if (methodHeader != null) {
insertBlock.setJump(new Jump(FlowBlock.NEXT_BY_ADDR)); 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
* "<init>" or "<clinit>".
* @return true, iff this method is a real constructor.
*/
public final boolean isConstructor() { public final boolean isConstructor() {
return isConstructor; return isConstructor;
} }
/**
* Checks if this method is static.
* @return true, iff this method is static.
*/
public final boolean isStatic() { public final boolean isStatic() {
return minfo.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() { public final boolean isSynthetic() {
return minfo.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) { public final void setJikesConstructor(boolean value) {
isJikesConstructor = 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) { public final void setJikesBlockInitializer(boolean value) {
isJikesBlockInitializer = 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) { public final void setHasOuterValue(boolean value) {
hasJikesOuterValue = 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) { public final void setAnonymousConstructor(boolean value) {
isImplicitAnonymousConstructor = 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() { public final boolean isAnonymousConstructor() {
return isImplicitAnonymousConstructor; 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() { public final SyntheticAnalyzer getSynthetic() {
return synth; return synth;
} }
/**
* Get the return type of this method.
*/
public Type getReturnType() { public Type getReturnType() {
return methodType.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) if (GlobalOptions.verboseLevel > 0)
GlobalOptions.err.print(methodName+": "); GlobalOptions.err.print(methodName+": ");
@ -348,23 +545,30 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
GlobalOptions.err.println(""); 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() public void analyze()
throws ClassFormatError throws ClassFormatError
{ {
if (code == null) Type[] paramTypes = getType().getParameterTypes();
return; int paramCount = (isStatic() ? 0 : 1) + paramTypes.length;
param = new LocalInfo[paramCount];
int offset = 0; int offset = 0;
int slot = 0;
if (!isStatic()) { if (!isStatic()) {
ClassInfo classInfo = classAnalyzer.getClazz(); ClassInfo classInfo = classAnalyzer.getClazz();
LocalInfo thisLocal = getParamInfo(0); LocalInfo thisLocal = getLocalInfo(0, slot++);
thisLocal.setExpression(new ThisOperator(classInfo, true)); thisLocal.setExpression(new ThisOperator(classInfo, true));
offset++; param[offset++] = thisLocal;
} }
Type[] paramTypes = methodType.getParameterTypes();
for (int i=0; i< paramTypes.length; i++) { 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++; offset++;
} }
@ -374,33 +578,39 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
if (!isConstructor) if (!isConstructor)
imports.useType(methodType.getReturnType()); imports.useType(methodType.getReturnType());
if (code != null)
analyzeCode(); analyzeCode();
} }
public final LocalInfo getParamInfo(int nr) { /**
return param[nr]; * This is the second pass of the analyzation. It will analyze
} * the method scoped classes.
*/
public void analyzeInnerClasses() public void analyzeInnerClasses()
throws ClassFormatError throws ClassFormatError
{ {
createAnonymousClasses(); 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() { public void makeDeclaration() {
if (isConstructor() && !isStatic() if (isConstructor() && !isStatic()
&& classAnalyzer.outerValues != null && classAnalyzer.outerValues != null
&& (Decompiler.options & Decompiler.OPTION_CONTRAFO) != 0) { && (Decompiler.options & Decompiler.OPTION_CONTRAFO) != 0) {
Expression[] outerValues = classAnalyzer.outerValues; Expression[] outerValues = classAnalyzer.outerValues;
for (int i=0; i< outerValues.length; i++) { for (int i=0; i< outerValues.length; i++) {
LocalInfo local = getParamInfo(1+i); LocalInfo local = param[1+i];
local.setExpression(outerValues[i]); local.setExpression(outerValues[i]);
} }
} }
if (isJikesConstructor && hasJikesOuterValue if (isJikesConstructor && hasJikesOuterValue
&& classAnalyzer.outerValues != null && classAnalyzer.outerValues != null
&& classAnalyzer.outerValues.length > 0) && classAnalyzer.outerValues.length > 0)
getParamInfo(1).setExpression(classAnalyzer.outerValues[0]); param[1].setExpression(classAnalyzer.outerValues[0]);
for (Enumeration enum = allLocals.elements(); for (Enumeration enum = allLocals.elements();
enum.hasMoreElements(); ) { enum.hasMoreElements(); ) {
@ -408,7 +618,6 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
if (!li.isShadow()) if (!li.isShadow())
imports.useType(li.getType()); imports.useType(li.getType());
} }
if (code != null) {
for (int i=0; i < param.length; i++) { for (int i=0; i < param.length; i++) {
param[i].guessName(); param[i].guessName();
for (int j=0; j < i; j++) { for (int j=0; j < i; j++) {
@ -419,6 +628,8 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
} }
} }
} }
if (code != null) {
methodHeader.makeDeclaration(param); methodHeader.makeDeclaration(param);
methodHeader.simplify(); methodHeader.simplify();
} }
@ -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() { public boolean skipWriting() {
if (synth != null) { if (synth != null) {
// We don't need this class anymore (hopefully?) // We don't need this class anymore (hopefully?)
@ -515,6 +731,11 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
return false; 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) public void dumpSource(TabbedPrintWriter writer)
throws IOException throws IOException
{ {
@ -578,31 +799,9 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
writer.print(" " + methodName); writer.print(" " + methodName);
} }
writer.print("("); writer.print("(");
Type[] paramTypes = methodType.getParameterTypes();
int offset = skipParams + (isStatic() ? 0 : 1); int offset = skipParams + (isStatic() ? 0 : 1);
int start = skipParams; for (int i = offset; i < param.length; i++) {
if (i > offset)
LocalInfo[] param = new LocalInfo[paramTypes.length];
for (int i=start; i<paramTypes.length; i++) {
if (code == null) {
param[i] = new LocalInfo(this, offset);
param[i].setType(paramTypes[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 */
}
}
} else {
param[i] = getParamInfo(offset);
offset++;
}
}
for (int i=start; i<paramTypes.length; i++) {
if (i>start)
writer.print(", "); writer.print(", ");
param[i].dumpDeclaration(writer); param[i].dumpDeclaration(writer);
} }
@ -620,10 +819,7 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
if (code != null) { if (code != null) {
writer.openBrace(); writer.openBrace();
writer.tab(); writer.tab();
if (methodHeader != null)
methodHeader.dumpSource(writer); methodHeader.dumpSource(writer);
else
writer.println("COULDN'T DECOMPILE METHOD!");
writer.untab(); writer.untab();
writer.closeBrace(); writer.closeBrace();
} else } else
@ -631,28 +827,10 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
writer.popScope(); 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. * 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) { public LocalInfo findLocal(String name) {
Enumeration enum = allLocals.elements(); 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) { public ClassAnalyzer findAnonClass(String name) {
if (innerAnalyzers != null) { if (innerAnalyzers != null) {
@ -682,6 +863,11 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
return null; 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) { public boolean isScopeOf(Object obj, int scopeType) {
if (scopeType == METHODSCOPE if (scopeType == METHODSCOPE
&& obj instanceof ClassInfo) { && obj instanceof ClassInfo) {
@ -692,6 +878,12 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
return false; 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) { public boolean conflicts(String name, int usageType) {
if (usageType == AMBIGUOUSNAME || usageType == LOCALNAME) if (usageType == AMBIGUOUSNAME || usageType == LOCALNAME)
return findLocal(name) != null; return findLocal(name) != null;
@ -700,11 +892,21 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
return false; return false;
} }
/**
* Gets the parent scope, i.e. the class analyzer for the class containing
* this method.
* @XXX needed?
*/
public ClassDeclarer getParent() { public ClassDeclarer getParent() {
return getClassAnalyzer(); 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) { public void addAnonymousConstructor(InvokeOperator cop) {
anonConstructors.addElement(cop); anonConstructors.addElement(cop);
} }

Loading…
Cancel
Save