big rework, outervalues are looked in two passes.

git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@896 379699f6-c40d-0410-875b-85095c16579e
stable
jochen 25 years ago
parent b3f87ea1e0
commit 01b81620bc
  1. 455
      jode/jode/flow/TransformConstructors.java

@ -23,8 +23,10 @@ import jode.GlobalOptions;
import jode.Decompiler;
import jode.decompiler.Analyzer;
import jode.decompiler.ClassAnalyzer;
import jode.decompiler.CodeAnalyzer;
import jode.decompiler.MethodAnalyzer;
import jode.decompiler.FieldAnalyzer;
import jode.decompiler.MethodAnalyzer;
import jode.decompiler.OuterValueListener;
import jode.expr.*;
import jode.type.MethodType;
import jode.type.Type;
@ -60,8 +62,9 @@ import java.util.Enumeration;
*
* @author Jochen Hoenicke
* @see jode.decompiler.FieldAnalyzer#setInitializer
* @see jode.decompiler.ClassAnalyzer#getOuterValues */
public class TransformConstructors {
* @see jode.decompiler.ClassAnalyzer#getOuterValues
*/
public class TransformConstructors implements OuterValueListener {
/* What is sometimes confusing is the distinction between slot and
* parameter. Most times parameter nr = slot nr, but double and
* long parameters take two slots, so the remaining parameters
@ -89,25 +92,46 @@ public class TransformConstructors {
this.clazzAnalyzer = clazzAnalyzer;
this.isStatic = isStatic;
this.cons = cons;
this.outerValues = clazzAnalyzer.getOuterValues();
this.ovMinSlots = 1;
ovMaxSlots = 1;
ovMaxSlots = Integer.MAX_VALUE;
this.outerValues = clazzAnalyzer.getOuterValues();
if (outerValues != null) {
clazzAnalyzer.addOuterValueListener(this);
updateOuterValues(outerValues.length);
}
}
public void updateOuterValues(int count) {
int outerSlots = 1;
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_CONSTRS) != 0)
GlobalOptions.err.print("OuterValues: ");
if (outerValues != null) {
for (int i=0; i< outerValues.length; i++) {
ovMaxSlots += outerValues[i].getType().stackSize();
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_CONSTRS) != 0)
GlobalOptions.err.print(outerValues[i]+", ");
}
for (int i=0; i< count; i++) {
outerSlots += outerValues[i].getType().stackSize();
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_CONSTRS) != 0)
GlobalOptions.err.print(outerValues[i]+", ");
}
if (outerSlots < ovMaxSlots)
ovMaxSlots = outerSlots;
if (outerSlots < ovMinSlots) {
GlobalOptions.err.println
("WARNING: something got wrong with scoped class "
+clazzAnalyzer.getClazz()+": "+ovMinSlots+","+ovMaxSlots);
GlobalOptions.err.println
("CAN'T REPAIR. PRODUCED CODE IS PROBABLY WRONG.");
Thread.dumpStack();
ovMinSlots = outerSlots;
}
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_CONSTRS) != 0)
GlobalOptions.err.println(" ["+ovMinSlots+","+ovMaxSlots+"]");
}
public void shrinkingOuterValues(ClassAnalyzer ca, int newCount) {
updateOuterValues(newCount);
}
public static boolean isThis(Expression thisExpr, ClassInfo clazz) {
return ((thisExpr instanceof ThisOperator)
&& (((ThisOperator)thisExpr).getClassInfo() == clazz));
@ -141,12 +165,12 @@ public class TransformConstructors {
*
* Mark constructor as anonymous constructor.
*/
Expression expr = superBlock.getInstruction().simplify();
if (!(superBlock.getInstruction() instanceof InvokeOperator))
if (!(expr instanceof InvokeOperator))
return false;
InvokeOperator superCall
= (InvokeOperator) superBlock.getInstruction().simplify();
InvokeOperator superCall = (InvokeOperator) expr;
superBlock.setInstruction(superCall);
if (superCall.getFreeOperandCount() != 0
@ -229,6 +253,8 @@ public class TransformConstructors {
}
public boolean checkJikesContinuation(MethodAnalyzer constr) {
if (outerValues == null)
return false;
MethodType constrType = constr.getType();
@ -263,23 +289,24 @@ public class TransformConstructors {
superBlock = (InstructionBlock) sb.getSubBlocks()[0];
sb = sb.getSubBlocks()[1];
if (!(superBlock.getInstruction() instanceof InvokeOperator))
Expression superExpr = superBlock.getInstruction().simplify();
if (!(superExpr instanceof InvokeOperator))
return false;
InvokeOperator superCall
= (InvokeOperator) superBlock.getInstruction().simplify();
superBlock.setInstruction(superCall);
InvokeOperator superInvoke = (InvokeOperator) superExpr;
superBlock.setInstruction(superInvoke);
if (superCall.getFreeOperandCount() != 0
|| !superCall.isConstructor() || !superCall.isSuperOrThis())
if (superInvoke.getFreeOperandCount() != 0
|| !superInvoke.isConstructor()
|| !superInvoke.isSuperOrThis())
return false;
Expression[] subExpr = superCall.getSubExpressions();
Expression[] subExpr = superInvoke.getSubExpressions();
Expression thisExpr = subExpr[0];
if (!isThis(thisExpr, clazzAnalyzer.getClazz()))
return false;
ClassInfo superClazz = superCall.getClassInfo();
ClassInfo superClazz = superInvoke.getClassInfo();
if ((Decompiler.options &
(Decompiler.OPTION_ANON | Decompiler.OPTION_INNER)) != 0
&& clazzAnalyzer.getName() == null
@ -305,7 +332,8 @@ public class TransformConstructors {
return false;
/* Now check the constructor$? invocation */
Expression lastExpr = ((InstructionBlock)sb).getInstruction();
Expression lastExpr
= ((InstructionBlock)sb).getInstruction().simplify();
if (!(lastExpr instanceof InvokeOperator))
return false;
@ -316,9 +344,6 @@ public class TransformConstructors {
MethodAnalyzer methodAna = invoke.getMethodAnalyzer();
if (methodAna == null)
return false;
CodeAnalyzer codeAna = methodAna.getCode();
if (codeAna == null)
return false;
MethodType methodType = methodAna.getType();
int ovLength = constrType.getParameterTypes().length
@ -349,8 +374,8 @@ public class TransformConstructors {
if (clazzAnalyzer.getParent() instanceof ClassAnalyzer)
parent = ((ClassAnalyzer) clazzAnalyzer.getParent())
.getClazz();
else if (clazzAnalyzer.getParent() instanceof CodeAnalyzer)
parent = ((CodeAnalyzer) clazzAnalyzer.getParent())
else if (clazzAnalyzer.getParent() instanceof MethodAnalyzer)
parent = ((MethodAnalyzer) clazzAnalyzer.getParent())
.getClazz();
else
return false;
@ -399,15 +424,15 @@ public class TransformConstructors {
int newSlot = (slot == 1
? 1 /* outerValues[0] */
: slot - ovSlots + 2);
llop.setCodeAnalyzer(codeAna);
llop.setLocalInfo(codeAna.getLocalInfo(0, newSlot));
llop.setMethodAnalyzer(methodAna);
llop.setLocalInfo(methodAna.getLocalInfo(0, newSlot));
}
codeAna.insertStructuredBlock(superBlock);
methodAna.insertStructuredBlock(superBlock);
}
clazzAnalyzer.setJikesAnonymousInner(jikesAnonInner);
constr.setJikesConstructor(true);
methodAna.setJikesConstructor(true);
codeAna.getParamInfo(1).setExpression(outerValues[0]);
methodAna.getParamInfo(1).setExpression(outerValues[0]);
methodAna.analyze();
if (constr.isAnonymousConstructor()
&& methodAna.getMethodHeader().block instanceof EmptyBlock)
@ -415,6 +440,236 @@ public class TransformConstructors {
return true;
}
/**
* This methods checks if expr is a valid field initializer. It
* will also merge outerValues, that occur in expr.
* @param expr the initializer to check
* @return the transformed initializer or null if expr is not valid.
*/
public Expression transformFieldInitializer(Expression expr) {
if (expr instanceof LocalVarOperator) {
if (!(expr instanceof LocalLoadOperator)) {
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_CONSTRS) != 0)
GlobalOptions.err.println("illegal local op: "+expr);
return null;
}
if (outerValues != null
&& (Decompiler.options & Decompiler.OPTION_CONTRAFO) != 0) {
int slot = ((LocalLoadOperator)expr).getLocalInfo().getSlot();
int pos = getOuterValueIndex(slot);
if (pos >= 0 && slot < ovMaxSlots) {
expr = outerValues[pos];
if (slot >= ovMinSlots)
ovMinSlots = slot + expr.getType().stackSize();
return expr;
}
}
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_CONSTRS) != 0)
GlobalOptions.err.println("not outerValue: "+expr
+" ["+ovMinSlots
+","+ovMaxSlots+"]");
return null;
}
if (expr instanceof Operator) {
Operator op = (Operator) expr;
Expression[] subExpr = op.getSubExpressions();
for (int i=0; i< subExpr.length; i++) {
Expression transformed = transformFieldInitializer(subExpr[i]);
if (transformed == null)
return null;
if (transformed != subExpr[i])
op.setSubExpressions(i, transformed);
}
}
return expr;
}
public void initSyntheticFields() {
if (isStatic)
return;
if (cons.length == 0)
return;
int constrCount = cons.length;
StructuredBlock[] sb = new StructuredBlock[constrCount];
for (int i=0; i< constrCount; ) {
FlowBlock header = cons[i].getMethodHeader();
/* Check that code block is fully analyzed */
if (header == null || !header.hasNoJumps())
return;
/* sb[i] will iterate the instructions of the constructor. */
sb[i] = cons[i].getMethodHeader().block;
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_CONSTRS) != 0)
GlobalOptions.err.println("constr "+i+": "+sb[i]);
/* A non static constructor must begin with a call to
* another constructor. Either to a constructor of the
* same class or to the super class
*/
InstructionBlock ib;
if (sb[i] instanceof InstructionBlock)
ib = (InstructionBlock)sb[i];
else if (sb[i] instanceof SequentialBlock
&& (sb[i].getSubBlocks()[0]
instanceof InstructionBlock))
ib = (InstructionBlock) sb[i].getSubBlocks()[0];
else
return;
Expression superExpr = ib.getInstruction().simplify();
if (!(superExpr instanceof InvokeOperator)
|| superExpr.getFreeOperandCount() != 0)
return;
InvokeOperator superInvoke = (InvokeOperator) superExpr;
if (!superInvoke.isConstructor()
|| !superInvoke.isSuperOrThis())
return;
Expression thisExpr = superInvoke.getSubExpressions()[0];
if (!isThis(thisExpr, clazzAnalyzer.getClazz()))
return;
if (superInvoke.isThis()) {
/* This constructor calls another constructor of this
* class, which will do the initialization. We can skip
* this constructor.
*/
/* Move constructor to the end of cons array, and
* decrease constrCount. It will not be transformed
* any further.
*/
MethodAnalyzer temp = cons[i];
cons[i] = cons[--constrCount];
cons[constrCount] = temp;
continue;
}
/* This constructor begins with a super call, as
* expected.
*/
if (sb[i] instanceof SequentialBlock)
sb[i] = sb[i].getSubBlocks()[1];
else
sb[i] = null;
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_CONSTRS) != 0)
GlobalOptions.err.println("normal constructor");
i++;
}
StructuredBlock[] start = new StructuredBlock[constrCount];
for (int i=0; i< constrCount; i++)
start[i] = sb[i];
big_loop:
for (;;) {
for (int i=0; i< constrCount; i++) {
if (sb[i] == null) {
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_CONSTRS) != 0)
GlobalOptions.err.println("constr "+i+" is over");
break big_loop;
}
}
StructuredBlock ib =
(sb[0] instanceof SequentialBlock)
? sb[0].getSubBlocks()[0]
: sb[0];
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_CONSTRS) != 0)
GlobalOptions.err.println("fieldInit: "+ib);
if (!(ib instanceof InstructionBlock))
break big_loop;
Expression instr
= ((InstructionBlock) ib).getInstruction().simplify();
if (!(instr instanceof StoreInstruction)
|| instr.getFreeOperandCount() != 0)
break big_loop;
StoreInstruction store = (StoreInstruction) instr;
if (!(store.getLValue() instanceof PutFieldOperator))
break big_loop;
PutFieldOperator pfo = (PutFieldOperator) store.getLValue();
if (pfo.isStatic() != isStatic || !pfo.isThis())
break big_loop;
if (!isThis(pfo.getSubExpressions()[0],
clazzAnalyzer.getClazz()))
break big_loop;
FieldAnalyzer field = clazzAnalyzer.getField(pfo.getFieldName(),
pfo.getFieldType());
if (!field.isFinal() || !field.isSynthetic())
break big_loop;
Expression expr = store.getSubExpressions()[1];
expr = transformFieldInitializer(expr);
if (expr == null)
break big_loop;
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_CONSTRS) != 0)
GlobalOptions.err.println("field " + pfo.getFieldName()
+ " = " + expr);
for (int i=1; i< constrCount; i++) {
ib = (sb[i] instanceof SequentialBlock)
? sb[i].getSubBlocks()[0]
: sb[i];
if (!(ib instanceof InstructionBlock)
|| !(((InstructionBlock)ib).getInstruction().simplify()
.equals(instr))) {
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_CONSTRS) != 0)
GlobalOptions.err.println("constr "+i+" differs: "+ib);
break big_loop;
}
}
if (!(field.setInitializer(expr))) {
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_CONSTRS) != 0)
GlobalOptions.err.println("setField failed");
break big_loop;
}
for (int i=0; i< constrCount; i++) {
if (sb[i] instanceof SequentialBlock)
sb[i] = sb[i].getSubBlocks()[1];
else
sb[i] = null;
}
}
for (int i=0; i< constrCount; i++) {
if (start[i] != null) {
if (sb[i] == null)
start[i].removeBlock();
else {
sb[i].replace(start[i]);
sb[i].simplify();
}
}
}
}
/**
* This does the transformations. It will set the field initializers
* and removing the initializers from all constructors.
*/
public void transform() {
if (cons.length == 0)
return;
@ -423,13 +678,21 @@ public class TransformConstructors {
StructuredBlock[] sb = new StructuredBlock[constrCount];
for (int i=0; i< constrCount; ) {
FlowBlock header = cons[i].getMethodHeader();
/* Check that code block is fully analyzed */
if (header == null || !header.hasNoJumps())
return;
/* sb[i] will iterate the instructions of the constructor. */
sb[i] = cons[i].getMethodHeader().block;
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_CONSTRS) != 0)
GlobalOptions.err.println("constr "+i+": "+sb[i]);
if (!isStatic) {
/* A non static constructor must begin with a call to
* another constructor. Either to a constructor of the
* same class or to the super class
*/
InstructionBlock ib;
if (sb[i] instanceof InstructionBlock)
ib = (InstructionBlock)sb[i];
@ -440,49 +703,90 @@ public class TransformConstructors {
else
return;
Expression instr = ib.getInstruction();
if (!(instr instanceof InvokeOperator)
|| instr.getFreeOperandCount() != 0)
Expression superExpr = ib.getInstruction().simplify();
if (!(superExpr instanceof InvokeOperator)
|| superExpr.getFreeOperandCount() != 0)
return;
InvokeOperator invoke = (InvokeOperator) instr;
if (!invoke.isConstructor() || !invoke.isSuperOrThis())
InvokeOperator superInvoke = (InvokeOperator) superExpr;
if (!superInvoke.isConstructor()
|| !superInvoke.isSuperOrThis())
return;
Expression thisExpr = invoke.getSubExpressions()[0];
Expression thisExpr = superInvoke.getSubExpressions()[0];
if (!isThis(thisExpr, clazzAnalyzer.getClazz()))
return;
if (invoke.isThis()) {
/* This constructor calls another constructor, so we
* can skip it.
if (superInvoke.isThis()) {
/* This constructor calls another constructor of this
* class, which will do the initialization. We can skip
* this constructor.
*/
/* But first check that outerValues are correctly promoted
* XXX: Note that I couldn't check this code, yet,
* since I couldn't find a compiler that can handle
* this() calls in method scoped classes :-(
*/
for (int slot = 1, j=0; slot < ovMaxSlots; j++) {
Expression param
= superInvoke.getSubExpressions()[j+1];
if (!(param instanceof LocalLoadOperator)
|| ((LocalLoadOperator)
param).getLocalInfo().getSlot() != slot) {
ovMaxSlots = slot;
break;
}
slot += param.getType().stackSize();
}
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_CONSTRS) != 0)
GlobalOptions.err.println("skipping this()");
/* Move constructor to the end of cons array, and
* decrease constrCount. It will not be transformed
* any further.
*/
MethodAnalyzer temp = cons[i];
cons[i] = cons[--constrCount];
cons[constrCount] = temp;
continue;
}
/* This constructor begins with a super call, as
* expected.
*/
InnerClassInfo outer = invoke.getOuterClassInfo();
/* If the super() has no parameters, we can remove it
ClassInfo superClazz = superInvoke.getClassInfo();
InnerClassInfo[] outers = superClazz.getOuterClasses();
/* If the super() has no parameters (or only default
* outerValue parameter for inner/anonymous classes), we
* can remove it
*/
if (outer != null && outer.outer != null
&& outer.name != null
&& !Modifier.isStatic(outer.modifiers)) {
if ((Decompiler.options & Decompiler.OPTION_INNER) != 0
&& (invoke.getMethodType().getParameterTypes()
if ((Decompiler.options & Decompiler.OPTION_ANON) != 0
&& outers != null
&& (outers[0].outer == null || outers[0].name == null)) {
ClassAnalyzer superAnalyzer = null;
Object parent = clazzAnalyzer.getParent();
while (parent != null) {
if (parent instanceof MethodAnalyzer) {
MethodAnalyzer methodAna = (MethodAnalyzer)parent;
parent = methodAna.getClassAnalyzer().getParent();
} else
parent = ((ClassAnalyzer) parent).getParent();
}
if (superAnalyzer != null) {
/* XXX check outer values.
*/
}
} else if ((Decompiler.options & Decompiler.OPTION_INNER) != 0
&& outers != null
&& outers[0].outer != null
&& outers[0].name != null) {
if (!Modifier.isStatic(outers[0].modifiers)
&& (superInvoke.getMethodType().getParameterTypes()
.length == 1)
&& (invoke.getSubExpressions()[1]
&& (superInvoke.getSubExpressions()[1]
instanceof ThisOperator))
ib.removeBlock();
} else if (invoke.getMethodType().getParameterTypes()
.length == 0)
} else if (superInvoke.getMethodType().getParameterTypes()
.length == 0)
ib.removeBlock();
if (sb[i] instanceof SequentialBlock)
@ -548,28 +852,9 @@ public class TransformConstructors {
}
Expression expr = store.getSubExpressions()[1];
if (expr instanceof LocalLoadOperator
&& outerValues != null
&& (Decompiler.options & Decompiler.OPTION_CONTRAFO) != 0) {
int slot = ((LocalLoadOperator)expr).getLocalInfo().getSlot();
int pos = getOuterValueIndex(slot);
if (pos < 0 || slot >= ovMaxSlots) {
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_CONSTRS) != 0)
GlobalOptions.err.println("not outerValue: "+expr
+" ["+ovMinSlots
+","+ovMaxSlots+"]");
break big_loop;
}
expr = outerValues[pos];
if (slot >= ovMinSlots)
ovMinSlots = slot + expr.getType().stackSize();
} else if (!expr.isConstant()) {
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_CONSTRS) != 0)
GlobalOptions.err.println("not constant: "+expr);
break big_loop;
}
expr = transformFieldInitializer(expr);
if (expr == null)
break big_loop;
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_CONSTRS) != 0)
@ -585,8 +870,7 @@ public class TransformConstructors {
.equals(instr))) {
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_CONSTRS) != 0)
GlobalOptions.err.println("constr "+i+" differs: "+ib
+((InstructionBlock)ib).getInstruction().simplify()+" <!=> "+instr);
GlobalOptions.err.println("constr "+i+" differs: "+ib);
break big_loop;
}
}
@ -630,7 +914,7 @@ public class TransformConstructors {
cons[i].getMethodHeader().block);
}
}
ovMaxSlots = ovMinSlots;
ovMinSlots = ovMaxSlots;
int ovLength = 0;
if (outerValues != null) {
for (int slot=1; slot < ovMinSlots; ) {
@ -643,9 +927,7 @@ public class TransformConstructors {
+ " to " + ovLength);
if (ovLength < outerValues.length) {
Expression[] newOuterValues = new Expression[ovLength];
System.arraycopy(outerValues, 0, newOuterValues, 0, ovLength);
clazzAnalyzer.setOuterValues(newOuterValues);
clazzAnalyzer.shrinkOuterValues(ovLength);
}
}
@ -653,12 +935,9 @@ public class TransformConstructors {
* and simplify them again.
*/
for (int i=0; i< cons.length; i++) {
CodeAnalyzer codeAna = cons[i].getCode();
if (codeAna == null)
continue;
for (int j=0; j< ovLength; j++)
codeAna.getParamInfo(j+1).setExpression(outerValues[j]);
codeAna.getMethodHeader().simplify();
cons[i].getParamInfo(j+1).setExpression(outerValues[j]);
cons[i].getMethodHeader().simplify();
}
}
}

Loading…
Cancel
Save