0001: /* MethodAnalyzer Copyright (C) 1998-2002 Jochen Hoenicke.
0002: *
0003: * This program is free software; you can redistribute it and/or modify
0004: * it under the terms of the GNU Lesser General Public License as published by
0005: * the Free Software Foundation; either version 2, or (at your option)
0006: * any later version.
0007: *
0008: * This program is distributed in the hope that it will be useful,
0009: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0010: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0011: * GNU General Public License for more details.
0012: *
0013: * You should have received a copy of the GNU Lesser General Public License
0014: * along with this program; see the file COPYING.LESSER. If not, write to
0015: * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
0016: *
0017: * $Id: MethodAnalyzer.java.in,v 4.8.2.4 2002/05/28 17:34:03 hoenicke Exp $
0018: */
0019:
0020: package jode.decompiler;
0021:
0022: import jode.AssertError;
0023: import jode.GlobalOptions;
0024: import jode.bytecode.BytecodeInfo;
0025: import jode.bytecode.ClassInfo;
0026: import jode.bytecode.MethodInfo;
0027: import jode.bytecode.Handler;
0028: import jode.bytecode.Instruction;
0029: import jode.bytecode.LocalVariableInfo;
0030: import jode.jvm.SyntheticAnalyzer;
0031: import jode.decompiler.Options;
0032: import jode.type.*;
0033: import jode.expr.Expression;
0034: import jode.expr.ConstOperator;
0035: import jode.expr.CheckNullOperator;
0036: import jode.expr.ThisOperator;
0037: import jode.expr.LocalLoadOperator;
0038: import jode.expr.OuterLocalOperator;
0039: import jode.expr.InvokeOperator;
0040: import jode.flow.StructuredBlock;
0041: import jode.flow.FlowBlock;
0042: import jode.flow.TransformExceptionHandlers;
0043: import jode.flow.Jump;
0044: import jode.jvm.CodeVerifier;
0045: import jode.jvm.VerifyException;
0046: import jode.util.SimpleMap;
0047:
0048: import java.lang.reflect.Modifier;
0049: import java.util.BitSet;
0050: import java.util.Stack;
0051: import java.util.Vector;
0052: import java.util.Enumeration;
0053: import java.io.DataInputStream;
0054: import java.io.ByteArrayInputStream;
0055: import java.io.IOException;
0056:
0057: import java.util.Map;
0058: import java.util.Collection;
0059: import java.util.ArrayList;
0060: import java.util.Iterator;
0061: import java.util.Set;
0062:
0063: /**
0064: * A method analyzer is the main class for analyzation of methods.
0065: * There is exactly one MethodAnalyzer object for each method (even
0066: * for abstract methods), that should be decompiled.
0067: *
0068: * Method analyzation is done in three passes:
0069: * <dl>
0070: * <dt><code>analyze()</code></dt>
0071: * <dd>the main analyzation, decompiles the code of the method</dd>
0072: * <dt><code>analyzeInners()</code></dt>
0073: * <dd>This will analyze method scopes classes by calling their
0074: * <code>analyze()</code> and <code>analyzeInners()</code>
0075: * methods.</dd>
0076: * <dt><code>makeDeclaration()</code></dt>
0077: * <dd>This will determine when to declare variables. For constructors
0078: * it will do special transformations like field initialization.</dd>
0079: */
0080: public class MethodAnalyzer implements Scope, ClassDeclarer {
0081: /**
0082: * The minimal visible complexity.
0083: */
0084: private static double STEP_COMPLEXITY = 0.01;
0085: /**
0086: * The value of the strictfp modifier.
0087: * JDK1.1 doesn't define it.
0088: */
0089: private static int STRICTFP = 0x800;
0090: /**
0091: * The import handler where we should register our types.
0092: */
0093: ImportHandler imports;
0094: /**
0095: * The class analyzer of the class that contains this method.
0096: */
0097: ClassAnalyzer classAnalyzer;
0098: /**
0099: * The method info structure for this method.
0100: */
0101: MethodInfo minfo;
0102: /**
0103: * This is the bytecode info structure, or null if this method has
0104: * no code (abstract or native).
0105: */
0106: BytecodeInfo code;
0107:
0108: /**
0109: * The method name.
0110: */
0111: String methodName;
0112: /**
0113: * The type of this method (parameter types + return type).
0114: */
0115: MethodType methodType;
0116: /**
0117: * True, iff this method is a constructor, i.e. methodName == <(cl)?init>
0118: */
0119: boolean isConstructor;
0120:
0121: /**
0122: * The exceptions this method may throw.
0123: */
0124: Type[] exceptions;
0125:
0126: /**
0127: * If the method is synthetic (access$, class$, etc.), this is the
0128: * synthetic analyzer describing the function of this method, otherwise
0129: * this is null.
0130: */
0131: SyntheticAnalyzer synth;
0132:
0133: /**
0134: * This is the first flow block of the method code. If this
0135: * method has no code, this is null. This is initialized at the
0136: * end of the <code>analyze()</code> phase.
0137: */
0138: FlowBlock methodHeader;
0139: /**
0140: * A list of all locals contained in this method.
0141: */
0142: Vector allLocals = new Vector();
0143:
0144: /**
0145: * This array contains the locals in the parameter list, including
0146: * the implicit <i>this</i> parameter for nonstatic methods.
0147: */
0148: LocalInfo[] param;
0149: /**
0150: * The local variable table containing info about names and types of
0151: * locals.
0152: */
0153: LocalVariableTable lvt;
0154:
0155: /**
0156: * If this method is the special constructor, that is generated
0157: * by jikes (constructor$xx), this points to the real constructor.
0158: * If this is the real constructor and calls a constructor$xx, it
0159: * points to this. Otherwise this is null.
0160: */
0161: MethodAnalyzer jikesConstructor;
0162: /**
0163: * True, iff this method is the special constructor, and its first
0164: * parameter is a reference to the outer class.
0165: */
0166: boolean hasJikesOuterValue;
0167: /**
0168: * True, iff this method is an anonymous constructor, that is
0169: * omitted even if it has parameters.
0170: */
0171: boolean isAnonymousConstructor;
0172: /**
0173: * True, if this method is the special block$ method generated by jikes
0174: * to initialize field members.
0175: */
0176: boolean isJikesBlockInitializer;
0177:
0178: /**
0179: * This list contains the InvokeOperator objects in the code of
0180: * this method, that create method scoped classes. */
0181: Vector anonConstructors = new Vector();
0182:
0183: /**
0184: * This list contains the class analyzers of all method scoped
0185: * classes that should be declared in this method or in a class
0186: * that is declared in this method.
0187: */
0188: Vector innerAnalyzers;
0189: /**
0190: * This list contains the class analyzers of all method scoped
0191: * classes that are used in this method.
0192: */
0193: Collection usedAnalyzers;
0194:
0195: /**
0196: * This is the default constructor.
0197: * @param cla the ClassAnalyzer of the class that contains this method.
0198: * @param minfo the method info structure for this method.
0199: * @param imports the import handler that should be informed about types.
0200: */
0201: public MethodAnalyzer(ClassAnalyzer cla, MethodInfo minfo,
0202: ImportHandler imports) {
0203: this .classAnalyzer = cla;
0204: this .imports = imports;
0205: this .minfo = minfo;
0206: this .methodName = minfo.getName();
0207: this .methodType = Type.tMethod(minfo.getType());
0208: this .isConstructor = methodName.equals("<init>")
0209: || methodName.equals("<clinit>");
0210:
0211: if (minfo.getBytecode() != null) {
0212: code = minfo.getBytecode();
0213: }
0214: String[] excattr = minfo.getExceptions();
0215: if (excattr == null) {
0216: exceptions = new Type[0];
0217: } else {
0218: int excCount = excattr.length;
0219: this .exceptions = new Type[excCount];
0220: for (int i = 0; i < excCount; i++)
0221: exceptions[i] = Type.tClass(excattr[i]);
0222: }
0223: if (minfo.isSynthetic() || methodName.indexOf('$') != -1)
0224: synth = new SyntheticAnalyzer(minfo, true);
0225: }
0226:
0227: /**
0228: * Returns the name of this method.
0229: */
0230: public String getName() {
0231: return methodName;
0232: }
0233:
0234: /**
0235: * Returns the type of this method.
0236: * @return the type of this method.
0237: */
0238: public MethodType getType() {
0239: return methodType;
0240: }
0241:
0242: /**
0243: * Returns the first flow block of the code.
0244: * @return the first flow block of the code.
0245: */
0246: public FlowBlock getMethodHeader() {
0247: return methodHeader;
0248: }
0249:
0250: /**
0251: * Returns the bytecode info for this method.
0252: * @return the bytecode info for this method, or null if it is
0253: * abstract or native.
0254: */
0255: public final BytecodeInfo getBytecodeInfo() {
0256: return code;
0257: }
0258:
0259: /**
0260: * Returns the import handler. The import handler should be informed
0261: * about all types we (or an expression in this method) use, so that
0262: * the corresponding class can be imported.
0263: * @return the import handler.
0264: */
0265: public final ImportHandler getImportHandler() {
0266: return imports;
0267: }
0268:
0269: /**
0270: * Registers a type at the import handler. This should be called
0271: * if an expression needs to print the type name to the code. The
0272: * corresponding class will be imported in that case (if used
0273: * often enough).
0274: * @param type the type that should be registered.
0275: */
0276: public final void useType(Type type) {
0277: imports.useType(type);
0278: }
0279:
0280: /**
0281: * Inserts a structured block to the beginning of the method.
0282: * This is called by transform constructors, to move the super
0283: * call from the real constructor to the constructor$xx method
0284: * (the jikes constructor).
0285: * @param insertBlock the structured block that should be inserted.
0286: */
0287: public void insertStructuredBlock(StructuredBlock insertBlock) {
0288: if (methodHeader != null) {
0289: insertBlock.setJump(new Jump(FlowBlock.NEXT_BY_ADDR));
0290: FlowBlock insertFlowBlock = new FlowBlock(this , 0);
0291: insertFlowBlock.appendBlock(insertBlock, 0);
0292: insertFlowBlock.setNextByAddr(methodHeader);
0293: insertFlowBlock.doT2(methodHeader);
0294: methodHeader = insertFlowBlock;
0295: } else {
0296: throw new IllegalStateException();
0297: }
0298: }
0299:
0300: /**
0301: * Checks if this method is a constructor, i.e. getName() returns
0302: * "<init>" or "<clinit>".
0303: * @return true, iff this method is a real constructor.
0304: */
0305: public final boolean isConstructor() {
0306: return isConstructor;
0307: }
0308:
0309: /**
0310: * Checks if this method is static.
0311: * @return true, iff this method is static.
0312: */
0313: public final boolean isStatic() {
0314: return minfo.isStatic();
0315: }
0316:
0317: /**
0318: * Checks if this method is synthetic, i.e. a synthetic attribute is
0319: * present.
0320: * @return true, iff this method is synthetic.
0321: */
0322: public final boolean isSynthetic() {
0323: return minfo.isSynthetic();
0324: }
0325:
0326: /**
0327: * Checks if this method is strictfp
0328: * @return true, iff this method is synthetic.
0329: */
0330: public final boolean isStrictFP() {
0331: return (minfo.getModifiers() & STRICTFP) != 0;
0332: }
0333:
0334: /**
0335: * Tells if this method is the constructor$xx method generated by jikes.
0336: * @param value true, iff this method is the jikes constructor.
0337: */
0338: public final void setJikesConstructor(MethodAnalyzer realConstr) {
0339: jikesConstructor = realConstr;
0340: }
0341:
0342: /**
0343: * Tells if this method is the block$xx method generated by jikes.
0344: * @param value true, iff this method is the jikes block initializer.
0345: */
0346: public final void setJikesBlockInitializer(boolean value) {
0347: isJikesBlockInitializer = value;
0348: }
0349:
0350: /**
0351: * Tells if this (constructor$xx) method has as first (implicit)
0352: * parameter the instance of the outer class.
0353: * @param value true, this method has the implicit parameter.
0354: */
0355: public final void setHasOuterValue(boolean value) {
0356: hasJikesOuterValue = value;
0357: }
0358:
0359: /**
0360: * Tells if this constructor can be omited, since it is implicit.
0361: * @param value true, this method is the implicit constructor.
0362: */
0363: public final void setAnonymousConstructor(boolean value) {
0364: isAnonymousConstructor = value;
0365: }
0366:
0367: /**
0368: * Checks if this constructor can be omited, since it is implicit.
0369: * @return true, this method is the implicit constructor.
0370: */
0371: public final boolean isAnonymousConstructor() {
0372: return isAnonymousConstructor;
0373: }
0374:
0375: /**
0376: * Get the synthetic analyzer for this method.
0377: * @return the synthetic analyzer, or null if this method isn't
0378: * synthetic.
0379: */
0380: public final SyntheticAnalyzer getSynthetic() {
0381: return synth;
0382: }
0383:
0384: /**
0385: * Get the return type of this method.
0386: */
0387: public Type getReturnType() {
0388: return methodType.getReturnType();
0389: }
0390:
0391: /**
0392: * Get the class analyzer for the class containing this method.
0393: */
0394: public ClassAnalyzer getClassAnalyzer() {
0395: return classAnalyzer;
0396: }
0397:
0398: /**
0399: * Get the class info for the class containing this method.
0400: */
0401: public ClassInfo getClazz() {
0402: return classAnalyzer.clazz;
0403: }
0404:
0405: /**
0406: * Get the local info for a parameter. This call is valid after
0407: * the analyze pass.
0408: * @param nr the index of the parameter (start by zero and
0409: * count the implicit this param for nonstatic method).
0410: * @return the local info for the specified parameter.
0411: * @see #getLocalInfo
0412: */
0413: public final LocalInfo getParamInfo(int nr) {
0414: return param[nr];
0415: }
0416:
0417: /**
0418: * Return the number of parameters for this method. This call is
0419: * valid after the analyze pass.
0420: */
0421: public final int getParamCount() {
0422: return param.length;
0423: }
0424:
0425: /**
0426: * Create a local info for a local variable located at an
0427: * instruction with the given address.
0428: * @param addr the address of the instruction using this local.
0429: * the address of the next instruction for stores.
0430: * @param slot the slot, the local variable uses.
0431: * @return a new local info representing that local.
0432: */
0433: public LocalInfo getLocalInfo(int addr, int slot) {
0434: LocalInfo li = new LocalInfo(this , slot);
0435: if (lvt != null) {
0436: LocalVarEntry entry = lvt.getLocal(slot, addr);
0437: if (entry != null)
0438: li.addHint(entry.getName(), entry.getType());
0439: }
0440: allLocals.addElement(li);
0441: return li;
0442: }
0443:
0444: /**
0445: * Gets the complexity of this class. Must be called after it has
0446: * been initialized. This is used for a nice progress bar.
0447: */
0448: public double getComplexity() {
0449: if (code == null)
0450: return 0.0;
0451: else
0452: return code.getInstructions().size();
0453: }
0454:
0455: /**
0456: * Analyzes the code of this method. This creates the
0457: * flow blocks (including methodHeader) and analyzes them.
0458: */
0459: private void analyzeCode(ProgressListener pl, double done,
0460: double scale) {
0461: int instrsPerStep = Integer.MAX_VALUE;
0462: if (GlobalOptions.verboseLevel > 0)
0463: GlobalOptions.err.print(methodName + ": ");
0464:
0465: if (pl != null) {
0466: instrsPerStep = (int) ((code.getInstructions().size() * STEP_COMPLEXITY) / (scale * 0.9));
0467: }
0468:
0469: /* The adjacent analyzation relies on this */
0470: DeadCodeAnalysis.removeDeadCode(code);
0471: Handler[] handlers = code.getExceptionHandlers();
0472: int returnCount;
0473: TransformExceptionHandlers excHandlers;
0474: {
0475: /* First create a FlowBlock for every block that has a
0476: * predecessor other than the previous instruction.
0477: */
0478: for (Iterator i = code.getInstructions().iterator(); i
0479: .hasNext();) {
0480: Instruction instr = (Instruction) i.next();
0481: if (instr.getPrevByAddr() == null
0482: || instr.getPrevByAddr().doesAlwaysJump()
0483: || instr.getPreds() != null)
0484: instr.setTmpInfo(new FlowBlock(this , instr
0485: .getAddr()));
0486: }
0487:
0488: for (int i = 0; i < handlers.length; i++) {
0489: Instruction instr = handlers[i].start;
0490: if (instr.getTmpInfo() == null)
0491: instr.setTmpInfo(new FlowBlock(this , instr
0492: .getAddr()));
0493: /* end doesn't have a predecessor, but we must prevent
0494: * it from being merged with the previous instructions.
0495: */
0496: instr = handlers[i].end.getNextByAddr();
0497: if (instr.getTmpInfo() == null)
0498: instr.setTmpInfo(new FlowBlock(this , instr
0499: .getAddr()));
0500: instr = handlers[i].catcher;
0501: if (instr.getTmpInfo() == null)
0502: instr.setTmpInfo(new FlowBlock(this , instr
0503: .getAddr()));
0504: }
0505:
0506: /* While we read the opcodes into FlowBlocks
0507: * we try to combine sequential blocks, as soon as we
0508: * find two sequential instructions in a row, where the
0509: * second has no predecessors.
0510: */
0511: int mark = 1000;
0512: int count = 0;
0513: FlowBlock lastBlock = null;
0514: boolean lastSequential = false;
0515: for (Iterator i = code.getInstructions().iterator(); i
0516: .hasNext();) {
0517: Instruction instr = (Instruction) i.next();
0518:
0519: jode.flow.StructuredBlock block = Opcodes.readOpcode(
0520: instr, this );
0521:
0522: if (GlobalOptions.verboseLevel > 0
0523: && instr.getAddr() > mark) {
0524: GlobalOptions.err.print('.');
0525: mark += 1000;
0526: }
0527: if (++count >= instrsPerStep) {
0528: done += count * scale
0529: / code.getInstructions().size();
0530: pl.updateProgress(done, methodName);
0531: count = 0;
0532: }
0533:
0534: if (lastSequential && instr.getTmpInfo() == null
0535: /* Only merge with previous block, if this is sequential,
0536: * too.
0537: * Why? appendBlock only handles sequential blocks.
0538: */
0539: && !instr.doesAlwaysJump() && instr.getSuccs() == null) {
0540:
0541: lastBlock.appendBlock(block, instr.getLength());
0542: } else {
0543:
0544: if (instr.getTmpInfo() == null)
0545: instr.setTmpInfo(new FlowBlock(this , instr
0546: .getAddr()));
0547: FlowBlock flowBlock = (FlowBlock) instr
0548: .getTmpInfo();
0549: flowBlock.appendBlock(block, instr.getLength());
0550:
0551: if (lastBlock != null)
0552: lastBlock.setNextByAddr(flowBlock);
0553:
0554: instr.setTmpInfo(lastBlock = flowBlock);
0555: lastSequential = !instr.doesAlwaysJump()
0556: && instr.getSuccs() == null;
0557: }
0558: }
0559:
0560: methodHeader = (FlowBlock) ((Instruction) code
0561: .getInstructions().get(0)).getTmpInfo();
0562: excHandlers = new TransformExceptionHandlers();
0563: for (int i = 0; i < handlers.length; i++) {
0564: Type type = null;
0565: FlowBlock start = (FlowBlock) handlers[i].start
0566: .getTmpInfo();
0567: int endAddr = handlers[i].end.getNextByAddr().getAddr();
0568: FlowBlock handler = (FlowBlock) handlers[i].catcher
0569: .getTmpInfo();
0570: if (handlers[i].type != null)
0571: type = Type.tClass(handlers[i].type);
0572:
0573: excHandlers.addHandler(start, endAddr, handler, type);
0574: }
0575: }
0576: for (Iterator i = code.getInstructions().iterator(); i
0577: .hasNext();) {
0578: Instruction instr = (Instruction) i.next();
0579: instr.setTmpInfo(null);
0580: }
0581:
0582: if (GlobalOptions.verboseLevel > 0)
0583: GlobalOptions.err.print('-');
0584:
0585: excHandlers.analyze();
0586: methodHeader.analyze();
0587:
0588: if ((Options.options & Options.OPTION_PUSH) == 0
0589: && methodHeader.mapStackToLocal())
0590: methodHeader.removePush();
0591: if ((Options.options & Options.OPTION_ONETIME) != 0)
0592: methodHeader.removeOnetimeLocals();
0593:
0594: methodHeader.mergeParams(param);
0595:
0596: if (GlobalOptions.verboseLevel > 0)
0597: GlobalOptions.err.println("");
0598: if (pl != null) {
0599: done += 0.1 * scale;
0600: pl.updateProgress(done, methodName);
0601: }
0602: }
0603:
0604: /**
0605: * This is the first pass of the analyzation. It will analyze the
0606: * code of this method, but not the method scoped classes.
0607: */
0608: public void analyze(ProgressListener pl, double done, double scale)
0609: throws ClassFormatError {
0610: if (pl != null)
0611: pl.updateProgress(done, methodName);
0612: if (code != null) {
0613: if ((Options.options & Options.OPTION_VERIFY) != 0) {
0614: CodeVerifier verifier = new CodeVerifier(getClazz(),
0615: minfo, code);
0616: try {
0617: verifier.verify();
0618: } catch (VerifyException ex) {
0619: ex.printStackTrace(GlobalOptions.err);
0620: throw new jode.AssertError("Verification error");
0621: }
0622: }
0623:
0624: if ((Options.options & Options.OPTION_LVT) != 0) {
0625: LocalVariableInfo[] localvars = code
0626: .getLocalVariableTable();
0627: if (localvars != null)
0628: lvt = new LocalVariableTable(code.getMaxLocals(),
0629: localvars);
0630: }
0631: }
0632:
0633: Type[] paramTypes = getType().getParameterTypes();
0634: int paramCount = (isStatic() ? 0 : 1) + paramTypes.length;
0635: param = new LocalInfo[paramCount];
0636:
0637: int offset = 0;
0638: int slot = 0;
0639: if (!isStatic()) {
0640: ClassInfo classInfo = classAnalyzer.getClazz();
0641: LocalInfo this Local = getLocalInfo(0, slot++);
0642: this Local.setExpression(new ThisOperator(classInfo, true));
0643: param[offset++] = this Local;
0644: }
0645:
0646: for (int i = 0; i < paramTypes.length; i++) {
0647: param[offset] = getLocalInfo(0, slot);
0648: param[offset].setType(paramTypes[i]);
0649: slot += paramTypes[i].stackSize();
0650: offset++;
0651: }
0652:
0653: for (int i = 0; i < exceptions.length; i++)
0654: imports.useType(exceptions[i]);
0655:
0656: if (!isConstructor)
0657: imports.useType(methodType.getReturnType());
0658:
0659: if (code != null)
0660: analyzeCode(pl, done, scale);
0661: }
0662:
0663: /**
0664: * This is the second pass of the analyzation. It will analyze
0665: * the method scoped classes.
0666: */
0667: public void analyzeInnerClasses() throws ClassFormatError {
0668: int serialnr = 0;
0669: Enumeration elts = anonConstructors.elements();
0670: while (elts.hasMoreElements()) {
0671: InvokeOperator cop = (InvokeOperator) elts.nextElement();
0672: analyzeInvokeOperator(cop);
0673: }
0674: }
0675:
0676: /**
0677: * This is the third and last pass of the analyzation. It will analyze
0678: * the types and names of the local variables and where to declare them.
0679: * It will also determine where to declare method scoped local variables.
0680: */
0681: public void makeDeclaration(Set done) {
0682: if (innerAnalyzers != null) {
0683: for (Enumeration enum = innerAnalyzers.elements();
0684: enum.hasMoreElements(); ) {
0685: ClassAnalyzer classAna = (ClassAnalyzer) enum.nextElement();
0686: if (classAna.getParent() == this ) {
0687: OuterValues innerOV = classAna.getOuterValues();
0688: for (int i=0; i < innerOV.getCount(); i++) {
0689: Expression value = innerOV.getValue(i);
0690: if (value instanceof OuterLocalOperator) {
0691: LocalInfo li = ((OuterLocalOperator)
0692: value).getLocalInfo();
0693: if (li.getMethodAnalyzer() == this )
0694: li.markFinal();
0695: }
0696: }
0697: }
0698: }
0699: }
0700:
0701: for (Enumeration enum = allLocals.elements();
0702: enum.hasMoreElements(); ) {
0703: LocalInfo li = (LocalInfo)enum.nextElement();
0704: if (!li.isShadow())
0705: imports.useType(li.getType());
0706: }
0707: for (int i=0; i < param.length; i++) {
0708: param[i].guessName();
0709: Iterator doneIter = done.iterator();
0710: while (doneIter.hasNext()) {
0711: Declarable previous = (Declarable) doneIter.next();
0712: if (param[i].getName().equals(previous.getName())) {
0713: /* A name conflict happened. */
0714: param[i].makeNameUnique();
0715: break;
0716: }
0717: }
0718: done.add(param[i]);
0719: }
0720:
0721: if (code != null) {
0722: methodHeader.makeDeclaration(done);
0723: methodHeader.simplify();
0724: }
0725: for (int i=0; i < param.length; i++) {
0726: done.remove(param[i]);
0727: // remove the parameters, since we leave the scope
0728: }
0729: }
0730:
0731: /**
0732: * Tells if this method is synthetic or implicit or something else, so
0733: * that it doesn't have to be written to the source code.
0734: * @return true, iff it shouldn't be written to the source code.
0735: */
0736: public boolean skipWriting() {
0737: if (synth != null) {
0738: // We don't need this class anymore (hopefully?)
0739: if (synth.getKind() == synth.GETCLASS)
0740: return true;
0741: if (synth.getKind() >= synth.ACCESSGETFIELD
0742: && synth.getKind() <= synth.ACCESSDUPPUTSTATIC
0743: && (Options.options & Options.OPTION_INNER) != 0
0744: && (Options.options & Options.OPTION_ANON) != 0)
0745: return true;
0746: }
0747:
0748: if (jikesConstructor == this ) {
0749: // This is the first empty part of a jikes constructor
0750: return true;
0751: }
0752:
0753: boolean declareAsConstructor = isConstructor;
0754: int skipParams = 0;
0755: if (isConstructor() && !isStatic()
0756: && classAnalyzer.outerValues != null)
0757: skipParams = classAnalyzer.outerValues.getCount();
0758:
0759: if (jikesConstructor != null) {
0760: // This is the real part of a jikes constructor
0761: declareAsConstructor = true;
0762: skipParams = hasJikesOuterValue
0763: && classAnalyzer.outerValues.getCount() > 0 ? 1 : 0;
0764: }
0765:
0766: if (isJikesBlockInitializer)
0767: return true;
0768:
0769: /* The default constructor must be empty
0770: * and mustn't throw exceptions */
0771: if (getMethodHeader() == null
0772: || !(getMethodHeader().getBlock() instanceof jode.flow.EmptyBlock)
0773: || !getMethodHeader().hasNoJumps()
0774: || exceptions.length > 0)
0775: return false;
0776:
0777: if (declareAsConstructor
0778: /* The access rights of default constructor should
0779: * be public, iff the class is public, otherwise package.
0780: * But this rule doesn't necessarily apply for anonymous
0781: * classes...
0782: */
0783: && ((minfo.getModifiers() & (Modifier.PROTECTED
0784: | Modifier.PUBLIC | Modifier.PRIVATE
0785: | Modifier.SYNCHRONIZED | Modifier.STATIC
0786: | Modifier.ABSTRACT | Modifier.NATIVE)) == (getClassAnalyzer()
0787: .getModifiers() & (Modifier.PROTECTED | Modifier.PUBLIC)) || classAnalyzer
0788: .getName() == null)
0789: && classAnalyzer.constructors.length == 1) {
0790:
0791: // If the constructor doesn't take parameters (except outerValues)
0792: // or if it is the anonymous constructor it can be removed.
0793: if (methodType.getParameterTypes().length == skipParams
0794: || isAnonymousConstructor)
0795: return true;
0796: }
0797:
0798: if (isConstructor() && isStatic())
0799: return true;
0800:
0801: return false;
0802: }
0803:
0804: /**
0805: * Dumps the source code for this method to the specified writer.
0806: * @param writer the tabbed print writer the code should be written to.
0807: * @exception IOException, if writer throws an exception.
0808: */
0809: public void dumpSource(TabbedPrintWriter writer) throws IOException {
0810: boolean declareAsConstructor = isConstructor;
0811: int skipParams = 0;
0812: int modifiedModifiers = minfo.getModifiers();
0813:
0814: if (isConstructor() && !isStatic()
0815: && (Options.options & Options.OPTION_CONTRAFO) != 0
0816: && classAnalyzer.outerValues != null)
0817: skipParams = classAnalyzer.outerValues.getCount();
0818:
0819: if (jikesConstructor != null) {
0820: // This is the real part of a jikes constructor
0821: declareAsConstructor = true;
0822: skipParams = hasJikesOuterValue
0823: && classAnalyzer.outerValues.getCount() > 0 ? 1 : 0;
0824: // get the modifiers of the real constructor
0825: modifiedModifiers = jikesConstructor.minfo.getModifiers();
0826: }
0827:
0828: if (minfo.isDeprecated()) {
0829: writer.println("/**");
0830: writer.println(" * @deprecated");
0831: writer.println(" */");
0832: }
0833:
0834: writer.pushScope(this );
0835:
0836: /*
0837: * JLS-1.0, section 9.4:
0838: *
0839: * For compatibility with older versions of Java, it is
0840: * permitted but discouraged, as a matter of style, to
0841: * redundantly specify the abstract modifier for methods
0842: * declared in interfaces.
0843: *
0844: * Every method declaration in the body of an interface is
0845: * implicitly public. It is permitted, but strongly
0846: * discouraged as a matter of style, to redundantly specify
0847: * the public modifier for interface methods. We don't
0848: * follow this second rule and mark this method explicitly
0849: * as public.
0850: */
0851: if (classAnalyzer.getClazz().isInterface())
0852: modifiedModifiers &= ~Modifier.ABSTRACT;
0853:
0854: /* Don't ask me why, but jikes declares the static constructor
0855: * as final. Another compiler or obfuscator seems to declare
0856: * it as public. I remove every fancy modifier, now.
0857: */
0858: if (isConstructor() && isStatic())
0859: modifiedModifiers &= ~(Modifier.FINAL | Modifier.PUBLIC
0860: | Modifier.PROTECTED | Modifier.PRIVATE);
0861: modifiedModifiers &= ~STRICTFP;
0862:
0863: writer.startOp(writer.NO_PAREN, 1);
0864: String delim = "";
0865: if (minfo.isSynthetic()) {
0866: writer.print("/*synthetic*/");
0867: delim = " ";
0868: }
0869:
0870: String modif = Modifier.toString(modifiedModifiers);
0871: if (modif.length() > 0) {
0872: writer.print(delim + modif);
0873: delim = " ";
0874: }
0875: if (isStrictFP()) {
0876: /* The STRICTFP modifier is set.
0877: * We handle it, since java.lang.reflect.Modifier is too dumb.
0878: */
0879:
0880: /* If STRICTFP is already set for class don't set it for method.
0881: * And don't set STRICTFP for native methods or constructors.
0882: */
0883: if (!classAnalyzer.isStrictFP() && !isConstructor()
0884: && (modifiedModifiers & Modifier.NATIVE) == 0) {
0885: writer.print(delim + "strictfp");
0886: delim = " ";
0887: }
0888: }
0889:
0890: if (isConstructor
0891: && (isStatic() || (classAnalyzer.getName() == null && skipParams == methodType
0892: .getParameterTypes().length))) {
0893: /* static block or unnamed constructor */
0894: } else {
0895: writer.print(delim);
0896: if (declareAsConstructor)
0897: writer.print(classAnalyzer.getName());
0898: else {
0899: writer.printType(getReturnType());
0900: writer.print(" " + methodName);
0901: }
0902: writer.breakOp();
0903: if ((Options.outputStyle & Options.GNU_SPACING) != 0)
0904: writer.print(" ");
0905: writer.print("(");
0906: writer.startOp(writer.EXPL_PAREN, 0);
0907: int offset = skipParams + (isStatic() ? 0 : 1);
0908: for (int i = offset; i < param.length; i++) {
0909: if (i > offset) {
0910: writer.print(", ");
0911: writer.breakOp();
0912: }
0913: param[i].dumpDeclaration(writer);
0914: }
0915: writer.endOp();
0916: writer.print(")");
0917: }
0918: if (exceptions.length > 0) {
0919: writer.breakOp();
0920: writer.print(" throws ");
0921: writer.startOp(writer.EXPL_PAREN, 2);
0922: for (int i = 0; i < exceptions.length; i++) {
0923: if (i > 0) {
0924: writer.print(",");
0925: writer.breakOp();
0926: writer.print(" ");
0927: }
0928: writer.printType(exceptions[i]);
0929: }
0930: writer.endOp();
0931: }
0932: writer.endOp();
0933: if (code != null) {
0934: writer.openBraceNoIndent();
0935: writer.tab();
0936: methodHeader.dumpSource(writer);
0937: writer.untab();
0938: writer.closeBraceNoIndent();
0939: } else
0940: writer.println(";");
0941: writer.popScope();
0942: }
0943:
0944: /**
0945: * Checks if the variable set contains a local with the given name.
0946: * @return the local info the has the given name, or null if it doesn't
0947: * exists.
0948: */
0949: public LocalInfo findLocal(String name) {
0950: Enumeration enum = allLocals.elements();
0951: while (enum.hasMoreElements()) {
0952: LocalInfo li = (LocalInfo) enum.nextElement();
0953: if (li.getName().equals(name))
0954: return li;
0955: }
0956: return null;
0957: }
0958:
0959: /**
0960: * Checks if a method scoped class with the given name exists in this
0961: * method (not in a parent method).
0962: * @return the class analyzer with the given name, or null if it
0963: * doesn' exists.
0964: */
0965: public ClassAnalyzer findAnonClass(String name) {
0966: if (innerAnalyzers != null) {
0967: Enumeration enum = innerAnalyzers.elements();
0968: while (enum.hasMoreElements()) {
0969: ClassAnalyzer classAna = (ClassAnalyzer) enum.nextElement();
0970: if (classAna.getParent() == this
0971: && classAna.getName() != null
0972: && classAna.getName().equals(name)) {
0973: return classAna;
0974: }
0975: }
0976: }
0977: return null;
0978: }
0979:
0980: /**
0981: * Checks if the specified object lies in this scope.
0982: * @param obj the object.
0983: * @param scopeType the type of this object.
0984: */
0985: public boolean isScopeOf(Object obj, int scopeType) {
0986: if (scopeType == METHODSCOPE && obj instanceof ClassInfo) {
0987: ClassAnalyzer ana = getClassAnalyzer((ClassInfo) obj);
0988: if (ana != null)
0989: return ana.getParent() == this ;
0990: }
0991: return false;
0992: }
0993:
0994: /**
0995: * Checks if the specified name conflicts with an object in this scope.
0996: * @param name the name to check.
0997: * @param scopeType the usage type of this name, AMBIGUOUSNAME if it is
0998: * ambiguous.
0999: */
1000: public boolean conflicts(String name, int usageType) {
1001: if (usageType == AMBIGUOUSNAME || usageType == LOCALNAME)
1002: return findLocal(name) != null;
1003: if (usageType == AMBIGUOUSNAME || usageType == CLASSNAME)
1004: return findAnonClass(name) != null;
1005: return false;
1006: }
1007:
1008: /**
1009: * Gets the parent scope, i.e. the class analyzer for the class containing
1010: * this method.
1011: * @XXX needed?
1012: */
1013: public ClassDeclarer getParent() {
1014: return getClassAnalyzer();
1015: }
1016:
1017: /**
1018: * Registers an anonymous constructor invokation. This should be called
1019: * in the analyze or analyzeInner pass by invoke subexpressions.
1020: * @param cop the constructor invokation, that creates the method scoped
1021: * class.
1022: */
1023: public void addAnonymousConstructor(InvokeOperator cop) {
1024: anonConstructors.addElement(cop);
1025: }
1026:
1027: public void analyzeInvokeOperator(InvokeOperator cop) {
1028: ClassInfo clazz = (ClassInfo) cop.getClassInfo();
1029: ClassAnalyzer anonAnalyzer = getParent()
1030: .getClassAnalyzer(clazz);
1031:
1032: if (anonAnalyzer == null) {
1033: /* Create a new outerValues array corresponding to the
1034: * first constructor invocation.
1035: */
1036: Expression[] outerValueArray;
1037: Expression[] subExprs = cop.getSubExpressions();
1038: outerValueArray = new Expression[subExprs.length - 1];
1039:
1040: for (int j = 0; j < outerValueArray.length; j++) {
1041: Expression expr = subExprs[j + 1].simplify();
1042: if (expr instanceof CheckNullOperator)
1043: expr = ((CheckNullOperator) expr)
1044: .getSubExpressions()[0];
1045: if (expr instanceof ThisOperator) {
1046: outerValueArray[j] = new ThisOperator(
1047: ((ThisOperator) expr).getClassInfo());
1048: continue;
1049: }
1050: LocalInfo li = null;
1051: if (expr instanceof LocalLoadOperator) {
1052: li = ((LocalLoadOperator) expr).getLocalInfo();
1053: if (!li.isConstant())
1054: li = null;
1055: }
1056: if (expr instanceof OuterLocalOperator)
1057: li = ((OuterLocalOperator) expr).getLocalInfo();
1058:
1059: if (li != null) {
1060: outerValueArray[j] = new OuterLocalOperator(li);
1061: continue;
1062: }
1063:
1064: Expression[] newOuter = new Expression[j];
1065: System.arraycopy(outerValueArray, 0, newOuter, 0, j);
1066: outerValueArray = newOuter;
1067: break;
1068: }
1069: anonAnalyzer = new ClassAnalyzer(this , clazz, imports,
1070: outerValueArray);
1071: addClassAnalyzer(anonAnalyzer);
1072: anonAnalyzer.initialize();
1073: anonAnalyzer.analyze(null, 0.0, 0.0);
1074: anonAnalyzer.analyzeInnerClasses(null, 0.0, 0.0);
1075: } else {
1076: /*
1077: * Get the previously created outerValues and
1078: * its length.
1079: */
1080: OuterValues outerValues = anonAnalyzer.getOuterValues();
1081: /*
1082: * Merge the other constructor invocation and
1083: * possibly shrink outerValues array.
1084: */
1085: Expression[] subExprs = cop.getSubExpressions();
1086: for (int j = 0; j < outerValues.getCount(); j++) {
1087: if (j + 1 < subExprs.length) {
1088: Expression expr = subExprs[j + 1].simplify();
1089: if (expr instanceof CheckNullOperator)
1090: expr = ((CheckNullOperator) expr)
1091: .getSubExpressions()[0];
1092:
1093: if (outerValues.unifyOuterValues(j, expr))
1094: continue;
1095:
1096: }
1097: outerValues.setCount(j);
1098: break;
1099: }
1100: }
1101:
1102: if (usedAnalyzers == null)
1103: usedAnalyzers = new ArrayList();
1104: usedAnalyzers.add(anonAnalyzer);
1105: }
1106:
1107: /**
1108: * Get the class analyzer for the given class info. This searches
1109: * the method scoped/anonymous classes in this method and all
1110: * outer methods and the outer classes for the class analyzer.
1111: * @param cinfo the classinfo for which the analyzer is searched.
1112: * @return the class analyzer, or null if there is not an outer
1113: * class that equals cinfo, and not a method scope/inner class in
1114: * an outer method.
1115: */
1116: public ClassAnalyzer getClassAnalyzer(ClassInfo cinfo) {
1117: if (innerAnalyzers != null) {
1118: Enumeration enum = innerAnalyzers.elements();
1119: while (enum.hasMoreElements()) {
1120: ClassAnalyzer classAna = (ClassAnalyzer) enum.nextElement();
1121: if (classAna.getClazz().equals(cinfo)) {
1122: if (classAna.getParent() != this ) {
1123: ClassDeclarer declarer = classAna.getParent();
1124: while (declarer != this ) {
1125: if (declarer instanceof MethodAnalyzer)
1126: ((MethodAnalyzer) declarer)
1127: .innerAnalyzers.removeElement(classAna);
1128: declarer = declarer.getParent();
1129: }
1130: classAna.setParent(this );
1131: }
1132: return classAna;
1133: }
1134: }
1135: }
1136: return getParent().getClassAnalyzer(cinfo);
1137: }
1138:
1139: public void addClassAnalyzer(ClassAnalyzer clazzAna) {
1140: if (innerAnalyzers == null)
1141: innerAnalyzers = new Vector();
1142: innerAnalyzers.addElement(clazzAna);
1143: getParent().addClassAnalyzer(clazzAna);
1144: }
1145:
1146: /**
1147: * We add the named method scoped classes to the declarables.
1148: */
1149: public void fillDeclarables(Collection used) {
1150: if (usedAnalyzers != null)
1151: used.addAll(usedAnalyzers);
1152: if (innerAnalyzers != null) {
1153: Enumeration enum = innerAnalyzers.elements();
1154: while (enum.hasMoreElements()) {
1155: ClassAnalyzer classAna = (ClassAnalyzer) enum.nextElement();
1156: if (classAna.getParent() == this )
1157: classAna.fillDeclarables(used);
1158: }
1159: }
1160: }
1161:
1162: public boolean isMoreOuterThan(ClassDeclarer declarer) {
1163: ClassDeclarer ancestor = declarer;
1164: while (ancestor != null) {
1165: if (ancestor == this )
1166: return true;
1167: ancestor = ancestor.getParent();
1168: }
1169: return false;
1170: }
1171:
1172: public String toString() {
1173: return getClass().getName() + "[" + getClazz() + "."
1174: + getName() + "]";
1175: }
1176: }
|