From 74001822d7098b346c8f890b10edb8b4e7c1e752 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 1 Sep 1999 10:16:09 +0000 Subject: [PATCH] simplified parameter handling. added comments git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@1158 379699f6-c40d-0410-875b-85095c16579e --- jode/jode/decompiler/MethodAnalyzer.java.in | 396 +++++++++++++++----- 1 file changed, 299 insertions(+), 97 deletions(-) diff --git a/jode/jode/decompiler/MethodAnalyzer.java.in b/jode/jode/decompiler/MethodAnalyzer.java.in index 07c7f55..7f9e830 100644 --- a/jode/jode/decompiler/MethodAnalyzer.java.in +++ b/jode/jode/decompiler/MethodAnalyzer.java.in @@ -56,39 +56,127 @@ import java.io.IOException; import @COLLECTIONS@.Map; import @COLLECTIONS@.Iterator; +/** + * A method analyzer is the main class for analyzation of methods. + * There is exactly one MethodAnalyzer object for each method (even + * for abstract methods), that should be decompiled. + * + * Method analyzation is done in three passes: + *
+ *
analyze()
+ *
the main analyzation, decompiles the code of the method
+ *
analyzeInners()
+ *
This will analyze method scopes classes by calling their + * analyze() and analyzeInners() + * methods.
+ *
makeDeclaration()
+ *
This will determine when to declare variables. For constructors + * it will do special transformations like field initialization.
+ */ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { + /** + * The import handler where we should register our types. + */ ImportHandler imports; + /** + * The class analyzer of the class that contains this method. + */ ClassAnalyzer classAnalyzer; + /** + * The method info structure for this method. + */ MethodInfo minfo; + /** + * This is the bytecode info structure, or null if this method has + * no code (abstract or native). + */ + BytecodeInfo code; + /** + * The method name. + */ String methodName; + /** + * The type of this method (parameter types + return type). + */ MethodType methodType; + /** + * True, iff this method is a constructor, i.e. methodName == <(cl)?init> + */ boolean isConstructor; + /** + * The exceptions this method may throw. + */ Type[] exceptions; + /** + * If the method is synthetic (access$, class$, etc.), this is the + * synthetic analyzer describing the function of this method, otherwise + * this is null. + */ SyntheticAnalyzer synth; + /** + * This is the first flow block of the method code. If this + * method has no code, this is null. This is initialized at the + * end of the analyze() phase. + */ FlowBlock methodHeader; - BytecodeInfo code; - + /** + * A list of all locals contained in this method. + */ Vector allLocals = new Vector(); + + /** + * This array contains the locals in the parameter list, including + * the implicit this parameter for nonstatic methods. + */ LocalInfo[] param; + /** + * The local variable table containing info about names and types of + * locals. + */ LocalVariableTable lvt; + /** + * True, iff this method is the special constructor, that is generated + * by jikes (constructor$xx) + */ boolean isJikesConstructor; + /** + * True, iff this method is the special constructor, and its first + * parameter is a reference to the outer class. + */ boolean hasJikesOuterValue; + /** + * True, iff this method is an implicit constructor, that may be (or even + * must be for anonymous classes) omitted. + */ boolean isImplicitAnonymousConstructor; + /** + * True, if this method is the special block$ method generated by jikes + * to initialize field members. + */ boolean isJikesBlockInitializer; /** - * This dictionary maps an anonymous ClassInfo to the - * InvokeOperator that creates this class. - */ + * This list contains the InvokeOperator objects in the code of + * this method, that create method scoped classes. */ Vector anonConstructors = new Vector(); - Vector innerAnalyzers; + /** + * This list contains the class analyzers of all method scoped + * classes that should be declared in this method. + */ + Vector innerAnalyzers; + /** + * This is the default constructor. + * @param cla the ClassAnalyzer of the class that contains this method. + * @param minfo the method info structure for this method. + * @param imports the import handler that should be informed about types. + */ public MethodAnalyzer(ClassAnalyzer cla, MethodInfo minfo, ImportHandler imports) { this.classAnalyzer = cla; @@ -119,7 +207,6 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { lvt = new LocalVariableTable(code.getMaxLocals(), localvars); } - initParams(); } String[] excattr = minfo.getExceptions(); if (excattr == null) { @@ -134,46 +221,66 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { synth = new SyntheticAnalyzer(minfo, true); } - - - public void initParams() { - Type[] paramTypes = getType().getParameterTypes(); - int paramCount = (isStatic() ? 0 : 1) + paramTypes.length; - param = new LocalInfo[paramCount]; - int offset = 0; - int slot = 0; - if (!isStatic()) - param[offset++] = getLocalInfo(0, slot++); - for (int i=0; i < paramTypes.length; i++) { - param[offset++] = getLocalInfo(0, slot); - slot += paramTypes[i].stackSize(); - } - } - + /** + * Returns the name of this method. + */ public String getName() { return methodName; } + /** + * Returns the type of this method. + * @return the type of this method. + */ public MethodType getType() { return methodType; } + /** + * Returns the first flow block of the code. + * @return the first flow block of the code. + */ public FlowBlock getMethodHeader() { return methodHeader; } + /** + * Returns the bytecode info for this method. + * @return the bytecode info for this method, or null if it is + * abstract or native. + */ public final BytecodeInfo getBytecodeInfo() { return code; } + /** + * Returns the import handler. The import handler should be informed + * about all types we (or an expression in this method) use, so that + * the corresponding class can be imported. + * @return the import handler. + */ public final ImportHandler getImportHandler() { return imports; } + /** + * Registers a type at the import handler. This should be called + * if an expression needs to print the type name to the code. The + * corresponding class will be imported in that case (if used + * often enough). + * @param type the type that should be registered. + */ public final void useType(Type type) { imports.useType(type); } + /** + * Inserts a structured block to the beginning of the method. + * This is called by transform constructors, to move the super + * call from the real constructor to the constructor$xx method + * (the jikes constructor). + * @param insertBlock the structured block that should be inserted. + */ public void insertStructuredBlock(StructuredBlock insertBlock) { if (methodHeader != null) { insertBlock.setJump(new Jump(FlowBlock.NEXT_BY_ADDR)); @@ -187,48 +294,138 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { } } + /** + * Checks if this method is a constructor, i.e. getName() returns + * "" or "". + * @return true, iff this method is a real constructor. + */ public final boolean isConstructor() { return isConstructor; } + /** + * Checks if this method is static. + * @return true, iff this method is static. + */ public final boolean isStatic() { return minfo.isStatic(); } + /** + * Checks if this method is synthetic, i.e. a synthetic attribute is + * present. + * @return true, iff this method is synthetic. + */ public final boolean isSynthetic() { return minfo.isSynthetic(); } + /** + * Tells if this method is the constructor$xx method generated by jikes. + * @param value true, iff this method is the jikes constructor. + */ public final void setJikesConstructor(boolean value) { isJikesConstructor = value; } + /** + * Tells if this method is the block$xx method generated by jikes. + * @param value true, iff this method is the jikes block initializer. + */ public final void setJikesBlockInitializer(boolean value) { isJikesBlockInitializer = value; } + /** + * Tells if this (constructor$xx) method has as first (implicit) + * parameter the instance of the outer class. + * @param value true, this method has the implicit parameter. + */ public final void setHasOuterValue(boolean value) { hasJikesOuterValue = value; } + /** + * Tells if this constructor can be omited, since it is implicit. + * @param value true, this method is the implicit constructor. + */ public final void setAnonymousConstructor(boolean value) { isImplicitAnonymousConstructor = value; } + /** + * Checks if this constructor can be omited, since it is implicit. + * @return true, this method is the implicit constructor. + */ public final boolean isAnonymousConstructor() { return isImplicitAnonymousConstructor; } + /** + * Get the synthetic analyzer for this method. + * @return the synthetic analyzer, or null if this method isn't + * synthetic. + */ public final SyntheticAnalyzer getSynthetic() { return synth; } + /** + * Get the return type of this method. + */ public Type getReturnType() { return methodType.getReturnType(); } + /** + * Get the class analyzer for the class containing this method. + */ + public ClassAnalyzer getClassAnalyzer() { + return classAnalyzer; + } + + /** + * Get the class info for the class containing this method. + */ + public ClassInfo getClazz() { + return classAnalyzer.clazz; + } + + /** + * Get the local info for a parameter. + * @param nr the index of the parameter (start by zero and + * count the implicit this param for nonstatic method). + * @return the local info for the specified parameter. + * @see #getLocalInfo + */ + public final LocalInfo getParamInfo(int nr) { + return param[nr]; + } + + /** + * Create a local info for a local variable located at an + * instruction with the given address. + * @param addr the address of the instruction using this local. + * the address of the next instruction for stores. + * @param slot the slot, the local variable uses. + * @return a new local info representing that local. + */ + public LocalInfo getLocalInfo(int addr, int slot) { + LocalInfo li = new LocalInfo(this, slot); + if (lvt != null) { + LocalVarEntry entry = lvt.getLocal(slot, addr); + if (entry != null) + li.addHint(entry.getName(), entry.getType()); + } + allLocals.addElement(li); + return li; + } - public void analyzeCode() + /** + * Analyzes the code of this method. This creates the + * flow blocks (including methodHeader) and analyzes them. + */ + private void analyzeCode() { if (GlobalOptions.verboseLevel > 0) GlobalOptions.err.print(methodName+": "); @@ -348,23 +545,30 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { GlobalOptions.err.println(""); } + /** + * This is the first pass of the analyzation. It will analyze the + * code of this method, but not the method scoped classes. + */ public void analyze() throws ClassFormatError { - if (code == null) - return; + Type[] paramTypes = getType().getParameterTypes(); + int paramCount = (isStatic() ? 0 : 1) + paramTypes.length; + param = new LocalInfo[paramCount]; int offset = 0; + int slot = 0; if (!isStatic()) { ClassInfo classInfo = classAnalyzer.getClazz(); - LocalInfo thisLocal = getParamInfo(0); + LocalInfo thisLocal = getLocalInfo(0, slot++); thisLocal.setExpression(new ThisOperator(classInfo, true)); - offset++; + param[offset++] = thisLocal; } - Type[] paramTypes = methodType.getParameterTypes(); for (int i=0; i< paramTypes.length; i++) { - getParamInfo(offset).setType(paramTypes[i]); + param[offset] = getLocalInfo(0, slot); + param[offset].setType(paramTypes[i]); + slot += paramTypes[i].stackSize(); offset++; } @@ -374,33 +578,39 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { if (!isConstructor) imports.useType(methodType.getReturnType()); - analyzeCode(); - } - - public final LocalInfo getParamInfo(int nr) { - return param[nr]; + if (code != null) + analyzeCode(); } + /** + * This is the second pass of the analyzation. It will analyze + * the method scoped classes. + */ public void analyzeInnerClasses() throws ClassFormatError { createAnonymousClasses(); } + /** + * This is the third and last pass of the analyzation. It will analyze + * the types and names of the local variables and where to declare them. + * It will also determine where to declare method scoped local variables. + */ public void makeDeclaration() { if (isConstructor() && !isStatic() && classAnalyzer.outerValues != null && (Decompiler.options & Decompiler.OPTION_CONTRAFO) != 0) { Expression[] outerValues = classAnalyzer.outerValues; for (int i=0; i< outerValues.length; i++) { - LocalInfo local = getParamInfo(1+i); + LocalInfo local = param[1+i]; local.setExpression(outerValues[i]); } } if (isJikesConstructor && hasJikesOuterValue && classAnalyzer.outerValues != null && classAnalyzer.outerValues.length > 0) - getParamInfo(1).setExpression(classAnalyzer.outerValues[0]); + param[1].setExpression(classAnalyzer.outerValues[0]); for (Enumeration enum = allLocals.elements(); enum.hasMoreElements(); ) { @@ -408,17 +618,18 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { if (!li.isShadow()) imports.useType(li.getType()); } - if (code != null) { - for (int i=0; i < param.length; i++) { - param[i].guessName(); - for (int j=0; j < i; j++) { - if (param[j].getName().equals(param[i].getName())) { - /* A name conflict happened. */ - param[i].makeNameUnique(); - break; /* j */ - } + for (int i=0; i < param.length; i++) { + param[i].guessName(); + for (int j=0; j < i; j++) { + if (param[j].getName().equals(param[i].getName())) { + /* A name conflict happened. */ + param[i].makeNameUnique(); + break; /* j */ } } + } + + if (code != null) { methodHeader.makeDeclaration(param); methodHeader.simplify(); } @@ -429,7 +640,7 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { ClassAnalyzer classAna = (ClassAnalyzer) enum.nextElement(); if (classAna.getParent() == this) { Expression[] outerValues = classAna.getOuterValues(); - for (int i=0; i< outerValues.length; i++) { + for (int i=0; i < outerValues.length; i++) { if (outerValues[i] instanceof OuterLocalOperator) { LocalInfo li = ((OuterLocalOperator) outerValues[i]).getLocalInfo(); @@ -443,6 +654,11 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { } } + /** + * Tells if this method is synthetic or implicit or something else, so + * that it doesn't have to be written to the source code. + * @return true, iff it shouldn't be written to the source code. + */ public boolean skipWriting() { if (synth != null) { // We don't need this class anymore (hopefully?) @@ -515,6 +731,11 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { return false; } + /** + * Dumps the source code for this method to the specified writer. + * @param writer the tabbed print writer the code should be written to. + * @exception IOException, if writer throws an exception. + */ public void dumpSource(TabbedPrintWriter writer) throws IOException { @@ -578,31 +799,9 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { writer.print(" " + methodName); } writer.print("("); - Type[] paramTypes = methodType.getParameterTypes(); int offset = skipParams + (isStatic() ? 0 : 1); - int start = skipParams; - - LocalInfo[] param = new LocalInfo[paramTypes.length]; - for (int i=start; istart) + for (int i = offset; i < param.length; i++) { + if (i > offset) writer.print(", "); param[i].dumpDeclaration(writer); } @@ -620,10 +819,7 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { if (code != null) { writer.openBrace(); writer.tab(); - if (methodHeader != null) - methodHeader.dumpSource(writer); - else - writer.println("COULDN'T DECOMPILE METHOD!"); + methodHeader.dumpSource(writer); writer.untab(); writer.closeBrace(); } else @@ -631,28 +827,10 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { writer.popScope(); } - public LocalInfo getLocalInfo(int addr, int slot) { - LocalInfo li = new LocalInfo(this, slot); - if (lvt != null) { - LocalVarEntry entry = lvt.getLocal(slot, addr); - if (entry != null) - li.addHint(entry.getName(), entry.getType()); - } - allLocals.addElement(li); - return li; - } - - public ClassAnalyzer getClassAnalyzer() { - return classAnalyzer; - } - - public ClassInfo getClazz() { - return classAnalyzer.clazz; - } - - /** * Checks if the variable set contains a local with the given name. + * @return the local info the has the given name, or null if it doesn't + * exists. */ public LocalInfo findLocal(String name) { Enumeration enum = allLocals.elements(); @@ -665,7 +843,10 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { } /** - * Checks if an anonymous class with the given name exists. + * Checks if a method scoped class with the given name exists in this + * method (not in a parent method). + * @return the class analyzer with the given name, or null if it + * doesn' exists. */ public ClassAnalyzer findAnonClass(String name) { if (innerAnalyzers != null) { @@ -682,6 +863,11 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { return null; } + /** + * Checks if the specified object lies in this scope. + * @param obj the object. + * @param scopeType the type of this object. + */ public boolean isScopeOf(Object obj, int scopeType) { if (scopeType == METHODSCOPE && obj instanceof ClassInfo) { @@ -692,6 +878,12 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { return false; } + /** + * Checks if the specified name conflicts with an object in this scope. + * @param name the name to check. + * @param scopeType the usage type of this name, AMBIGUOUSNAME if it is + * ambiguous. + */ public boolean conflicts(String name, int usageType) { if (usageType == AMBIGUOUSNAME || usageType == LOCALNAME) return findLocal(name) != null; @@ -700,11 +892,21 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { return false; } + /** + * Gets the parent scope, i.e. the class analyzer for the class containing + * this method. + * @XXX needed? + */ public ClassDeclarer getParent() { return getClassAnalyzer(); } - + /** + * Registers an anonymous constructor invokation. This should be called + * in the analyze or analyzeInner pass by invoke subexpressions. + * @param cop the constructor invokation, that creates the method scoped + * class. + */ public void addAnonymousConstructor(InvokeOperator cop) { anonConstructors.addElement(cop); }