|
|
|
@ -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); |
|
|
|
|
} |
|
|
|
|