CodeAnalyzer merged into MethodAnalyzer

outerValues completely reworked
more passes: analyze, analyzeInnerClasses, makeDeclaration


git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@907 379699f6-c40d-0410-875b-85095c16579e
stable
jochen 26 years ago
parent 5e24bfce86
commit 13d300eb66
  1. 732
      jode/jode/decompiler/MethodAnalyzer.java

@ -18,18 +18,36 @@
*/ */
package jode.decompiler; package jode.decompiler;
import jode.bytecode.ClassInfo; import jode.AssertError;
import jode.bytecode.MethodInfo; import jode.Decompiler;
import jode.GlobalOptions;
import jode.bytecode.*;
import jode.jvm.SyntheticAnalyzer; import jode.jvm.SyntheticAnalyzer;
import jode.type.*; import jode.type.*;
import jode.expr.Expression; import jode.expr.Expression;
import jode.expr.ConstOperator;
import jode.expr.CheckNullOperator;
import jode.expr.ThisOperator; import jode.expr.ThisOperator;
import jode.AssertError; import jode.expr.LocalLoadOperator;
import jode.Decompiler; import jode.expr.OuterLocalOperator;
import jode.GlobalOptions; import jode.expr.ConstructorOperator;
import jode.flow.StructuredBlock;
import jode.flow.FlowBlock;
import jode.flow.TransformExceptionHandlers;
import jode.flow.Jump;
import jode.jvm.CodeVerifier;
import jode.jvm.VerifyException;
import jode.util.SimpleDictionary;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.io.*; import java.util.BitSet;
import java.util.Stack;
import java.util.Vector;
import java.util.Dictionary;
import java.util.Enumeration;
import java.io.DataInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
ImportHandler imports; ImportHandler imports;
@ -40,14 +58,34 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
MethodType methodType; MethodType methodType;
boolean isConstructor; boolean isConstructor;
CodeAnalyzer code = null;
Type[] exceptions; Type[] exceptions;
SyntheticAnalyzer synth; SyntheticAnalyzer synth;
FlowBlock methodHeader;
BytecodeInfo code;
Vector allLocals = new Vector();
LocalInfo[] param;
LocalVariableTable lvt;
/**
* This is a block that will be inserted at the beginning of the
* method, when the code is analyzed.
*/
StructuredBlock insertBlock = null;
boolean isJikesConstructor; boolean isJikesConstructor;
boolean isImplicitAnonymousConstructor; boolean isImplicitAnonymousConstructor;
/**
* This dictionary maps an anonymous ClassInfo to the
* ConstructorOperator that creates this class.
*/
Vector anonConstructors = new Vector();
Vector innerAnalyzers;
public MethodAnalyzer(ClassAnalyzer cla, MethodInfo minfo, public MethodAnalyzer(ClassAnalyzer cla, MethodInfo minfo,
ImportHandler imports) { ImportHandler imports) {
this.classAnalyzer = cla; this.classAnalyzer = cla;
@ -58,8 +96,28 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
this.isConstructor = this.isConstructor =
methodName.equals("<init>") || methodName.equals("<clinit>"); methodName.equals("<init>") || methodName.equals("<clinit>");
if (minfo.getBytecode() != null) if (minfo.getBytecode() != null) {
code = new CodeAnalyzer(this, minfo, imports); code = minfo.getBytecode();
if ((Decompiler.options & Decompiler.OPTION_VERIFY) != 0) {
CodeVerifier verifier
= new CodeVerifier(getClazz(), minfo, code);
try {
verifier.verify();
} catch (VerifyException ex) {
ex.printStackTrace(GlobalOptions.err);
throw new jode.AssertError("Verification error");
}
}
if ((Decompiler.options & Decompiler.OPTION_LVT) != 0) {
LocalVariableInfo[] localvars = code.getLocalVariableTable();
if (localvars != null)
lvt = new LocalVariableTable(code.getMaxLocals(),
localvars);
}
initParams();
}
String[] excattr = minfo.getExceptions(); String[] excattr = minfo.getExceptions();
if (excattr == null) { if (excattr == null) {
exceptions = new Type[0]; exceptions = new Type[0];
@ -73,6 +131,22 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
synth = new SyntheticAnalyzer(minfo, true); 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();
}
}
public String getName() { public String getName() {
return methodName; return methodName;
} }
@ -81,14 +155,28 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
return methodType; return methodType;
} }
public jode.flow.FlowBlock getMethodHeader() { public FlowBlock getMethodHeader() {
return code != null ? code.getMethodHeader() : null; return methodHeader;
} }
public CodeAnalyzer getCode() { public final BytecodeInfo getBytecodeInfo() {
return code; return code;
} }
public final ImportHandler getImportHandler() {
return imports;
}
public final void useType(Type type) {
imports.useType(type);
}
public void insertStructuredBlock(StructuredBlock superBlock) {
if (insertBlock != null)
throw new jode.AssertError();
insertBlock = superBlock;
}
public final boolean isConstructor() { public final boolean isConstructor() {
return isConstructor; return isConstructor;
} }
@ -121,6 +209,134 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
return methodType.getReturnType(); return methodType.getReturnType();
} }
public void analyzeCode()
{
if (GlobalOptions.verboseLevel > 0)
GlobalOptions.err.print(methodName+": ");
/* The adjacent analyzation relies on this */
DeadCodeAnalysis.removeDeadCode(code);
Handler[] handlers = code.getExceptionHandlers();
int returnCount;
TransformExceptionHandlers excHandlers;
{
/* First create a FlowBlock for every block that has a
* predecessor other than the previous instruction.
*/
for (Instruction instr = code.getFirstInstr();
instr != null; instr = instr.nextByAddr) {
if (instr.prevByAddr == null
|| instr.prevByAddr.alwaysJumps
|| instr.preds != null)
instr.tmpInfo = new FlowBlock
(this, instr.addr, instr.length);
}
for (int i=0; i < handlers.length; i++) {
Instruction instr = handlers[i].start;
if (instr.tmpInfo == null)
instr.tmpInfo
= new FlowBlock(this, instr.addr, instr.length);
instr = handlers[i].catcher;
if (instr.tmpInfo == null)
instr.tmpInfo
= new FlowBlock(this, instr.addr, instr.length);
}
/* While we read the opcodes into FlowBlocks
* we try to combine sequential blocks, as soon as we
* find two sequential instructions in a row, where the
* second has no predecessors.
*/
int mark = 1000;
FlowBlock lastBlock = null;
boolean lastSequential = false;
for (Instruction instr = code.getFirstInstr();
instr != null; instr = instr.nextByAddr) {
jode.flow.StructuredBlock block
= Opcodes.readOpcode(instr, this);
if (GlobalOptions.verboseLevel > 0 && instr.addr > mark) {
GlobalOptions.err.print('.');
mark += 1000;
}
if (lastSequential && instr.tmpInfo == null
/* Only merge with previous block, if this is sequential,
* too.
* Why? doSequentialT2 does only handle sequential blocks.
*/
&& !instr.alwaysJumps && instr.succs == null) {
lastBlock.doSequentialT2(block, instr.length);
} else {
if (instr.tmpInfo == null)
instr.tmpInfo = new FlowBlock
(this, instr.addr, instr.length);
FlowBlock flowBlock = (FlowBlock) instr.tmpInfo;
flowBlock.setBlock(block);
if (lastBlock != null)
lastBlock.setNextByAddr(flowBlock);
instr.tmpInfo = lastBlock = flowBlock;
lastSequential = !instr.alwaysJumps && instr.succs == null;
}
}
methodHeader = (FlowBlock) code.getFirstInstr().tmpInfo;
if (insertBlock != null) {
insertBlock.setJump(new Jump(methodHeader));
FlowBlock insertFlowBlock = new FlowBlock(this, 0, 0);
insertFlowBlock.setBlock(insertBlock);
insertFlowBlock.setNextByAddr(methodHeader);
methodHeader = insertFlowBlock;
}
excHandlers = new TransformExceptionHandlers();
for (int i=0; i<handlers.length; i++) {
Type type = null;
FlowBlock start
= (FlowBlock) handlers[i].start.tmpInfo;
int endAddr = handlers[i].end.nextByAddr.addr;
FlowBlock handler
= (FlowBlock) handlers[i].catcher.tmpInfo;
if (handlers[i].type != null)
type = Type.tClass(handlers[i].type);
excHandlers.addHandler(start, endAddr, handler, type);
}
}
for (Instruction instr = code.getFirstInstr();
instr != null; instr = instr.nextByAddr)
instr.tmpInfo = null;
if (GlobalOptions.verboseLevel > 0)
GlobalOptions.err.print('-');
// try {
// TabbedPrintWriter writer = new TabbedPrintWriter(System.err);
// methodHeader.dumpSource(writer);
// } catch (java.io.IOException ex) {
// }
excHandlers.analyze();
methodHeader.analyze();
if ((Decompiler.options & Decompiler.OPTION_PUSH) == 0
&& methodHeader.mapStackToLocal())
methodHeader.removePush();
if ((Decompiler.options & Decompiler.OPTION_ONETIME) != 0)
methodHeader.removeOnetimeLocals();
methodHeader.mergeParams(param);
if (GlobalOptions.verboseLevel > 0)
GlobalOptions.err.println("");
}
public void analyze() public void analyze()
throws ClassFormatError throws ClassFormatError
{ {
@ -130,14 +346,14 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
int offset = 0; int offset = 0;
if (!isStatic()) { if (!isStatic()) {
ClassInfo classInfo = classAnalyzer.getClazz(); ClassInfo classInfo = classAnalyzer.getClazz();
LocalInfo thisLocal = code.getParamInfo(0); LocalInfo thisLocal = getParamInfo(0);
thisLocal.setExpression(new ThisOperator(classInfo, true)); thisLocal.setExpression(new ThisOperator(classInfo, true));
offset++; offset++;
} }
Type[] paramTypes = methodType.getParameterTypes(); Type[] paramTypes = methodType.getParameterTypes();
for (int i=0; i< paramTypes.length; i++) { for (int i=0; i< paramTypes.length; i++) {
code.getParamInfo(offset).setType(paramTypes[i]); getParamInfo(offset).setType(paramTypes[i]);
offset++; offset++;
} }
@ -147,28 +363,18 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
if (!isConstructor) if (!isConstructor)
imports.useType(methodType.getReturnType()); imports.useType(methodType.getReturnType());
if ((Decompiler.options & Decompiler.OPTION_IMMEDIATE) == 0) { if ((Decompiler.options & Decompiler.OPTION_IMMEDIATE) == 0)
if (GlobalOptions.verboseLevel > 0) analyzeCode();
GlobalOptions.err.print(methodName+": ");
code.analyze();
if (GlobalOptions.verboseLevel > 0)
GlobalOptions.err.println("");
}
} }
public LocalInfo getParamInfo(int i) { public final LocalInfo getParamInfo(int nr) {
if (code == null) return param[nr];
return null;
return code.getParamInfo(i);
} }
public void analyzeAnonymousClasses() public void analyzeInnerClasses()
throws ClassFormatError throws ClassFormatError
{ {
if (code == null) createAnonymousClasses();
return;
code.analyzeAnonymousClasses();
} }
public void makeDeclaration() { public void makeDeclaration() {
@ -176,13 +382,48 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
&& classAnalyzer.outerValues != null) { && classAnalyzer.outerValues != null) {
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 = code.getParamInfo(1+i); LocalInfo local = getParamInfo(1+i);
local.setExpression(outerValues[i]); local.setExpression(outerValues[i]);
} }
} }
if (code != null) for (Enumeration enum = allLocals.elements();
code.makeDeclaration(); enum.hasMoreElements(); ) {
LocalInfo li = (LocalInfo)enum.nextElement();
if (!li.isShadow())
imports.useType(li.getType());
}
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 */
}
}
}
methodHeader.makeDeclaration(param);
methodHeader.simplify();
if (innerAnalyzers != null) {
for (Enumeration enum = innerAnalyzers.elements();
enum.hasMoreElements(); ) {
ClassAnalyzer classAna = (ClassAnalyzer) enum.nextElement();
if (classAna.getParent() == this) {
Expression[] outerValues = classAna.getOuterValues();
for (int i=0; i< outerValues.length; i++) {
if (outerValues[i] instanceof OuterLocalOperator) {
LocalInfo li = ((OuterLocalOperator)
outerValues[i]).getLocalInfo();
if (li.getMethodAnalyzer() == this)
li.markFinal();
}
}
classAna.makeDeclaration();
}
}
}
} }
public boolean skipWriting() { public boolean skipWriting() {
@ -255,11 +496,7 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
// We do the code.analyze() here, to get // We do the code.analyze() here, to get
// immediate output. // immediate output.
if (GlobalOptions.verboseLevel > 0) analyzeCode();
GlobalOptions.err.print(methodName+": ");
code.analyze();
if (GlobalOptions.verboseLevel > 0)
GlobalOptions.err.println("");
} }
if (isConstructor() && isStatic() if (isConstructor() && isStatic()
@ -271,7 +508,12 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
writer.println(" * @deprecated"); writer.println(" * @deprecated");
writer.println(" */"); writer.println(" */");
} }
if (minfo.isSynthetic())
writer.pushScope(this);
if (minfo.isSynthetic()
&& (classAnalyzer.getName() != null
|| !isConstructor()))
writer.print("/*synthetic*/ "); writer.print("/*synthetic*/ ");
int modifiedModifiers = minfo.getModifiers(); int modifiedModifiers = minfo.getModifiers();
/* /*
@ -295,9 +537,9 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
if (isConstructor if (isConstructor
&& (isStatic() && (isStatic()
|| (classAnalyzer.getName() == null || (classAnalyzer.getName() == null
&& skipParams == methodType.getParameterTypes().length))) && skipParams == methodType.getParameterTypes().length))) {
writer.print(""); /* static block or unnamed constructor */ /* static block or unnamed constructor */
else { } else {
if (declareAsConstructor) if (declareAsConstructor)
writer.print(classAnalyzer.getName()); writer.print(classAnalyzer.getName());
else { else {
@ -312,7 +554,7 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
LocalInfo[] param = new LocalInfo[paramTypes.length]; LocalInfo[] param = new LocalInfo[paramTypes.length];
for (int i=start; i<paramTypes.length; i++) { for (int i=start; i<paramTypes.length; i++) {
if (code == null) { if (code == null) {
param[i] = new LocalInfo(offset); param[i] = new LocalInfo(this, offset);
param[i].setType(paramTypes[i]); param[i].setType(paramTypes[i]);
param[i].guessName(); param[i].guessName();
for (int j=0; j < i; j++) { for (int j=0; j < i; j++) {
@ -323,7 +565,7 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
} }
} }
} else { } else {
param[i] = code.getParamInfo(offset); param[i] = getParamInfo(offset);
offset++; offset++;
} }
} }
@ -347,14 +589,414 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer {
if (code != null) { if (code != null) {
writer.openBrace(); writer.openBrace();
writer.tab(); writer.tab();
code.dumpSource(writer); if (methodHeader != null)
methodHeader.dumpSource(writer);
else
writer.println("COULDN'T DECOMPILE METHOD!");
writer.untab(); writer.untab();
writer.closeBrace(); writer.closeBrace();
} else } else
writer.println(";"); writer.println(";");
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() { public ClassAnalyzer getClassAnalyzer() {
return classAnalyzer; return classAnalyzer;
} }
public ClassInfo getClazz() {
return classAnalyzer.clazz;
}
/**
* Checks if the variable set contains a local with the given name.
*/
public LocalInfo findLocal(String name) {
Enumeration enum = allLocals.elements();
while (enum.hasMoreElements()) {
LocalInfo li = (LocalInfo) enum.nextElement();
if (li.getName().equals(name))
return li;
}
return null;
}
/**
* Checks if an anonymous class with the given name exists.
*/
public ClassAnalyzer findAnonClass(String name) {
if (innerAnalyzers != null) {
Enumeration enum = innerAnalyzers.elements();
while (enum.hasMoreElements()) {
ClassAnalyzer classAna = (ClassAnalyzer) enum.nextElement();
if (classAna.getParent() == this
&& classAna.getName() != null
&& classAna.getName().equals(name)) {
return classAna;
}
}
}
return null;
}
public boolean isScopeOf(Object obj, int scopeType) {
if (scopeType == METHODSCOPE
&& obj instanceof ClassInfo) {
ClassAnalyzer ana = getClassAnalyzer((ClassInfo)obj);
if (ana != null)
return ana.getParent() == this;
}
return false;
}
public boolean conflicts(String name, int usageType) {
if (usageType == AMBIGUOUSNAME || usageType == LOCALNAME)
return findLocal(name) != null;
if (usageType == AMBIGUOUSNAME || usageType == CLASSNAME)
return findAnonClass(name) != null;
return false;
}
public ClassDeclarer getParent() {
return getClassAnalyzer();
}
public void addAnonymousConstructor(ConstructorOperator cop) {
anonConstructors.addElement(cop);
}
private boolean unifyOuterValues(Expression ov1, Expression ov2,
final ClassAnalyzer clazzAna,
final int shrinkTo) {
/* Wow, unifying outer values of different constructors in
* different methods of different classes can get complicated.
* We have not committed the number of OuterValues. So we
* can't say for sure, if the local load matches an outer
* local if this is a constructor. Even worse: The previous
* outerValues may be a load of a constructor local, that
* should be used as outer value...
*
* We look if there is a way to merge them and register an
* outer value listener to lots of classes.
*/
LocalInfo li1 = null;
MethodAnalyzer method1 = null;
if (ov2 instanceof ThisOperator) {
if (ov1 instanceof ThisOperator)
return ov1.equals(ov2);
Expression temp = ov2;
ov2 = ov1;
ov1 = temp;
} else {
if (ov1 instanceof LocalLoadOperator)
li1 = ((LocalLoadOperator) ov1).getLocalInfo();
else if (ov1 instanceof OuterLocalOperator)
li1 = ((OuterLocalOperator) ov1).getLocalInfo();
else if (!(ov1 instanceof ThisOperator))
return false;
}
LocalInfo li2;
if (ov2 instanceof LocalLoadOperator)
li2 = ((LocalLoadOperator) ov2).getLocalInfo();
else if (ov2 instanceof OuterLocalOperator)
li2 = ((OuterLocalOperator) ov2).getLocalInfo();
else
return false;
MethodAnalyzer method2 = li2.getMethodAnalyzer();
/* Now: li2 != null, method2 != null
* (li1 == null and method1 == null) iff ov1 is ThisOperator
*/
class ShrinkOnShrink implements OuterValueListener {
Dictionary limits = new SimpleDictionary();
public void setLimit(ClassAnalyzer other,
int newLimit) {
limits.put(other, new Integer(newLimit));
other.addOuterValueListener(this);
}
public void done() {
shrinkTo = -1;
}
public void shrinkingOuterValues
(ClassAnalyzer other, int newCount) {
if (shrinkTo != -1) {
int limit = ((Integer) limits.get(other)
).intValue();
if (newCount <= limit) {
clazzAna.shrinkOuterValues(shrinkTo);
done();
}
}
}
}
ShrinkOnShrink sos = new ShrinkOnShrink();
if (li1 != null) {
method1 = li1.getMethodAnalyzer();
System.err.println("unifyLocalInfos: "+method1+"."+li1
+" and "+method2+"."+li2);
while (!method2.isParent(method1)) {
if (!method1.isConstructor() || method1.isStatic()) {
sos.done();
return false;
}
ClassAnalyzer ca1 = method2.classAnalyzer;
int slot = li1.getSlot();
Expression[] ov = ca1.getOuterValues();
if (ov == null) {
sos.done();
return false;
}
int param = 0;
while (param < ov.length && slot > 0)
slot -= ov[param++].getType().stackSize();
if (slot != 0) {
sos.done();
return false;
}
ov1 = ov[param];
if (ov1 instanceof ThisOperator) {
li1 = null;
method1 = null;
break;
}
sos.setLimit(ca1, param);
li1 = ((OuterLocalOperator) ov1).getLocalInfo();
method1 = li1.getMethodAnalyzer();
System.err.println("unifyLocalInfos: "+method1+"."+li1
+" and "+method2+"."+li2);
}
}
/* Now: ov1 is ThisOperator and method1 == null
* or (ov1 is LocalExpression, li1 is LocalInfo,
* method1 is parent of method2).
*/
System.err.println(method1+" is parent of "+method2);
while (method1 != method2) {
if (!method2.isConstructor() || method2.isStatic()) {
sos.done();
return false;
}
ClassAnalyzer ca2 = method2.classAnalyzer;
int slot = li2.getSlot();
Expression[] ov = ca2.getOuterValues();
if (ov == null) {
sos.done();
return false;
}
slot--;
int param = 0;
while (param < ov.length && slot > 0)
slot -= ov[param++].getType().stackSize();
if (slot != 0) {
System.err.println("slot: "+slot+"; param: "+param+"; "+ov[param]);
sos.done();
return false;
}
ov2 = ov[param];
if (ov2 instanceof ThisOperator) {
if (ov1.equals(ov2))
return true;
else {
sos.done();
return false;
}
}
sos.setLimit(ca2, param);
li2 = ((OuterLocalOperator) ov2).getLocalInfo();
method2 = li2.getMethodAnalyzer();
System.err.println("unifyLocalInfos: "+method1+"."+li1
+" and "+method2+"."+li2);
}
if (!li1.equals(li2)) {
sos.done();
return false;
}
return true;
}
public void analyzeConstructorOperator(ConstructorOperator cop) {
ClassInfo clazz = (ClassInfo) cop.getClassInfo();
ClassAnalyzer anonAnalyzer = getParent().getClassAnalyzer(clazz);
Expression[] outerValues;
if (anonAnalyzer == null) {
/* Create a new outerValues array corresponding to the
* first constructor invocation.
*/
if (GlobalOptions.verboseLevel > 0)
GlobalOptions.err.println("Analyzing method scope class: "
+clazz);
Expression[] subExprs = cop.getSubExpressions();
outerValues = new Expression[subExprs.length];
for (int j=0; j < outerValues.length; j++) {
Expression expr = subExprs[j].simplify();
if (expr instanceof CheckNullOperator)
expr = ((CheckNullOperator)
expr).getSubExpressions()[0];
if (expr instanceof ThisOperator) {
outerValues[j] =
new ThisOperator(((ThisOperator)
expr).getClassInfo());
continue;
}
LocalInfo li = null;
if (expr instanceof LocalLoadOperator) {
li = ((LocalLoadOperator) expr).getLocalInfo();
if (!li.isConstant())
li = null;
}
if (expr instanceof OuterLocalOperator)
li = ((OuterLocalOperator) expr).getLocalInfo();
if (li != null) {
outerValues[j] = new OuterLocalOperator(li);
continue;
}
Expression[] newOuter = new Expression[j];
System.arraycopy(outerValues, 0, newOuter, 0, j);
outerValues = newOuter;
break;
}
anonAnalyzer = new ClassAnalyzer(this, clazz, imports,
outerValues);
addClassAnalyzer(anonAnalyzer);
anonAnalyzer.analyze();
anonAnalyzer.analyzeInnerClasses();
} else {
/*
* Get the previously created outerValues array and
* its length.
*/
outerValues = anonAnalyzer.getOuterValues();
/*
* Merge the other constructor invocation and
* possibly shrink outerValues array.
*/
Expression[] subExprs = cop.getSubExpressions();
for (int j=0; j < outerValues.length; j++) {
if (j < subExprs.length) {
Expression expr = subExprs[j].simplify();
if (expr instanceof CheckNullOperator)
expr = ((CheckNullOperator) expr)
.getSubExpressions()[0];
if (unifyOuterValues(outerValues[j], expr,
anonAnalyzer, j))
continue;
System.err.println("shrinkOuterValues: "
+outerValues[j]+" vs. "+expr);
}
anonAnalyzer.shrinkOuterValues(j);
break;
}
}
}
public void createAnonymousClasses() {
int serialnr = 0;
Enumeration elts = anonConstructors.elements();
while (elts.hasMoreElements()) {
ConstructorOperator cop = (ConstructorOperator) elts.nextElement();
analyzeConstructorOperator(cop);
}
}
/**
* Get the class analyzer for the given class info. This searches
* the method scoped/anonymous classes in this method and all
* outer methods and the outer classes for the class analyzer.
* @param cinfo the classinfo for which the analyzer is searched.
* @return the class analyzer, or null if there is not an outer
* class that equals cinfo, and not a method scope/inner class in
* an outer method.
*/
public ClassAnalyzer getClassAnalyzer(ClassInfo cinfo) {
if (innerAnalyzers != null) {
Enumeration enum = innerAnalyzers.elements();
while (enum.hasMoreElements()) {
ClassAnalyzer classAna = (ClassAnalyzer) enum.nextElement();
if (classAna.getClazz().equals(cinfo)) {
if (!isParent(classAna.getParent())) {
Expression[] outerValues = classAna.getOuterValues();
for (int i=0; i< outerValues.length; i++) {
if (outerValues[i] instanceof OuterLocalOperator) {
LocalInfo li = ((OuterLocalOperator)
outerValues[i]).getLocalInfo();
classAna.shrinkOuterValues(i-1);
}
}
classAna.setParent(this);
}
return classAna;
}
}
}
return getParent().getClassAnalyzer(cinfo);
}
public void addClassAnalyzer(ClassAnalyzer clazzAna) {
if (innerAnalyzers == null)
innerAnalyzers = new Vector();
innerAnalyzers.addElement(clazzAna);
getParent().addClassAnalyzer(clazzAna);
}
public boolean isParent(ClassDeclarer declarer) {
ClassDeclarer ancestor = this;
while (ancestor != null) {
if (ancestor == declarer)
return true;
ancestor = ancestor.getParent();
}
return false;
}
public String toString() {
return "MethodAnalyzer["+getClazz()+"."+getName()+"]";
}
} }

Loading…
Cancel
Save