diff --git a/jode/jode/flow/TransformConstructors.java b/jode/jode/flow/TransformConstructors.java index c2c3aad..c60797e 100644 --- a/jode/jode/flow/TransformConstructors.java +++ b/jode/jode/flow/TransformConstructors.java @@ -35,38 +35,113 @@ import java.util.Vector; import java.util.Enumeration; /** + * This class will transform the constructors. This involves several + * steps: + * + * + * + * It will make use of the outerValues expression, that + * tell which parameters (this and final method variables) are always + * given to the constructor. + * + * You can debug this class with the --debug=constructors + * switch. * - * @author Jochen Hoenicke - */ + * @author Jochen Hoenicke + * @see jode.decompiler.FieldAnalyzer#setInitializer + * @see jode.decompiler.ClassAnalyzer#getOuterValues */ public class TransformConstructors { + /* 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 + * will shift. + */ + + ClassAnalyzer clazzAnalyzer; + boolean isStatic; + MethodAnalyzer[] cons; + + Expression[] outerValues; + /** + * The minimal first slot number after the outerValues. This is because + * the outerValues array may shrink to the desired size. */ + int ovMinSlots; + /** + * The maximal first slot number after the outerValues. + */ + int ovMaxSlots; + + boolean jikesAnonInner = false; + + public TransformConstructors(ClassAnalyzer clazzAnalyzer, + boolean isStatic, MethodAnalyzer[] cons) { + this.clazzAnalyzer = clazzAnalyzer; + this.isStatic = isStatic; + this.cons = cons; + this.outerValues = clazzAnalyzer.getOuterValues(); + this.ovMinSlots = 1; + ovMaxSlots = 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]+", "); + } + } + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_CONSTRS) != 0) + GlobalOptions.err.println(" ["+ovMinSlots+","+ovMaxSlots+"]"); + } public static boolean isThis(Expression thisExpr, ClassInfo clazz) { return ((thisExpr instanceof ThisOperator) && (((ThisOperator)thisExpr).getClassInfo() == clazz)); } - public static boolean checkImplicitAnonymousConstructor - (ClassAnalyzer clazzAnalyzer, MethodAnalyzer constr) { + /** + * Translate a slot into an index into the outerValues array. + * @return index into outerValues array or -1, if not matched. + */ + public int getOuterValueIndex(int slot) { + int ovSlot = 1; // slot of first outerValue + for (int i=0; i< outerValues.length; i++) { + if (ovSlot == slot) + return i; + ovSlot += outerValues[i].getType().stackSize(); + } + return -1; + } + + public boolean checkAnonymousConstructor(MethodAnalyzer constr, + InstructionBlock superBlock) { if (clazzAnalyzer.getName() != null) return false; - int outerValuesLength = clazzAnalyzer.getOuterValues().length; - MethodType origType = constr.getType(); /** * Situation: - * constructor(outerParams, params) { + * constructor(outerValues, params) { * super(params); * } * * Mark constructor as anonymous constructor. */ - StructuredBlock sb = constr.getMethodHeader().block; - if (!(sb instanceof InstructionBlock)) - return false; - - InstructionBlock superBlock = (InstructionBlock) sb; if (!(superBlock.getInstruction() instanceof InvokeOperator)) return false; @@ -84,23 +159,49 @@ public class TransformConstructors { return false; Type[] constrParams = constr.getType().getParameterTypes(); - if (subExpr.length - 1 != constrParams.length - outerValuesLength) + + int ovLength = constrParams.length - (subExpr.length - 1); + int ovSlots = 1; + for (int i=0; i< ovLength; i++) { + ovSlots += outerValues[i].getType().stackSize(); + } + + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_CONSTRS) != 0) + GlobalOptions.err.println("anonymousConstructor: slots expected: " + +ovSlots+" possible: [" + +ovMinSlots+","+ovMaxSlots+"]"); + if (ovSlots < ovMinSlots || ovSlots > ovMaxSlots) return false; - for (int i = 1; i < subExpr.length; i++) { + int slot = ovSlots; + int start = jikesAnonInner ? 2 : 1; + for (int i = start; i < subExpr.length; i++) { if (!(subExpr[i] instanceof LocalLoadOperator)) return false; LocalLoadOperator llop = (LocalLoadOperator) subExpr[i]; - if (llop.getLocalInfo().getSlot() != i + outerValuesLength) + + if (llop.getLocalInfo().getSlot() != slot) return false; + slot += subExpr[i].getType().stackSize(); } + if (jikesAnonInner) { + if (!(subExpr[1] instanceof LocalLoadOperator)) + return false; + LocalLoadOperator llop = (LocalLoadOperator) subExpr[1]; + + if (llop.getLocalInfo().getSlot() != slot) + return false; + } + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_CONSTRS) != 0) + GlobalOptions.err.println("anonymousConstructors succeeded"); + ovMinSlots = ovMaxSlots = ovSlots; constr.setAnonymousConstructor(true); return true; - } - public static boolean checkJikesSuperAndFillLoads(Expression expr, - int outerValuesLength, - Vector localLoads) { + public boolean checkJikesSuperAndFillLoads(Expression expr, + Vector localLoads) { if (expr instanceof LocalStoreOperator || expr instanceof IIncOperator) return false; @@ -108,33 +209,41 @@ public class TransformConstructors { if (expr instanceof LocalLoadOperator) { LocalLoadOperator llop = (LocalLoadOperator) expr; int slot = llop.getLocalInfo().getSlot(); - if (slot < outerValuesLength) - return false; + if (slot != 1) { + /* slot 1 is outerValues[0], which is okay */ + if (slot < ovMinSlots) + return false; + if (slot < ovMaxSlots) + ovMaxSlots = slot; + } localLoads.addElement(llop); } if (expr instanceof Operator) { Expression subExpr[] = ((Operator)expr).getSubExpressions(); for (int i=0; i< subExpr.length; i++) { - if (!checkJikesSuperAndFillLoads(subExpr[i], outerValuesLength, - localLoads)) + if (!checkJikesSuperAndFillLoads(subExpr[i], localLoads)) return false; } } return true; } - public static boolean checkJikesContinuation - (ClassAnalyzer clazzAnalyzer, MethodAnalyzer constr) { + public boolean checkJikesContinuation(MethodAnalyzer constr) { - Expression[] oVs = clazzAnalyzer.getOuterValues(); - int outerValuesLength = oVs == null ? 0 : oVs.length; - MethodType origType = constr.getType(); + MethodType constrType = constr.getType(); - /** + /* * Situation: - * constructor(outerParams, params) { - * [optional: super(this, params, exprs)] - * constructor$?(Outer.this, params); + * constructor(outerValues, params) { + * [optional: super(expressions builded of (outerValues[0], params))] + * constructor$?(outerValues[0], params); + * } + * + * For anonymous classes that extends class/method scope classes + * the situation is more unusal: + * constructor(outerValues, params, outerClass) { + * outerClass.super(params); + * constructor$?(outerValues[0], params); * } * * Move optional super to method constructor$? @@ -170,10 +279,25 @@ public class TransformConstructors { if (!isThis(thisExpr, clazzAnalyzer.getClazz())) return false; + ClassInfo superClazz = superCall.getClassInfo(); + if ((Decompiler.options & + (Decompiler.OPTION_ANON | Decompiler.OPTION_INNER)) != 0 + && clazzAnalyzer.getName() == null + && superClazz.getOuterClasses() != null + && subExpr[1] instanceof LocalLoadOperator) { + Type[] paramTypes = constrType.getParameterTypes(); + int expectedSlot = 1; + for (int i=0; i< paramTypes.length-1; i++) { + expectedSlot += paramTypes[i].stackSize(); + } + if (((LocalLoadOperator) + subExpr[1]).getLocalInfo().getSlot() == expectedSlot) + jikesAnonInner = true; + } + localLoads = new Vector(); - for (int i=1; i< subExpr.length; i++) { - if (!checkJikesSuperAndFillLoads(subExpr[i], outerValuesLength, - localLoads)) + for (int i=2; i< subExpr.length; i++) { + if (!checkJikesSuperAndFillLoads(subExpr[i], localLoads)) return false; } @@ -195,18 +319,32 @@ public class TransformConstructors { CodeAnalyzer codeAna = methodAna.getCode(); if (codeAna == null) return false; - MethodType type = methodAna.getType(); - + MethodType methodType = methodAna.getType(); + + int ovLength = constrType.getParameterTypes().length + - (methodType.getParameterTypes().length - 1); + if (jikesAnonInner) + ovLength--; + if (ovLength > outerValues.length) + return false; + int ovSlots = 1; + for (int i=0; i< ovLength; i++) { + ovSlots += outerValues[i].getType().stackSize(); + } + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_CONSTRS) != 0) + GlobalOptions.err.println("jikesConstrCont: slots expected: " + +ovSlots+" possible: [" + +ovMinSlots+","+ovMaxSlots+"]"); + if (!methodAna.getName().startsWith("constructor$") - || (type.getParameterTypes().length - 1 - != origType.getParameterTypes().length - outerValuesLength) - || type.getReturnType() != Type.tVoid) + || ovSlots < ovMinSlots || ovSlots > ovMaxSlots + || methodType.getReturnType() != Type.tVoid) return false; - + if (!isThis(invoke.getSubExpressions()[0], clazzAnalyzer.getClazz())) return false; - ClassInfo parent; if (clazzAnalyzer.getParent() instanceof ClassAnalyzer) parent = ((ClassAnalyzer) clazzAnalyzer.getParent()) @@ -216,18 +354,36 @@ public class TransformConstructors { .getClazz(); else return false; - if (!isThis(invoke.getSubExpressions()[1], parent)) + + Expression[] constrParams = invoke.getSubExpressions(); + if (!isThis(outerValues[0], parent) + || !(constrParams[1] instanceof LocalLoadOperator) + || ((LocalLoadOperator) + constrParams[1]).getLocalInfo().getSlot() != 1) return false; - for (int j = 1; j < type.getParameterTypes().length; j++) { - if (!(invoke.getSubExpressions()[j+1] - instanceof LocalLoadOperator)) - return false; - LocalLoadOperator llop - = (LocalLoadOperator) invoke.getSubExpressions()[j+1]; - if (llop.getLocalInfo().getSlot() != j + outerValuesLength) - return false; + { + int slot = ovSlots; + int start = 2; + for (int j = start; j < constrParams.length; j++) { + if (!(constrParams[j] instanceof LocalLoadOperator)) + return false; + LocalLoadOperator llop + = (LocalLoadOperator) constrParams[j]; + if (llop.getLocalInfo().getSlot() != slot) + return false; + slot += constrParams[j].getType().stackSize(); + } } - + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_CONSTRS) != 0) + GlobalOptions.err.println("jikesConstrCont succeded."); + + ovMinSlots = ovMaxSlots = ovSlots; + if (superBlock != null + && checkAnonymousConstructor(constr, superBlock)) { + superBlock = null; + } + constr.getMethodHeader().block.removeBlock(); /* Now move the constructor call. @@ -238,22 +394,28 @@ public class TransformConstructors { while (enum.hasMoreElements()) { LocalLoadOperator llop = (LocalLoadOperator) enum.nextElement(); - int newSlot = llop.getLocalInfo().getSlot() - - outerValuesLength + 1; + int slot = llop.getLocalInfo().getSlot(); + + int newSlot = (slot == 1 + ? 1 /* outerValues[0] */ + : slot - ovSlots + 2); llop.setCodeAnalyzer(codeAna); llop.setLocalInfo(codeAna.getLocalInfo(0, newSlot)); } codeAna.insertStructuredBlock(superBlock); } + clazzAnalyzer.setJikesAnonymousInner(jikesAnonInner); constr.setJikesConstructor(true); methodAna.setJikesConstructor(true); + codeAna.getParamInfo(1).setExpression(outerValues[0]); methodAna.analyze(); - checkImplicitAnonymousConstructor(clazzAnalyzer, methodAna); + if (constr.isAnonymousConstructor() + && methodAna.getMethodHeader().block instanceof EmptyBlock) + methodAna.setAnonymousConstructor(true); return true; } - public static void transform(ClassAnalyzer clazzAnalyzer, - boolean isStatic, MethodAnalyzer[] cons) { + public void transform() { if (cons.length == 0) return; @@ -299,7 +461,9 @@ public class TransformConstructors { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) GlobalOptions.err.println("skipping this()"); + MethodAnalyzer temp = cons[i]; cons[i] = cons[--constrCount]; + cons[constrCount] = temp; continue; } /* This constructor begins with a super call, as @@ -308,38 +472,6 @@ public class TransformConstructors { InnerClassInfo outer = invoke.getOuterClassInfo(); /* If the super() has no parameters, we can remove it */ -// /* If this is an anonymous class, and the super() gets -// * the same parameters as this class, remove it, too -// */ -// if (clazzAnalyzer.getName() == null -// && (Decompiler.options & Decompiler.OPTION_ANON) != 0 -// && cons.length == 1) { -// int skipParams = 1; -// if (outer != null && outer.outer != null -// && outer.name != null -// && !Modifier.isStatic(outer.modifiers) -// && (Decompiler.options -// & Decompiler.OPTION_INNER) != 0 -// && (invoke.getSubExpressions()[1] -// instanceof ThisOperator)) -// skipParams++; -// int length = invoke.getSubExpressions().length -// - skipParams; -// Type[] consType = cons[i].getMethodType() -// .getParameterTypes(); -// int outerValuesLength = -// clazzAnalyzer.getOuterValues().length; -// if (length == consType.length - outerValuesLength) { -// for (int j=0; j< length; j++) { -// Expression expr = -// invoke.getSubExpressions()[skipParams+j]; -// /*XXX*/ -// } -// } -// clazzAnalyzer.setSuperParams(params); -// ib.removeBlock(); -// } -// else if (outer != null && outer.outer != null && outer.name != null && !Modifier.isStatic(outer.modifiers)) { @@ -416,7 +548,23 @@ public class TransformConstructors { } Expression expr = store.getSubExpressions()[1]; - if (!expr.isConstant()) { + 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); @@ -428,7 +576,6 @@ public class TransformConstructors { GlobalOptions.err.println("field " + pfo.getFieldName() + " = " + expr); - for (int i=1; i< constrCount; i++) { ib = (sb[i] instanceof SequentialBlock) ? sb[i].getSubBlocks()[0] @@ -438,7 +585,8 @@ public class TransformConstructors { .equals(instr))) { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) - GlobalOptions.err.println("constr "+i+" differs: "+ib); + GlobalOptions.err.println("constr "+i+" differs: "+ib + +((InstructionBlock)ib).getInstruction().simplify()+" "+instr); break big_loop; } } @@ -452,7 +600,7 @@ public class TransformConstructors { GlobalOptions.err.println("setField failed"); break big_loop; } - + for (int i=0; i< constrCount; i++) { if (sb[i] instanceof SequentialBlock) @@ -461,7 +609,7 @@ public class TransformConstructors { sb[i] = null; } } - + for (int i=0; i< constrCount; i++) { if (start[i] != null) { if (sb[i] == null) @@ -470,11 +618,47 @@ public class TransformConstructors { sb[i].replace(start[i]); sb[i].simplify(); } - // Check for jikes continuation - checkJikesContinuation(clazzAnalyzer, cons[i]); + if ((Decompiler.options & Decompiler.OPTION_CONTRAFO) != 0) + // Check for jikes continuation + checkJikesContinuation(cons[i]); } - checkImplicitAnonymousConstructor(clazzAnalyzer, cons[i]); - } + if ((Decompiler.options & Decompiler.OPTION_CONTRAFO) != 0 + && (cons[i].getMethodHeader().block + instanceof InstructionBlock)) { + checkAnonymousConstructor(cons[i], + (InstructionBlock) + cons[i].getMethodHeader().block); + } + } + ovMaxSlots = ovMinSlots; + int ovLength = 0; + if (outerValues != null) { + for (int slot=1; slot < ovMinSlots; ) { + slot += outerValues[ovLength++].getType().stackSize(); + } + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_CONSTRS) != 0) + GlobalOptions.err.println("shrinking outerValues from " + + outerValues.length + + " to " + ovLength); + + if (ovLength < outerValues.length) { + Expression[] newOuterValues = new Expression[ovLength]; + System.arraycopy(outerValues, 0, newOuterValues, 0, ovLength); + clazzAnalyzer.setOuterValues(newOuterValues); + } + } + + /* Now tell _all_ constructors the value of outerValues-parameters + * 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(); + } } } -