/* TransformConstructors Copyright (C) 1998-2002 Jochen Hoenicke. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; see the file COPYING.LESSER. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id$ */ package net.sf.jode.flow; import java.lang.reflect.Modifier; import net.sf.jode.GlobalOptions; import net.sf.jode.decompiler.ClassAnalyzer; import net.sf.jode.decompiler.MethodAnalyzer; import net.sf.jode.decompiler.FieldAnalyzer; import net.sf.jode.decompiler.Options; import net.sf.jode.decompiler.OuterValues; import net.sf.jode.decompiler.OuterValueListener; import net.sf.jode.expr.*; import net.sf.jode.type.MethodType; import net.sf.jode.type.Type; import net.sf.jode.bytecode.ClassInfo; import net.sf.jode.bytecode.ClassPath; import net.sf.jode.bytecode.MethodInfo; import java.io.IOException; /** * This class will transform the constructors. We differ three types of * constructors: *
type0
*
are constructors, that call no constructors or whose default super * call was already removed. java.lang.Object.<init> * and static constructors are examples for the first kind.
*
type1
*
are constructors, that call the constructor of super class
*
type2
*
are constructors, that call another constructor of the same class
*
* * The transformation involves several steps: * * The first step is done by removeSynthInitializers, which does the following: *
* * In the last analyze phase (makeDeclaration) the rest is done: * * * 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 * @see net.sf.jode.decompiler.FieldAnalyzer#setInitializer * @see net.sf.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; /* The method analyzers of the constructors: type0 constructors * come first, then type1, then type2. */ MethodAnalyzer[] cons; int type0Count; int type01Count; OuterValues outerValues; public TransformConstructors(ClassAnalyzer clazzAnalyzer, boolean isStatic, MethodAnalyzer[] cons) { this.clazzAnalyzer = clazzAnalyzer; this.isStatic = isStatic; this.cons = cons; if (!isStatic) this.outerValues = clazzAnalyzer.getOuterValues(); lookForConstructorCall(); } /** * Returns the type of the constructor. We differ three types of * constructors: *
type1
*
are constructors, that call the constructor of super class
*
type2
*
are constructors, that call another constructor of the same * class
*
type0
*
are constructors, that call no constructors. * java.lang.Object.<init> and static constructors * are examples for this
*
* @param body the content of the constructor. * @return the type of the constructor. */ private int getConstructorType(StructuredBlock body) { /* 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 (body instanceof InstructionBlock) ib = (InstructionBlock)body; else if (body instanceof SequentialBlock && (body.getSubBlocks()[0] instanceof InstructionBlock)) ib = (InstructionBlock) body.getSubBlocks()[0]; else return 0; Expression superExpr = ib.getInstruction().simplify(); if (!(superExpr instanceof InvokeOperator) || superExpr.getFreeOperandCount() != 0) return 0; InvokeOperator superInvoke = (InvokeOperator) superExpr; if (!superInvoke.isConstructor() || !superInvoke.isSuperOrThis()) return 0; Expression thisExpr = superInvoke.getSubExpressions()[0]; if (!isThis(thisExpr, clazzAnalyzer.getClazz())) return 0; if (superInvoke.isThis()) return 2; else return 1; } private void lookForConstructorCall() { type01Count = cons.length; for (int i=0; i< type01Count; ) { MethodAnalyzer current = cons[i]; if (!isStatic && (Options.options & Options.OPTION_CONTRAFO) != 0 && clazzAnalyzer.getOuterInstance() != null) current.getParamInfo(1).setExpression (clazzAnalyzer.getOuterInstance()); FlowBlock header = cons[i].getMethodHeader(); /* Check that code block is fully analyzed */ if (header == null || !header.hasNoJumps()) return; StructuredBlock body = cons[i].getMethodHeader().block; int type = isStatic ? 0 : getConstructorType(body); if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) GlobalOptions.err.println("constr "+i+": type"+type+" "+body); switch(type) { case 0: // type0 are moved to the beginning. cons[i] = cons[type0Count]; cons[type0Count++] = current; /* fall through */ case 1: // type1 are not moved at all. i++; break; case 2: // type2 are moved to the end. cons[i] = cons[--type01Count]; cons[type01Count] = current; break; } } } public static boolean isThis(Expression thisExpr, ClassInfo clazz) { return ((thisExpr instanceof ThisOperator) && (((ThisOperator)thisExpr).getClassInfo() == clazz)); } /** * Check if this is a single anonymous constructor and mark it as * such. We only check if the super() call is correctly formed and * ignore the rest of the body. * * This method also marks the jikesAnonymousInner. */ private void checkAnonymousConstructor() { if (isStatic || cons.length != 1 || type01Count - type0Count != 1 || clazzAnalyzer.getName() != null) return; if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) GlobalOptions.err.println("checkAnonymousConstructor of " +clazzAnalyzer.getClazz()); StructuredBlock sb = cons[0].getMethodHeader().block; if (sb instanceof SequentialBlock) sb = sb.getSubBlocks()[0]; InstructionBlock superBlock = (InstructionBlock) sb; /** * Situation: * constructor(outerValues, params) { * super(someOuters, params); * } * * For jikes anonymous classes that extends class or method * scoped classes the situation is more unusal for type1. We * check if this is the case and mark the class as * jikesAnonymousInner: * * constructor(outerValues, params, outerClass) { * outerClass.super(someOuters, params); * constructor$?(outerValues[0], params); * } * * Mark constructor as anonymous constructor. */ Expression expr = superBlock.getInstruction().simplify(); InvokeOperator superCall = (InvokeOperator) expr; Expression[] subExpr = superCall.getSubExpressions(); /* An anonymous constructor may only give locals * to its super constructor. */ for (int i = 1; i < subExpr.length; i++) { if (!(subExpr[i] instanceof LocalLoadOperator)) return; } Type[] params = cons[0].getType().getParameterTypes(); boolean jikesAnon = false; int minOuter = params.length; int slot = 1; for (int i = 0; i < params.length - 1; i++) slot += params[i].stackSize(); /* slot counts from last slot down. */ int start = 1; if (subExpr.length >= 2) { LocalLoadOperator llop = (LocalLoadOperator) subExpr[1]; if (llop.getLocalInfo().getSlot() == slot) { jikesAnon = true; start++; // This is not an outer value. outerValues.setCount(params.length - 1); minOuter--; slot -= params[minOuter - 1].stackSize(); } } int sub = subExpr.length - 1; /* Check how many parameters are passed correctly. */ while (sub >= start) { LocalLoadOperator llop = (LocalLoadOperator) subExpr[sub]; if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) GlobalOptions.err.println(" pos "+sub+": "+slot+"," + llop.getLocalInfo().getSlot()+ "; "+minOuter); if (llop.getLocalInfo().getSlot() != slot) { // restore the slot. slot += params[minOuter - 1].stackSize(); break; } sub--; /* This parameter is not forced to be an outer value */ minOuter--; if (minOuter == 0) break; slot -= params[minOuter - 1].stackSize(); } ClassAnalyzer superAna = superCall.getClassAnalyzer(); OuterValues superOV = null; if (superAna != null && superAna.getParent() instanceof MethodAnalyzer) { // super is a method scope class. superOV = superAna.getOuterValues(); } int minSuperOuter = sub - start + 1; if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) GlobalOptions.err.println(" super outer: " + superOV); /* The remaining sub expressions must be outerValues. */ for (; sub >= start; sub--) { LocalLoadOperator llop = (LocalLoadOperator) subExpr[sub]; if (llop.getLocalInfo().getSlot() >= slot) { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) GlobalOptions.err.println(" Illegal slot at "+sub+":" + llop.getLocalInfo().getSlot()); return; } } if (minSuperOuter == 1 && superAna.getParent() instanceof ClassAnalyzer) { /* Check if this is the implicit Outer Class */ LocalLoadOperator llop = (LocalLoadOperator) subExpr[start]; if (outerValues.getValueBySlot(llop.getLocalInfo().getSlot()) instanceof ThisOperator) { minSuperOuter = 0; outerValues.setImplicitOuterClass(true); } } if (minSuperOuter > 0) { if (superOV == null || superOV.getCount() < minSuperOuter) { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) GlobalOptions.err.println(" super outer doesn't match: " + minSuperOuter); return; } superOV.setMinCount(minSuperOuter); } outerValues.setMinCount(minOuter); if (superOV != null) { final int ovdiff = minOuter - minSuperOuter; outerValues.setCount(superOV.getCount() + ovdiff); superOV.addOuterValueListener(new OuterValueListener() { public void shrinkingOuterValues (OuterValues other, int newCount) { outerValues.setCount(newCount + ovdiff); } }); } else outerValues.setCount(minOuter); if (jikesAnon) outerValues.setJikesAnonymousInner(true); if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) GlobalOptions.err.println(" succeeded: "+outerValues); cons[0].setAnonymousConstructor(true); superBlock.removeBlock(); type0Count++; } private boolean checkJikesSuper(Expression expr) { if (expr instanceof LocalStoreOperator || expr instanceof IIncOperator) return false; if (expr instanceof Operator) { Expression subExpr[] = ((Operator)expr).getSubExpressions(); for (int i=0; i< subExpr.length; i++) { if (!checkJikesSuper(subExpr[i])) return false; } } return true; } private Expression renameJikesSuper(Expression expr, MethodAnalyzer methodAna, Expression outer0, int firstOuterSlot, int firstParamSlot) { if (expr instanceof LocalLoadOperator) { LocalLoadOperator llop = (LocalLoadOperator) expr; int slot = llop.getLocalInfo().getSlot(); if (slot >= firstOuterSlot && slot < firstParamSlot) return outerValues.getValueBySlot(slot); else if (slot == 1) { return outer0; } else { Type[] paramTypes = methodAna.getType().getParameterTypes(); int param; /* Adjust the slot */ if (slot >= firstParamSlot) slot -= firstParamSlot - firstOuterSlot; for (param = 0; slot > 1 && param < paramTypes.length; param++) slot -= paramTypes[param].stackSize(); llop.setLocalInfo(methodAna.getParamInfo(1+param)); llop.setMethodAnalyzer(methodAna); return llop; } } if (expr instanceof Operator) { Expression subExpr[] = ((Operator)expr).getSubExpressions(); for (int i=0; i< subExpr.length; i++) { Expression newSubExpr = renameJikesSuper(subExpr[i], methodAna, outer0, firstOuterSlot, firstParamSlot); if (newSubExpr != subExpr[i]) ((Operator)expr).setSubExpressions(i, newSubExpr); } } return expr; } private void checkJikesContinuation() { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) System.err.println("checkJikesContinuation: "+outerValues); constr_loop: for (int i=0; i < cons.length; i++) { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) GlobalOptions.err.println("constr "+i+" type" + (i < type0Count ? 0 : i < type01Count ? 1 : 2) + " : " + cons[i].getMethodHeader()); MethodAnalyzer constr = cons[i]; /* * constructor(outerValues, params, opt. jikesAnonInner param) { * optional super/this(expressions); * constructor$?(optional outerValues[0], params); * } * * The outerValues[0] parameter is the this local in the * surrounding method. But we can't be sure, what the * surrounding method is, since it could be either the method * that uses the class, or a method that declares the class, that * contains the method that uses the class.
* * If the surrounding method is static, the outerValues[0] * parameter disappears. * * Move optional super to method constructor$? * (renaming local variables) and mark constructor and * constructor$? as Jikes constructor. */ StructuredBlock sb = constr.getMethodHeader().block; InstructionBlock superBlock = null; InvokeOperator superInvoke = null; if (i >= type0Count) { /* Extract the super() or this() call at the beginning * of the constructor */ if (!(sb instanceof SequentialBlock) || !(sb.getSubBlocks()[1] instanceof InstructionBlock)) continue constr_loop; superBlock = (InstructionBlock) sb.getSubBlocks()[0]; sb = sb.getSubBlocks()[1]; superInvoke = (InvokeOperator) superBlock.getInstruction().simplify(); if (!checkJikesSuper(superInvoke)) continue constr_loop; } if (!(sb instanceof InstructionBlock)) continue constr_loop; /* Now check the constructor$? invocation */ Expression lastExpr = ((InstructionBlock)sb).getInstruction().simplify(); if (!(lastExpr instanceof InvokeOperator)) continue constr_loop; InvokeOperator invoke = (InvokeOperator) lastExpr; if (!invoke.isThis() || invoke.getFreeOperandCount() != 0) continue constr_loop; MethodAnalyzer methodAna = invoke.getMethodAnalyzer(); if (methodAna == null) continue constr_loop; MethodType methodType = methodAna.getType(); Expression[] methodParams = invoke.getSubExpressions(); if (!methodAna.getName().startsWith("constructor$") || methodType.getReturnType() != Type.tVoid) continue constr_loop; if (!isThis(methodParams[0], clazzAnalyzer.getClazz())) continue constr_loop; for (int j=1; j < methodParams.length; j++) { if (!(methodParams[j] instanceof LocalLoadOperator)) continue constr_loop; } Type[] paramTypes = constr.getType().getParameterTypes(); int paramCount = paramTypes.length; if (outerValues.isJikesAnonymousInner()) paramCount--; int maxOuterCount = paramCount - methodParams.length + 2; int minOuterCount = maxOuterCount - 1; int slot = 1; int firstParam = 1; Expression outer0 = null; if (maxOuterCount > 0 && methodParams.length > 1 && outerValues.getCount() > 0) { /* Check if the outerValues[0] param is present. * we can't be sure if maxOuterCount equals 1, but * we assume so, since at this time all possible * info about outers have been collected. */ if (((LocalLoadOperator)methodParams[firstParam] ).getLocalInfo().getSlot() == 1) { minOuterCount = maxOuterCount; outer0 = outerValues.getValue(0); firstParam++; } else maxOuterCount--; for (int j=0; j < maxOuterCount; j++) slot += paramTypes[j].stackSize(); } if (minOuterCount > outerValues.getCount()) continue constr_loop; int firstParamSlot = slot; int firstOuterSlot = firstParam; /* check the remaining parameters. */ for (int j=firstParam; j < methodParams.length; j++) { if (((LocalLoadOperator) methodParams[j] ).getLocalInfo().getSlot() != slot) continue constr_loop; slot += methodParams[j].getType().stackSize(); } outerValues.setMinCount(minOuterCount); outerValues.setCount(maxOuterCount); /* Now move the constructor call. */ if (superBlock != null) { InvokeOperator newSuper = (InvokeOperator) renameJikesSuper(superInvoke, methodAna, outer0, firstOuterSlot, firstParamSlot); superBlock.removeBlock(); if (i > type0Count) { cons[i] = cons[type0Count]; cons[type0Count] = constr; } type0Count++; if (!isDefaultSuper(newSuper)) { superBlock.setInstruction(newSuper); methodAna.insertStructuredBlock(superBlock); } } if (outer0 != null) { methodAna.getParamInfo(1).setExpression(outer0); methodAna.getMethodHeader().simplify(); } if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) GlobalOptions.err.println(" succeeded"); constr.setJikesConstructor(constr); methodAna.setJikesConstructor(constr); methodAna.setHasOuterValue(firstOuterSlot == 2); if (constr.isAnonymousConstructor()) methodAna.setAnonymousConstructor(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. */ private Expression transformFieldInitializer(int fieldSlot, 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 && (Options.options & Options.OPTION_CONTRAFO) != 0) { int slot = ((LocalLoadOperator)expr).getLocalInfo().getSlot(); Expression outExpr = outerValues.getValueBySlot(slot); if (outExpr != null) return outExpr; } if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) GlobalOptions.err.println("not outerValue: "+expr +" "+outerValues); return null; } if (expr instanceof FieldOperator) { if (expr instanceof PutFieldOperator) return null; FieldOperator fo = (FieldOperator) expr; if (fo.getClassInfo() == clazzAnalyzer.getClazz() && clazzAnalyzer.getFieldIndex(fo.getFieldName(), fo.getFieldType()) >= fieldSlot) return null; } if (expr instanceof InvokeOperator) { /* Don't allow method invocations that can throw a checked * exception to leave the constructor. */ MethodInfo method = ((InvokeOperator) expr).getMethodInfo(); String[] excs = method == null ? null : method.getExceptions(); if (excs != null) { ClassPath classPath = clazzAnalyzer.getClassPath(); ClassInfo runtimeException = classPath.getClassInfo("java.lang.RuntimeException"); ClassInfo error = classPath.getClassInfo("java.lang.Error"); for (int i = 0; i < excs.length; i++) { ClassInfo exClass = classPath.getClassInfo(excs[i]); try { if (!runtimeException.superClassOf(exClass) && !error.superClassOf(exClass)) return null; } catch (IOException ex) { 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(fieldSlot, subExpr[i]); if (transformed == null) return null; if (transformed != subExpr[i]) op.setSubExpressions(i, transformed); } } return expr; } /** * Remove initializers of synthetic fields and * This is called for non static constructors in the analyze pass, * after the constructors are analyzed. */ public void removeSynthInitializers() { if ((Options.options & Options.OPTION_CONTRAFO) == 0 || isStatic || type01Count == 0) return; if ((Options.options & Options.OPTION_ANON) != 0) checkAnonymousConstructor(); if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) GlobalOptions.err.println("removeSynthInitializers of " +clazzAnalyzer.getClazz()); /* sb will iterate the instructions of the constructor. */ StructuredBlock[] sb = new StructuredBlock[type01Count]; for (int i=0; i < type01Count; i++) { sb[i] = cons[i].getMethodHeader().block; if (i >= type0Count) { if (sb[i] instanceof SequentialBlock) sb[i] = sb[i].getSubBlocks()[1]; else /* One constructor is done. There is no field */ return; } } big_loop: for (;;) { StructuredBlock ib = (sb[0] instanceof SequentialBlock) ? sb[0].getSubBlocks()[0] : sb[0]; 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.getClassInfo() != clazzAnalyzer.getClazz()) break big_loop; if (!isThis(pfo.getSubExpressions()[0], clazzAnalyzer.getClazz())) break big_loop; int field = clazzAnalyzer.getFieldIndex(pfo.getFieldName(), pfo.getFieldType()); if (field < 0) break big_loop; FieldAnalyzer fieldAna = clazzAnalyzer.getField(field); /* Don't check for final. Jikes sometimes omits this attribute. */ if (!fieldAna.isSynthetic()) break big_loop; Expression expr = store.getSubExpressions()[1]; expr = transformFieldInitializer(field, expr); if (expr == null) break big_loop; for (int i=1; i< type01Count; 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 0 and "+i +" differ: " +instr+"<-/->"+ib); break big_loop; } } if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) GlobalOptions.err.println(" field " + pfo.getFieldName() + " = " + expr); if (!(fieldAna.setInitializer(expr))) { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) GlobalOptions.err.println(" setField failed"); break big_loop; } boolean done = false; for (int i=0; i< type01Count; i++) { if (sb[i] instanceof SequentialBlock) { StructuredBlock next = sb[i].getSubBlocks()[1]; next.replace(sb[i]); sb[i] = next; } else { sb[i].removeBlock(); sb[i] = null; done = true; } } if (done) { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) GlobalOptions.err.println("one constr is over"); break; } } } private int transformOneField(int lastField, StructuredBlock ib) { if (!(ib instanceof InstructionBlock)) return -1; Expression instr = ((InstructionBlock) ib).getInstruction().simplify(); if (!(instr instanceof StoreInstruction) || instr.getFreeOperandCount() != 0) return -1; StoreInstruction store = (StoreInstruction) instr; if (!(store.getLValue() instanceof PutFieldOperator)) return -1; PutFieldOperator pfo = (PutFieldOperator) store.getLValue(); if (pfo.isStatic() != isStatic || pfo.getClassInfo() != clazzAnalyzer.getClazz()) return -1; if (!isStatic) { if (!isThis(pfo.getSubExpressions()[0], clazzAnalyzer.getClazz())) { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) GlobalOptions.err.println(" not this: "+instr); return -1; } } int field = clazzAnalyzer.getFieldIndex(pfo.getFieldName(), pfo.getFieldType()); if (field <= lastField) return -1; Expression expr = store.getSubExpressions()[1]; expr = transformFieldInitializer(field, expr); if (expr == null) return -1; if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) GlobalOptions.err.println(" field " + pfo.getFieldName() + " = " + expr); // if field does not exists: -1 <= lastField. if (field <= lastField || !(clazzAnalyzer.getField(field).setInitializer(expr))) { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) GlobalOptions.err.println("set field failed"); return -1; } return field; } private void transformBlockInitializer(StructuredBlock block) { int lastField = -1; while (block instanceof SequentialBlock) { StructuredBlock ib = block.getSubBlocks()[0]; int field = transformOneField(lastField, ib); if (field < 0) clazzAnalyzer.addBlockInitializer(lastField + 1, ib); else lastField = field; block = block.getSubBlocks()[1]; } if (transformOneField(lastField, block) < 0) clazzAnalyzer.addBlockInitializer(lastField + 1, block); } private boolean checkBlockInitializer(InvokeOperator invoke) { if (!invoke.isThis() || invoke.getFreeOperandCount() != 0) return false; MethodAnalyzer methodAna = invoke.getMethodAnalyzer(); if (methodAna == null) return false; FlowBlock flow = methodAna.getMethodHeader(); MethodType methodType = methodAna.getType(); if (!methodAna.getName().startsWith("block$") || methodType.getParameterTypes().length != 0 || methodType.getReturnType() != Type.tVoid) return false; if (flow == null || !flow.hasNoJumps()) return false; if (!isThis(invoke.getSubExpressions()[0], clazzAnalyzer.getClazz())) return false; methodAna.setJikesBlockInitializer(true); transformBlockInitializer(flow.block); return true; } /* Checks if superInvoke is the default super call. */ private boolean isDefaultSuper(InvokeOperator superInvoke) { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) GlobalOptions.err.println("isDefaultSuper: "+superInvoke); ClassInfo superClazz = superInvoke.getClassInfo(); Expression[] params = superInvoke.getSubExpressions(); if (superClazz == null) return false; if ((Options.options & Options.OPTION_INNER) != 0 && superClazz.getOuterClass() != null && !Modifier.isStatic(superClazz.getModifiers())) { /* Super class is an inner class. Check if the default outer * instance is passed. */ if (params.length != 2) return false; Expression superOuterExpr = params[1].simplify(); if (superOuterExpr instanceof ThisOperator && (((ThisOperator) superOuterExpr).getClassInfo() == superClazz.getOuterClass())) { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) GlobalOptions.err.println(" isDefaultSuper success"); return true; } return false; } int outerValCount = 0; ClassAnalyzer superClazzAna = superInvoke.getClassAnalyzer(); if (superClazzAna != null) { OuterValues superOV = superClazzAna.getOuterValues(); if (superOV != null) outerValCount = superOV.getCount(); } /* Check if only this and the outer value parameters are * transmitted. The analyze pass already made sure, that the * outer value parameters are correct. */ if (params.length != outerValCount + 1) return false; if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) GlobalOptions.err.println(" isDefaultSuper success"); return true; } private void removeDefaultSuper() { /* Check if we can remove the super() call of type1 constructors. * This transforms a type1 constructor in a type0 constructor. */ for (int i=type0Count; i< type01Count; i++) { MethodAnalyzer current = cons[i]; FlowBlock header = cons[i].getMethodHeader(); StructuredBlock body = header.block; InstructionBlock ib; if (body instanceof InstructionBlock) ib = (InstructionBlock) body; else ib = (InstructionBlock) body.getSubBlocks()[0]; InvokeOperator superInvoke = (InvokeOperator) ib.getInstruction().simplify(); if (isDefaultSuper(superInvoke)) { ib.removeBlock(); if (i > type0Count) { cons[i] = cons[type0Count]; cons[type0Count] = current; } type0Count++; } } } private void removeInitializers() { if (type01Count == 0) return; if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) GlobalOptions.err.println("removeInitializers"); StructuredBlock[] sb = new StructuredBlock[type01Count]; for (int i=0; i< type01Count; i++) { FlowBlock header = cons[i].getMethodHeader(); /* sb[i] will iterate the instructions of the constructor. */ sb[i] = header.block; if (i >= type0Count) { if (sb[i] instanceof SequentialBlock) sb[i] = sb[i].getSubBlocks()[1]; else { sb[i] = null; return; } } } int lastField = -1; big_loop: for (;;) { StructuredBlock ib = (sb[0] instanceof SequentialBlock) ? sb[0].getSubBlocks()[0] : sb[0]; if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) GlobalOptions.err.println("Instruction: "+ib); if (!(ib instanceof InstructionBlock)) break big_loop; Expression instr = ((InstructionBlock) ib).getInstruction().simplify(); for (int i=1; i < type01Count; 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 (instr instanceof InvokeOperator && checkBlockInitializer((InvokeOperator) instr)) { for (int i=0; i< type01Count; i++) { if (sb[i] instanceof SequentialBlock) { StructuredBlock next = sb[i].getSubBlocks()[1]; next.replace(sb[i]); sb[i] = next; } else { sb[i].removeBlock(); sb[i] = null; } } break big_loop; } int field = transformOneField(lastField, ib); if (field < 0) break big_loop; lastField = field; boolean done = false; for (int i=0; i< type01Count; i++) { if (sb[i] instanceof SequentialBlock) { StructuredBlock next = sb[i].getSubBlocks()[1]; next.replace(sb[i]); sb[i] = next; } else { sb[i].removeBlock(); sb[i] = null; done = true; } } if (done) { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) GlobalOptions.err.println("one constr is over"); break; } } } /** * This does the normal constructor transformations. * * javac copies the field initializers to each constructor. This * will undo the transformation: it will tell the fields about the * initial value and removes the initialization from all constructors. * * There are of course many checks necessary: All field * initializers must be equal in all constructors, and there * mustn't be locals that used in field initialization (except * outerValue - locals). */ public void transform() { if ((Options.options & Options.OPTION_CONTRAFO) == 0 || cons.length == 0) return; removeInitializers(); checkJikesContinuation(); if (outerValues != null) { /* Now tell all constructors the value of outerValues parameters */ for (int i=0; i< cons.length; i++) { for (int j = 0; j < outerValues.getCount(); j++) cons[i].getParamInfo(j+1) .setExpression(outerValues.getValue(j)); } } removeDefaultSuper(); } }