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 25 years ago
parent 12d8d27cdd
commit 74001822d7
  1. 396
      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:
* <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 {
/**
* 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 <code>analyze()</code> 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 <i>this</i> 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
* "<init>" or "<clinit>".
* @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; 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)
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);
}

Loading…
Cancel
Save