From 6a1af4e3ed7791fb6d1e85b601c02434b422af87 Mon Sep 17 00:00:00 2001 From: jochen Date: Sat, 26 Jun 1999 16:08:20 +0000 Subject: [PATCH] CodeAnalyzer merged into MethodAnalyzer hintTypes, e.g. indexOf takes an int, but should be char insert necessary widening casts for overloaded methods git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@891 379699f6-c40d-0410-875b-85095c16579e --- jode/jode/expr/InvokeOperator.java | 324 ++++++++++++++++++++++++----- 1 file changed, 273 insertions(+), 51 deletions(-) diff --git a/jode/jode/expr/InvokeOperator.java b/jode/jode/expr/InvokeOperator.java index 6091c1c..2e439cc 100644 --- a/jode/jode/expr/InvokeOperator.java +++ b/jode/jode/expr/InvokeOperator.java @@ -21,7 +21,7 @@ package jode.expr; import java.lang.reflect.Modifier; import jode.Decompiler; -import jode.decompiler.CodeAnalyzer; +import jode.decompiler.MethodAnalyzer; import jode.decompiler.MethodAnalyzer; import jode.decompiler.ClassAnalyzer; import jode.decompiler.TabbedPrintWriter; @@ -29,31 +29,120 @@ import jode.GlobalOptions; import jode.bytecode.*; import jode.jvm.*; import jode.type.*; -import java.lang.reflect.InvocationTargetException; import jode.decompiler.Scope; +import java.lang.reflect.InvocationTargetException; +import java.util.Hashtable; + public final class InvokeOperator extends Operator implements MatchableOperator { - CodeAnalyzer codeAnalyzer; + MethodAnalyzer methodAnalyzer; boolean staticFlag; boolean specialFlag; MethodType methodType; String methodName; Type classType; + Type[] hints; + + private final static Hashtable hintTypes = new Hashtable(); + + static { + /* Fill the hint type hashtable. For example, the first + * parameter of String.indexOf should be hinted as char, even + * though the formal parameter is an int. + * First hint is hint of return value (even if void) + * other hints are that of the parameters in order + */ + Type tCharHint = new IntegerType(IntegerType.IT_I, IntegerType.IT_C); + Type[] hintC = new Type[] { tCharHint }; + Type[] hint0C = new Type[] { null, tCharHint }; + Type[] hint0C0 = new Type[] { null, tCharHint, null }; + hintTypes.put(Reference.getReference + ("Ljava/lang/String;", "indexOf", "(I)I"), + hint0C); + hintTypes.put(Reference.getReference + ("Ljava/lang/String;", "indexOf", "(II)I"), + hint0C0); + hintTypes.put(Reference.getReference + ("Ljava/lang/String;", "lastIndexOf", "(I)I"), + hint0C); + hintTypes.put(Reference.getReference + ("Ljava/lang/String;", "lastIndexOf", "(II)I"), + hint0C0); + hintTypes.put(Reference.getReference + ("Ljava/io/Writer;", "write", "(I)V"), + hint0C); + hintTypes.put(Reference.getReference + ("Ljava/io/BufferedWriter;", "write", "(I)V"), + hint0C); + hintTypes.put(Reference.getReference + ("Ljava/io/CharArrayWriter;", "write", "(I)V"), + hint0C); + hintTypes.put(Reference.getReference + ("Ljava/io/FilterWriter;", "write", "(I)V"), + hint0C); + hintTypes.put(Reference.getReference + ("Ljava/io/OutputStreamWriter;", "write", "(I)V"), + hint0C); + hintTypes.put(Reference.getReference + ("Ljava/io/PipedWriter;", "write", "(I)V"), + hint0C); + hintTypes.put(Reference.getReference + ("Ljava/io/PrintWriter;", "write", "(I)V"), + hint0C); + hintTypes.put(Reference.getReference + ("Ljava/io/StringWriter;", "write", "(I)V"), + hint0C); + hintTypes.put(Reference.getReference + ("Ljava/io/PushbackReader;", "unread", "(I)V"), + hint0C); + hintTypes.put(Reference.getReference + ("Ljava/lang/Reader;", "read", "()I"), + hintC); + hintTypes.put(Reference.getReference + ("Ljava/lang/BufferedReader;", "read", "()I"), + hintC); + hintTypes.put(Reference.getReference + ("Ljava/lang/CharArrayReader;", "read", "()I"), + hintC); + hintTypes.put(Reference.getReference + ("Ljava/lang/FilterReader;", "read", "()I"), + hintC); + hintTypes.put(Reference.getReference + ("Ljava/lang/InputStreamReader;", "read", "()I"), + hintC); + hintTypes.put(Reference.getReference + ("Ljava/lang/LineNumberReader;", "read", "()I"), + hintC); + hintTypes.put(Reference.getReference + ("Ljava/lang/PipedReader;", "read", "()I"), + hintC); + hintTypes.put(Reference.getReference + ("Ljava/lang/PushBackReader;", "read", "()I"), + hintC); + hintTypes.put(Reference.getReference + ("Ljava/lang/StringReader;", "read", "()I"), + hintC); + } - public InvokeOperator(CodeAnalyzer codeAnalyzer, + + public InvokeOperator(MethodAnalyzer methodAnalyzer, boolean staticFlag, boolean specialFlag, Reference reference) { super(Type.tUnknown, 0); this.methodType = Type.tMethod(reference.getType()); this.methodName = reference.getName(); this.classType = Type.tType(reference.getClazz()); - this.type = methodType.getReturnType(); - this.codeAnalyzer = codeAnalyzer; + this.hints = (Type[]) hintTypes.get(reference); + if (hints != null && hints[0] != null) + this.type = hints[0]; + else + this.type = methodType.getReturnType(); + this.methodAnalyzer = methodAnalyzer; this.staticFlag = staticFlag; this.specialFlag = specialFlag; if (staticFlag) - codeAnalyzer.useType(classType); + methodAnalyzer.useType(classType); initOperands((staticFlag ? 0 : 1) + methodType.getParameterTypes().length); } @@ -84,9 +173,13 @@ public final class InvokeOperator extends Operator subExpressions[offset++].setType(Type.tSubType(getClassType())); } Type[] paramTypes = methodType.getParameterTypes(); - for (int i=0; i < paramTypes.length; i++) - subExpressions[offset++].setType(Type.tSubType(paramTypes[i])); + for (int i=0; i < paramTypes.length; i++) { + Type pType = (hints != null && hints[i+1] != null) + ? hints[i+1] : paramTypes[i]; + subExpressions[offset++].setType(Type.tSubType(pType)); + } } + public void updateType() { } @@ -106,13 +199,12 @@ public final class InvokeOperator extends Operator * allow super class */ public boolean isThis() { - return getClassInfo() == codeAnalyzer.getClazz(); + return getClassInfo() == methodAnalyzer.getClazz(); } - public InnerClassInfo getOuterClassInfo() { - ClassInfo clazz = getClassInfo(); - if (clazz != null) { - InnerClassInfo[] outers = clazz.getOuterClasses(); + public InnerClassInfo getOuterClassInfo(ClassInfo ci) { + if (ci != null) { + InnerClassInfo[] outers = ci.getOuterClasses(); if (outers != null) return outers[0]; } @@ -126,15 +218,15 @@ public final class InvokeOperator extends Operator public boolean isOuter() { if (classType instanceof ClassInterfacesType) { ClassInfo clazz = ((ClassInterfacesType) classType).getClassInfo(); - ClassAnalyzer ana = codeAnalyzer.getClassAnalyzer(); + ClassAnalyzer ana = methodAnalyzer.getClassAnalyzer(); while (true) { if (clazz == ana.getClazz()) return true; if (ana.getParent() == null) break; - if (ana.getParent() instanceof CodeAnalyzer + if (ana.getParent() instanceof MethodAnalyzer && (Decompiler.options & Decompiler.OPTION_ANON) != 0) - ana = ((CodeAnalyzer) ana.getParent()) + ana = ((MethodAnalyzer) ana.getParent()) .getClassAnalyzer(); else if (ana.getParent() instanceof ClassAnalyzer && (Decompiler.options @@ -150,7 +242,7 @@ public final class InvokeOperator extends Operator public MethodAnalyzer getMethodAnalyzer() { if (classType instanceof ClassInterfacesType) { - ClassAnalyzer ana = codeAnalyzer.getClassAnalyzer(); + ClassAnalyzer ana = methodAnalyzer.getClassAnalyzer(); while (true) { if (((ClassInterfacesType) classType).getClassInfo() == ana.getClazz()) { @@ -158,8 +250,8 @@ public final class InvokeOperator extends Operator } if (ana.getParent() == null) return null; - if (ana.getParent() instanceof CodeAnalyzer) - ana = ((CodeAnalyzer) ana.getParent()) + if (ana.getParent() instanceof MethodAnalyzer) + ana = ((MethodAnalyzer) ana.getParent()) .getClassAnalyzer(); else if (ana.getParent() instanceof ClassAnalyzer) ana = (ClassAnalyzer) ana.getParent(); @@ -177,7 +269,7 @@ public final class InvokeOperator extends Operator public boolean isSuperOrThis() { if (classType instanceof ClassInterfacesType) { return ((ClassInterfacesType) classType).getClassInfo() - .superClassOf(codeAnalyzer.getClazz()); + .superClassOf(methodAnalyzer.getClazz()); } return false; } @@ -210,10 +302,10 @@ public final class InvokeOperator extends Operator Object cls, Object[] params) throws InterpreterException, InvocationTargetException { if (ref.getClazz().equals - ("L"+codeAnalyzer.getClazz().getName().replace('.','/')+";")) { + ("L"+methodAnalyzer.getClazz().getName().replace('.','/')+";")) { MethodType mt = (MethodType) Type.tType(ref.getType()); - BytecodeInfo info = codeAnalyzer.getClassAnalyzer() - .getMethod(ref.getName(), mt).getCode().getBytecodeInfo(); + BytecodeInfo info = methodAnalyzer.getClassAnalyzer() + .getMethod(ref.getName(), mt).getBytecodeInfo(); Value[] locals = new Value[info.getMaxLocals()]; for (int i=0; i< locals.length; i++) locals[i] = new Value(); @@ -232,12 +324,12 @@ public final class InvokeOperator extends Operator } public ConstOperator deobfuscateString(ConstOperator op) { - ClassAnalyzer clazz = codeAnalyzer.getClassAnalyzer(); - CodeAnalyzer ca = clazz.getMethod(methodName, methodType).getCode(); - if (ca == null) + ClassAnalyzer clazz = methodAnalyzer.getClassAnalyzer(); + MethodAnalyzer ma = clazz.getMethod(methodName, methodType); + if (ma == null) return null; Environment env = new Environment(); - BytecodeInfo info = ca.getBytecodeInfo(); + BytecodeInfo info = ma.getBytecodeInfo(); Value[] locals = new Value[info.getMaxLocals()]; for (int i=0; i< locals.length; i++) locals[i] = new Value(); @@ -265,23 +357,33 @@ public final class InvokeOperator extends Operator && getMethodName().equals("append") && getMethodType().getParameterTypes().length == 1) { - Expression e = subExpressions[0].simplifyStringBuffer(); - if (e == null) + Expression firstOp = subExpressions[0].simplifyStringBuffer(); + if (firstOp == null) return null; subExpressions[1] = subExpressions[1].simplifyString(); - if (e == EMPTYSTRING + if (firstOp == EMPTYSTRING && subExpressions[1].getType().isOfType(Type.tString)) return subExpressions[1]; - if (e instanceof StringAddOperator - && ((Operator)e).getSubExpressions()[0] == EMPTYSTRING) - e = ((Operator)e).getSubExpressions()[1]; - + if (firstOp instanceof StringAddOperator + && ((Operator)firstOp).getSubExpressions()[0] == EMPTYSTRING) + firstOp = ((Operator)firstOp).getSubExpressions()[1]; + + Expression secondOp = subExpressions[1]; + Type[] paramTypes = new Type[] { + Type.tStringBuffer, secondOp.getType().getCanonic() + }; + if (needsCast(1, paramTypes)) { + Type castType = methodType.getParameterTypes()[0]; + Operator castOp = new ConvertOperator(castType, castType); + castOp.addOperand(secondOp); + secondOp = castOp; + } Operator result = new StringAddOperator(); - result.addOperand(subExpressions[1]); - result.addOperand(e); + result.addOperand(secondOp); + result.addOperand(firstOp); return result; } return null; @@ -347,29 +449,29 @@ public final class InvokeOperator extends Operator Operator op = null; switch (synth.getKind()) { case SyntheticAnalyzer.ACCESSGETFIELD: - op = new GetFieldOperator(codeAnalyzer, false, + op = new GetFieldOperator(methodAnalyzer, false, synth.getReference()); break; case SyntheticAnalyzer.ACCESSGETSTATIC: - op = new GetFieldOperator(codeAnalyzer, true, + op = new GetFieldOperator(methodAnalyzer, true, synth.getReference()); break; case SyntheticAnalyzer.ACCESSPUTFIELD: op = new StoreInstruction - (new PutFieldOperator(codeAnalyzer, false, + (new PutFieldOperator(methodAnalyzer, false, synth.getReference())); break; case SyntheticAnalyzer.ACCESSPUTSTATIC: op = new StoreInstruction - (new PutFieldOperator(codeAnalyzer, true, + (new PutFieldOperator(methodAnalyzer, true, synth.getReference())); break; case SyntheticAnalyzer.ACCESSMETHOD: - op = new InvokeOperator(codeAnalyzer, false, + op = new InvokeOperator(methodAnalyzer, false, false, synth.getReference()); break; case SyntheticAnalyzer.ACCESSSTATICMETHOD: - op = new InvokeOperator(codeAnalyzer, true, + op = new InvokeOperator(methodAnalyzer, true, false, synth.getReference()); break; } @@ -386,6 +488,64 @@ public final class InvokeOperator extends Operator return null; } + public boolean needsCast(int param, Type[] paramTypes) { + Type realClassType; + if (staticFlag) + realClassType = classType; + else { + if (param == 0) + return paramTypes[0] instanceof NullType; + realClassType = paramTypes[0]; + } + + if (!(realClassType instanceof ClassInterfacesType)) { + /* Arrays don't have overloaded methods, all okay */ + return false; + } + ClassInfo clazz = ((ClassInterfacesType) realClassType).getClassInfo(); + int offset = staticFlag ? 0 : 1; + + Type[] myParamTypes = methodType.getParameterTypes(); + if (myParamTypes[param-offset].equals(paramTypes[param])) { + /* Type at param is okay. */ + return false; + } + /* Now check if there is a conflicting method in this class or + * a superclass. */ + while (clazz != null) { + MethodInfo[] methods = clazz.getMethods(); + next_method: + for (int i=0; i< methods.length; i++) { + if (!methods[i].getName().equals(methodName)) + /* method name doesn't match*/ + continue next_method; + + Type[] otherParamTypes + = Type.tMethod(methods[i].getType()).getParameterTypes(); + if (otherParamTypes.length != myParamTypes.length) { + /* parameter count doesn't match*/ + continue next_method; + } + + if (myParamTypes[param-offset].isOfType + (Type.tSubType(otherParamTypes[param-offset]))) { + /* cast to myParamTypes cannot resolve any conflicts. */ + continue next_method; + } + for (int p = offset; p < paramTypes.length; p++) { + if (!paramTypes[p] + .isOfType(Type.tSubType(otherParamTypes[p-offset]))) + /* No conflict here */ + continue next_method; + } + /* There is a conflict that can be resolved by a cast. */ + return true; + } + clazz = clazz.getSuperclass(); + } + return false; + } + public Expression simplify() { Expression expr = simplifyAccess(); if (expr != null) @@ -406,15 +566,65 @@ public final class InvokeOperator extends Operator boolean opIsThis = !staticFlag && subExpressions[0] instanceof ThisOperator; int arg = 1; + int length = subExpressions.length; + ClassInfo clazz = getClassInfo(); + InnerClassInfo outer = getOuterClassInfo(clazz); + + Type[] paramTypes = new Type[subExpressions.length]; + for (int i=0; i< subExpressions.length; i++) + paramTypes[i] = subExpressions[i].getType().getCanonic(); if (isConstructor()) { - InnerClassInfo outer = getOuterClassInfo(); + boolean jikesAnonymousInner = false; + if ((Decompiler.options & + (Decompiler.OPTION_ANON | Decompiler.OPTION_CONTRAFO)) != 0 + && outer != null + && (outer.outer == null || outer.name == null)) { + ClassAnalyzer anonymousClass = methodAnalyzer.getClassAnalyzer(clazz); + jikesAnonymousInner = anonymousClass.isJikesAnonymousInner(); + + if (outer.name == null) { + writer.print("SUPER IS ANONYMOUS?"); + outer = null; + } + /* XXX check outerValues */ + arg += anonymousClass.getOuterValues().length; + } + if (outer != null && outer.outer != null && outer.name != null && !Modifier.isStatic(outer.modifiers) && (Decompiler.options & Decompiler.OPTION_INNER) != 0) { - Expression outerInstance = subExpressions[arg++]; - if (!(outerInstance instanceof ThisOperator)) { - outerInstance.dumpExpression(writer, 0); + Expression outerExpr = jikesAnonymousInner + ? subExpressions[--length] + : subExpressions[arg++]; + if (outerExpr instanceof CheckNullOperator) { + CheckNullOperator cno = (CheckNullOperator) outerExpr; + outerExpr = cno.subExpressions[0]; + } else if (!(outerExpr instanceof ThisOperator)) { + if (!jikesAnonymousInner) + // Bug in jikes: it doesn't do a check null. + // We don't complain here. + writer.print("MISSING CHECKNULL "); + } + + if (outerExpr instanceof ThisOperator) { + Scope scope = writer.getScope + (((ThisOperator) outerExpr).getClassInfo(), + Scope.CLASSSCOPE); + if (writer.conflicts(outer.name, scope, Scope.CLASSNAME)) { + outerExpr.dumpExpression(writer, 950); + writer.print("."); + } + } else { + if (outerExpr.getType() instanceof NullType) { + writer.print("(("); + writer.printType(Type.tClass + (ClassInfo.forName(outer.outer))); + writer.print(") "); + outerExpr.dumpExpression(writer, 700); + writer.print(")"); + } else + outerExpr.dumpExpression(writer, 950); writer.print("."); } } @@ -422,7 +632,7 @@ public final class InvokeOperator extends Operator if (specialFlag) { if (opIsThis && (((ThisOperator)subExpressions[0]).getClassInfo() - == codeAnalyzer.getClazz())) { + == methodAnalyzer.getClazz())) { if (isThis()) { /* XXX check if this is a private or final method. */ } else { @@ -438,6 +648,7 @@ public final class InvokeOperator extends Operator writer.print("(NON VIRTUAL "); writer.printType(classType); writer.print(")"); + paramTypes[0] = classType; minPriority = 700; } subExpressions[0].dumpExpression(writer, minPriority); @@ -468,12 +679,13 @@ public final class InvokeOperator extends Operator writer.print("this."); } } else { - if (subExpressions[0].getType() instanceof NullType) { + if (needsCast(0, paramTypes)){ writer.print("(("); writer.printType(classType); writer.print(") "); subExpressions[0].dumpExpression(writer, 700); writer.print(")"); + paramTypes[0] = classType; } else subExpressions[0].dumpExpression(writer, 950); } @@ -489,12 +701,22 @@ public final class InvokeOperator extends Operator } writer.print("("); boolean first = true; - while (arg < subExpressions.length) { + int offset = staticFlag ? 0 : 1; + while (arg < length) { if (!first) writer.print(", "); else first = false; - subExpressions[arg++].dumpExpression(writer, 0); + int priority = 0; + if (needsCast(arg, paramTypes)) { + Type castType = methodType.getParameterTypes()[arg-offset]; + writer.print("("); + writer.printType(castType); + writer.print(")"); + paramTypes[arg] = castType; + priority = 700; + } + subExpressions[arg++].dumpExpression(writer, priority); } writer.print(")"); }