0001: /* InvokeOperator 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: InvokeOperator.java.in,v 4.10.2.8 2002/05/28 17:34:06 hoenicke Exp $
0018: */
0019:
0020: package jode.expr;
0021:
0022: import java.lang.reflect.Modifier;
0023:
0024: import jode.decompiler.MethodAnalyzer;
0025: import jode.decompiler.MethodAnalyzer;
0026: import jode.decompiler.ClassAnalyzer;
0027: import jode.decompiler.TabbedPrintWriter;
0028: import jode.decompiler.Options;
0029: import jode.decompiler.OuterValues;
0030: import jode.decompiler.Scope;
0031: import jode.GlobalOptions;
0032: import jode.bytecode.*;
0033: import jode.jvm.*;
0034: import jode.type.*;
0035: import jode.util.SimpleMap;
0036:
0037: import java.lang.reflect.InvocationTargetException;
0038: import java.util.Hashtable;
0039: import java.util.Collections;
0040: import java.util.Collection;
0041: import java.util.Map;
0042: import java.util.Iterator;
0043: import java.util.Set;
0044:
0045: public final class InvokeOperator extends Operator implements
0046: MatchableOperator {
0047:
0048: public final static int VIRTUAL = 0;
0049: public final static int SPECIAL = 1;
0050: public final static int STATIC = 2;
0051: public final static int CONSTRUCTOR = 3;
0052: public final static int ACCESSSPECIAL = 4;
0053:
0054: /**
0055: * The methodAnalyzer of the method, that contains this invocation.
0056: * This is not the method that we should call.
0057: */
0058: MethodAnalyzer methodAnalyzer;
0059: int methodFlag;
0060: MethodType methodType;
0061: String methodName;
0062: Reference ref;
0063: int skippedArgs;
0064: Type classType;
0065: Type[] hints;
0066:
0067: /**
0068: * This hashtable contains hints for every library method. Some
0069: * library method take or return an int, but it should be a char
0070: * instead. We will remember that here to give them the right
0071: * hint.
0072: *
0073: * The key is the string: methodName + "." + methodType, the value
0074: * is a map: It maps base class types for which this hint applies,
0075: * to an array of hint types corresponding to the parameters: The
0076: * first element is the hint type of the return value, the
0077: * remaining entries are the hint types of the parameters. All
0078: * hint types may be null, if that parameter shouldn't be hinted.
0079: */
0080: private final static Hashtable hintTypes = new Hashtable();
0081:
0082: static {
0083: /* Fill the hint type hashtable. For example, the first
0084: * parameter of String.indexOf should be hinted as char, even
0085: * though the formal parameter is an int.
0086: * First hint is hint of return value (even if void)
0087: * other hints are that of the parameters in order
0088: *
0089: * You only have to hint the base class. Other classes will
0090: * inherit the hints.
0091: *
0092: * We reuse a lot of objects, since they are all unchangeable
0093: * this is no problem. We only hint for chars; it doesn't
0094: * make much sense to hint for byte, since its constant
0095: * representation is more difficult than an int
0096: * representation. If you have more hints to suggest, please
0097: * write contact me. (see GlobalOptions.EMAIL)
0098: */
0099: Type tCharHint = new IntegerType(IntegerType.IT_I,
0100: IntegerType.IT_C);
0101: Type[] hintC = new Type[] { tCharHint };
0102: Type[] hint0C = new Type[] { null, tCharHint };
0103: Type[] hint0C0 = new Type[] { null, tCharHint, null };
0104:
0105: Map hintString0CMap = new SimpleMap(Collections
0106: .singleton(new SimpleMap.SimpleEntry(Type.tString,
0107: hint0C)));
0108: Map hintString0C0Map = new SimpleMap(Collections
0109: .singleton(new SimpleMap.SimpleEntry(Type.tString,
0110: hint0C0)));
0111: hintTypes.put("indexOf.(I)I", hintString0CMap);
0112: hintTypes.put("lastIndexOf.(I)I", hintString0CMap);
0113: hintTypes.put("indexOf.(II)I", hintString0C0Map);
0114: hintTypes.put("lastIndexOf.(II)I", hintString0C0Map);
0115: hintTypes.put("write.(I)V", new SimpleMap(Collections
0116: .singleton(new SimpleMap.SimpleEntry(Type
0117: .tClass("java.io.Writer"), hint0C))));
0118: hintTypes.put("read.()I", new SimpleMap(Collections
0119: .singleton(new SimpleMap.SimpleEntry(Type
0120: .tClass("java.io.Reader"), hintC))));
0121: hintTypes.put("unread.(I)V", new SimpleMap(Collections
0122: .singleton(new SimpleMap.SimpleEntry(Type
0123: .tClass("java.io.PushbackReader"), hint0C))));
0124: }
0125:
0126: public InvokeOperator(MethodAnalyzer methodAnalyzer,
0127: int methodFlag, Reference reference) {
0128: super (Type.tUnknown, 0);
0129: this .ref = reference;
0130: this .methodType = Type.tMethod(reference.getType());
0131: this .methodName = reference.getName();
0132: this .classType = Type.tType(reference.getClazz());
0133: this .hints = null;
0134: Map allHints = (Map) hintTypes.get(methodName + "."
0135: + methodType);
0136: if (allHints != null) {
0137: for (Iterator i = allHints.entrySet().iterator(); i
0138: .hasNext();) {
0139: Map.Entry e = (Map.Entry) i.next();
0140: if (classType
0141: .isOfType(((Type) e.getKey()).getSubType())) {
0142: this .hints = (Type[]) e.getValue();
0143: break;
0144: }
0145: }
0146: }
0147: if (hints != null && hints[0] != null)
0148: this .type = hints[0];
0149: else
0150: this .type = methodType.getReturnType();
0151: this .methodAnalyzer = methodAnalyzer;
0152: this .methodFlag = methodFlag;
0153: if (methodFlag == STATIC)
0154: methodAnalyzer.useType(classType);
0155: skippedArgs = (methodFlag == STATIC ? 0 : 1);
0156: initOperands(skippedArgs
0157: + methodType.getParameterTypes().length);
0158: checkAnonymousClasses();
0159: }
0160:
0161: public final boolean isStatic() {
0162: return methodFlag == STATIC;
0163: }
0164:
0165: public MethodType getMethodType() {
0166: return methodType;
0167: }
0168:
0169: public String getMethodName() {
0170: return methodName;
0171: }
0172:
0173: private static MethodInfo getMethodInfo(ClassInfo clazz,
0174: String name, String type) {
0175: while (clazz != null) {
0176: MethodInfo method = clazz.findMethod(name, type);
0177: if (method != null)
0178: return method;
0179: clazz = clazz.getSuperclass();
0180: }
0181: return null;
0182: }
0183:
0184: public MethodInfo getMethodInfo() {
0185: ClassInfo clazz;
0186: if (ref.getClazz().charAt(0) == '[')
0187: clazz = ClassInfo.javaLangObject;
0188: else
0189: clazz = TypeSignature.getClassInfo(ref.getClazz());
0190: return getMethodInfo(clazz, ref.getName(), ref.getType());
0191: }
0192:
0193: public Type getClassType() {
0194: return classType;
0195: }
0196:
0197: public int getPriority() {
0198: return 950;
0199: }
0200:
0201: public void checkAnonymousClasses() {
0202: if (methodFlag != CONSTRUCTOR
0203: || (Options.options & Options.OPTION_ANON) == 0)
0204: return;
0205: InnerClassInfo outer = getOuterClassInfo(getClassInfo());
0206: if (outer != null
0207: && (outer.outer == null || outer.name == null)) {
0208: methodAnalyzer.addAnonymousConstructor(this );
0209: }
0210: }
0211:
0212: public void updateSubTypes() {
0213: int offset = 0;
0214: if (!isStatic()) {
0215: subExpressions[offset++].setType(Type
0216: .tSubType(getClassType()));
0217: }
0218: Type[] paramTypes = methodType.getParameterTypes();
0219: for (int i = 0; i < paramTypes.length; i++) {
0220: Type pType = (hints != null && hints[i + 1] != null) ? hints[i + 1]
0221: : paramTypes[i];
0222: subExpressions[offset++].setType(Type.tSubType(pType));
0223: }
0224: }
0225:
0226: public void updateType() {
0227: }
0228:
0229: /**
0230: * Makes a non void expression, in case this is a constructor.
0231: */
0232: public void makeNonVoid() {
0233: if (type != Type.tVoid)
0234: throw new jode.AssertError("already non void");
0235: ClassInfo clazz = getClassInfo();
0236: InnerClassInfo outer = getOuterClassInfo(clazz);
0237: if (outer != null && outer.name == null) {
0238: /* This is an anonymous class */
0239: if (clazz.getInterfaces().length > 0)
0240: type = Type.tClass(clazz.getInterfaces()[0]);
0241: else
0242: type = Type.tClass(clazz.getSuperclass());
0243: } else
0244: type = subExpressions[0].getType();
0245: }
0246:
0247: public boolean isConstructor() {
0248: return methodFlag == CONSTRUCTOR;
0249: }
0250:
0251: public ClassInfo getClassInfo() {
0252: if (classType instanceof ClassInterfacesType)
0253: return ((ClassInterfacesType) classType).getClassInfo();
0254: return null;
0255: }
0256:
0257: /**
0258: * Checks, whether this is a call of a method from this class.
0259: */
0260: public boolean isThis() {
0261: return getClassInfo() == methodAnalyzer.getClazz();
0262: }
0263:
0264: public InnerClassInfo getOuterClassInfo(ClassInfo ci) {
0265: if (ci != null) {
0266: InnerClassInfo[] outers = ci.getOuterClasses();
0267: if (outers != null)
0268: return outers[0];
0269: }
0270: return null;
0271: }
0272:
0273: /**
0274: * Tries to locate the class analyzer for the callee class. This
0275: * is mainly useful for inner and anonymous classes.
0276: *
0277: * @return The class analyzer, if the callee class is declared
0278: * inside the same base class as the caller class, null otherwise.
0279: */
0280: public ClassAnalyzer getClassAnalyzer() {
0281: if ((Options.options & (Options.OPTION_ANON | Options.OPTION_INNER)) == 0)
0282: return null;
0283:
0284: ClassInfo callee = getClassInfo();
0285: if (callee == null)
0286: return null;
0287:
0288: int nested = 0;
0289: InnerClassInfo[] outers = callee.getOuterClasses();
0290: if ((Options.options & Options.OPTION_INNER) != 0
0291: && outers != null) {
0292: /* If the callee class is an inner class we take its
0293: * (outermost) parent instead. This will assure that we
0294: * find the callee class with one inner -> outer pass.
0295: */
0296: nested = outers.length;
0297: if (outers[nested - 1].outer == null
0298: || outers[nested - 1].name == null)
0299: nested--;
0300:
0301: if (nested > 0)
0302: callee = ClassInfo.forName(outers[nested - 1].outer);
0303: }
0304:
0305: /* First check if it is an inner class */
0306: ClassAnalyzer ana = methodAnalyzer.getClassAnalyzer(callee);
0307:
0308: if (ana == null) {
0309: /* Now we iterate the caller analyzer queue to find the class
0310: * analyzer for callee
0311: */
0312: ana = methodAnalyzer.getClassAnalyzer();
0313: while (callee != ana.getClazz()) {
0314: if (ana.getParent() == null)
0315: return null;
0316: if (ana.getParent() instanceof MethodAnalyzer
0317: && (Options.options & Options.OPTION_ANON) != 0)
0318: ana = ((MethodAnalyzer) ana.getParent())
0319: .getClassAnalyzer();
0320: else if (ana.getParent() instanceof ClassAnalyzer
0321: && (Options.options & Options.OPTION_INNER) != 0)
0322: ana = (ClassAnalyzer) ana.getParent();
0323: else
0324: throw new jode.AssertError("Unknown parent: " + ana
0325: + ": " + ana.getParent());
0326: }
0327: }
0328:
0329: /* Now get the ClassAnalyzer of the real callee */
0330: while (nested > 0) {
0331: nested--;
0332: ana = ana.getInnerClassAnalyzer(outers[nested].name);
0333: if (ana == null)
0334: return null;
0335: }
0336: return ana;
0337: }
0338:
0339: /**
0340: * Checks, whether this is a call of a method from this class or an
0341: * outer instance.
0342: */
0343: public boolean isOuter() {
0344: if (classType instanceof ClassInterfacesType) {
0345: ClassInfo clazz = ((ClassInterfacesType) classType)
0346: .getClassInfo();
0347: ClassAnalyzer ana = methodAnalyzer.getClassAnalyzer();
0348: while (true) {
0349: if (clazz == ana.getClazz())
0350: return true;
0351: if (ana.getParent() == null)
0352: break;
0353: if (ana.getParent() instanceof MethodAnalyzer
0354: && (Options.options & Options.OPTION_ANON) != 0)
0355: ana = ((MethodAnalyzer) ana.getParent())
0356: .getClassAnalyzer();
0357: else if (ana.getParent() instanceof ClassAnalyzer
0358: && (Options.options & Options.OPTION_INNER) != 0)
0359: ana = (ClassAnalyzer) ana.getParent();
0360: else
0361: throw new jode.AssertError("Unknown parent: " + ana
0362: + ": " + ana.getParent());
0363: }
0364: }
0365: return false;
0366: }
0367:
0368: /**
0369: * Tries to locate the method analyzer for the callee. This
0370: * is mainly useful for inner and anonymous classes.
0371: *
0372: * @return The method analyzer, if the callee is declared
0373: * inside the same base class as the caller class, null otherwise.
0374: */
0375: public MethodAnalyzer getMethodAnalyzer() {
0376: ClassAnalyzer ana = getClassAnalyzer();
0377: if (ana == null)
0378: return null;
0379: return ana.getMethod(methodName, methodType);
0380: }
0381:
0382: /**
0383: * Checks, whether this is a call of a method from the super class.
0384: * @XXX check, if its the first super class that implements the method.
0385: */
0386: public boolean isSuperOrThis() {
0387: ClassInfo clazz = getClassInfo();
0388: if (clazz != null) {
0389: return clazz.super ClassOf(methodAnalyzer.getClazz());
0390: }
0391: return false;
0392: }
0393:
0394: public boolean isConstant() {
0395: if ((Options.options & Options.OPTION_ANON) == 0)
0396: return super .isConstant();
0397:
0398: ClassInfo clazz = getClassInfo();
0399: InnerClassInfo outer = getOuterClassInfo(clazz);
0400: ClassAnalyzer clazzAna = methodAnalyzer.getClassAnalyzer(clazz);
0401: if (clazzAna != null && outer != null && outer.outer == null
0402: && outer.name != null
0403: && clazzAna.getParent() == methodAnalyzer) {
0404: /* This is a named method scope class, it needs
0405: * declaration. And therefore can't be moved into
0406: * a field initializer. */
0407: return false;
0408: }
0409: return super .isConstant();
0410: }
0411:
0412: /**
0413: * Checks if the value of the operator can be changed by this expression.
0414: */
0415: public boolean matches(Operator loadop) {
0416: return (loadop instanceof InvokeOperator || loadop instanceof GetFieldOperator);
0417: }
0418:
0419: /**
0420: * Checks if the method is the magic class$ method.
0421: * @return true if this is the magic class$ method, false otherwise.
0422: */
0423: public boolean isGetClass() {
0424: MethodAnalyzer mana = getMethodAnalyzer();
0425: if (mana == null)
0426: return false;
0427: SyntheticAnalyzer synth = getMethodAnalyzer().getSynthetic();
0428: return (synth != null && synth.getKind() == SyntheticAnalyzer.GETCLASS);
0429: }
0430:
0431: class Environment extends SimpleRuntimeEnvironment {
0432:
0433: Interpreter interpreter;
0434: String classSig;
0435:
0436: public Environment(String interpretedClassSig) {
0437: classSig = interpretedClassSig.intern();
0438: }
0439:
0440: public Object invokeMethod(Reference ref, boolean isVirtual,
0441: Object cls, Object[] params)
0442: throws InterpreterException, InvocationTargetException {
0443: if (cls == null && ref.getClazz().equals(classSig)) {
0444: String clazzName = ref.getClazz();
0445: clazzName = clazzName.substring(1,
0446: ref.getClazz().length() - 1).replace('/', '.');
0447: BytecodeInfo info = ClassInfo.forName(clazzName)
0448: .findMethod(ref.getName(), ref.getType())
0449: .getBytecode();
0450: if (info != null)
0451: return interpreter.interpretMethod(info, null,
0452: params);
0453: throw new InterpreterException(
0454: "Can't interpret static native method: " + ref);
0455: } else
0456: return super .invokeMethod(ref, isVirtual, cls, params);
0457: }
0458: }
0459:
0460: public ConstOperator deobfuscateString(ConstOperator op) {
0461: ClassAnalyzer clazz = methodAnalyzer.getClassAnalyzer();
0462: MethodAnalyzer ma = clazz.getMethod(methodName, methodType);
0463: if (ma == null)
0464: return null;
0465: Environment env = new Environment("L"
0466: + methodAnalyzer.getClazz().getName().replace('.', '/')
0467: + ";");
0468: Interpreter interpreter = new Interpreter(env);
0469: env.interpreter = interpreter;
0470:
0471: String result;
0472: try {
0473: result = (String) interpreter.interpretMethod(ma
0474: .getBytecodeInfo(), null, new Object[] { op
0475: .getValue() });
0476: } catch (InterpreterException ex) {
0477: if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_INTERPRT) != 0) {
0478: GlobalOptions.err
0479: .println("Warning: Can't interpret method "
0480: + methodName);
0481: ex.printStackTrace(GlobalOptions.err);
0482: }
0483: return null;
0484: } catch (InvocationTargetException ex) {
0485: if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_INTERPRT) != 0) {
0486: GlobalOptions.err
0487: .println("Warning: Interpreted method throws"
0488: + " an uncaught exception: ");
0489: ex.getTargetException().printStackTrace(
0490: GlobalOptions.err);
0491: }
0492: return null;
0493: }
0494: return new ConstOperator(result);
0495: }
0496:
0497: public Expression simplifyStringBuffer() {
0498: if (getClassType().equals(Type.tStringBuffer)) {
0499: if (isConstructor()
0500: && subExpressions[0] instanceof NewOperator) {
0501: if (methodType.getParameterTypes().length == 0)
0502: return EMPTYSTRING;
0503: if (methodType.getParameterTypes().length == 1
0504: && methodType.getParameterTypes()[0]
0505: .equals(Type.tString))
0506: return subExpressions[1].simplifyString();
0507: }
0508:
0509: if (!isStatic() && getMethodName().equals("append")
0510: && getMethodType().getParameterTypes().length == 1) {
0511:
0512: Expression firstOp = subExpressions[0]
0513: .simplifyStringBuffer();
0514: if (firstOp == null)
0515: return null;
0516:
0517: subExpressions[1] = subExpressions[1].simplifyString();
0518:
0519: if (firstOp == EMPTYSTRING
0520: && subExpressions[1].getType().isOfType(
0521: Type.tString))
0522: return subExpressions[1];
0523:
0524: if (firstOp instanceof StringAddOperator
0525: && (((Operator) firstOp).getSubExpressions()[0] == EMPTYSTRING))
0526: firstOp = ((Operator) firstOp).getSubExpressions()[1];
0527:
0528: Expression secondOp = subExpressions[1];
0529: Type[] paramTypes = new Type[] { Type.tStringBuffer,
0530: secondOp.getType().getCanonic() };
0531: if (needsCast(1, paramTypes)) {
0532: Type castType = methodType.getParameterTypes()[0];
0533: Operator castOp = new ConvertOperator(castType,
0534: castType);
0535: castOp.addOperand(secondOp);
0536: secondOp = castOp;
0537: }
0538: Operator result = new StringAddOperator();
0539: result.addOperand(secondOp);
0540: result.addOperand(firstOp);
0541: return result;
0542: }
0543: }
0544: return null;
0545: }
0546:
0547: public Expression simplifyString() {
0548: if (getMethodName().equals("toString") && !isStatic()
0549: && getClassType().equals(Type.tStringBuffer)
0550: && subExpressions.length == 1) {
0551: Expression simple = subExpressions[0]
0552: .simplifyStringBuffer();
0553: if (simple != null)
0554: return simple;
0555: } else if (getMethodName().equals("valueOf") && isStatic()
0556: && getClassType().equals(Type.tString)
0557: && subExpressions.length == 1) {
0558:
0559: if (subExpressions[0].getType().isOfType(Type.tString))
0560: return subExpressions[0];
0561:
0562: Operator op = new StringAddOperator();
0563: op.addOperand(subExpressions[0]);
0564: op.addOperand(EMPTYSTRING);
0565: }
0566: /* The pizza way (pizza is the compiler of kaffe) */
0567: else if (getMethodName().equals("concat") && !isStatic()
0568: && getClassType().equals(Type.tString)) {
0569:
0570: Expression result = new StringAddOperator();
0571: Expression right = subExpressions[1].simplify();
0572: if (right instanceof StringAddOperator) {
0573: Operator op = (Operator) right;
0574: if (op.subExpressions != null
0575: && op.subExpressions[0] == EMPTYSTRING)
0576: right = op.subExpressions[1];
0577: }
0578: result.addOperand(right);
0579: result.addOperand(subExpressions[0].simplify());
0580: } else if ((Options.options & Options.OPTION_DECRYPT) != 0
0581: && isThis()
0582: && isStatic()
0583: && methodType.getParameterTypes().length == 1
0584: && methodType.getParameterTypes()[0]
0585: .equals(Type.tString)
0586: && methodType.getReturnType().equals(Type.tString)) {
0587:
0588: Expression expr = subExpressions[0].simplifyString();
0589: if (expr instanceof ConstOperator) {
0590: expr = deobfuscateString((ConstOperator) expr);
0591: if (expr != null)
0592: return expr;
0593: }
0594: }
0595: return this ;
0596: }
0597:
0598: public Expression simplifyAccess() {
0599: if (getMethodAnalyzer() != null) {
0600: SyntheticAnalyzer synth = getMethodAnalyzer()
0601: .getSynthetic();
0602: if (synth != null) {
0603: int unifyParam = synth.getUnifyParam();
0604: Expression op = null;
0605: switch (synth.getKind()) {
0606: case SyntheticAnalyzer.ACCESSGETFIELD:
0607: op = new GetFieldOperator(methodAnalyzer, false,
0608: synth.getReference());
0609: break;
0610: case SyntheticAnalyzer.ACCESSGETSTATIC:
0611: op = new GetFieldOperator(methodAnalyzer, true,
0612: synth.getReference());
0613: break;
0614: case SyntheticAnalyzer.ACCESSPUTFIELD:
0615: case SyntheticAnalyzer.ACCESSDUPPUTFIELD:
0616: op = new StoreInstruction(
0617: new PutFieldOperator(methodAnalyzer, false,
0618: synth.getReference()));
0619: if (synth.getKind() == synth.ACCESSDUPPUTFIELD)
0620: ((StoreInstruction) op).makeNonVoid();
0621: break;
0622: case SyntheticAnalyzer.ACCESSPUTSTATIC:
0623: case SyntheticAnalyzer.ACCESSDUPPUTSTATIC:
0624: op = new StoreInstruction(new PutFieldOperator(
0625: methodAnalyzer, true, synth.getReference()));
0626: if (synth.getKind() == synth.ACCESSDUPPUTSTATIC)
0627: ((StoreInstruction) op).makeNonVoid();
0628: break;
0629: case SyntheticAnalyzer.ACCESSMETHOD:
0630: op = new InvokeOperator(methodAnalyzer,
0631: ACCESSSPECIAL, synth.getReference());
0632: break;
0633: case SyntheticAnalyzer.ACCESSSTATICMETHOD:
0634: op = new InvokeOperator(methodAnalyzer, STATIC,
0635: synth.getReference());
0636: break;
0637: case SyntheticAnalyzer.ACCESSCONSTRUCTOR:
0638: if (subExpressions[unifyParam] instanceof ConstOperator
0639: && ((ConstOperator) subExpressions[unifyParam])
0640: .getValue() == null) {
0641: op = new InvokeOperator(methodAnalyzer,
0642: CONSTRUCTOR, synth.getReference());
0643: }
0644: break;
0645: }
0646:
0647: if (op != null) {
0648: if (subExpressions != null) {
0649: for (int i = subExpressions.length; i-- > 0;) {
0650: if (i == unifyParam
0651: && synth.getKind() == SyntheticAnalyzer.ACCESSCONSTRUCTOR)
0652: // skip the null param.
0653: continue;
0654: op = op.addOperand(subExpressions[i]);
0655: if (subExpressions[i].getFreeOperandCount() > 0)
0656: break;
0657: }
0658: }
0659: return op;
0660: }
0661: }
0662: }
0663: return null;
0664: }
0665:
0666: public boolean needsCast(int param, Type[] paramTypes) {
0667: Type realClassType;
0668: if (methodFlag == STATIC)
0669: realClassType = classType;
0670: else if (param == 0) {
0671: if (paramTypes[0] instanceof NullType)
0672: return true;
0673: if (!(paramTypes[0] instanceof ClassInterfacesType && classType instanceof ClassInterfacesType))
0674: return false;
0675:
0676: ClassInfo clazz = ((ClassInterfacesType) classType)
0677: .getClassInfo();
0678: ClassInfo parClazz = ((ClassInterfacesType) paramTypes[0])
0679: .getClassInfo();
0680: MethodInfo method = getMethodInfo();
0681: if (method == null)
0682: /* This is a NoSuchMethodError */
0683: return false;
0684: if (Modifier.isPrivate(method.getModifiers()))
0685: return parClazz != clazz;
0686: else if ((method.getModifiers() & (Modifier.PROTECTED | Modifier.PUBLIC)) == 0) {
0687: /* Method is protected. We need a cast if parClazz is in
0688: * other package than clazz.
0689: */
0690: int lastDot = clazz.getName().lastIndexOf('.');
0691: if (lastDot != parClazz.getName().lastIndexOf('.')
0692: || !(parClazz.getName().startsWith(clazz
0693: .getName().substring(0, lastDot + 1))))
0694: return true;
0695: }
0696: return false;
0697: } else {
0698: realClassType = paramTypes[0];
0699: }
0700:
0701: if (!(realClassType instanceof ClassInterfacesType)) {
0702: /* Arrays don't have overloaded methods, all okay */
0703: return false;
0704: }
0705: ClassInfo clazz = ((ClassInterfacesType) realClassType)
0706: .getClassInfo();
0707: int offset = skippedArgs;
0708:
0709: Type[] myParamTypes = methodType.getParameterTypes();
0710: if (myParamTypes[param - offset].equals(paramTypes[param])) {
0711: /* Type at param is okay. */
0712: return false;
0713: }
0714: /* Now check if there is a conflicting method in this class or
0715: * a superclass. */
0716: while (clazz != null) {
0717: MethodInfo[] methods = clazz.getMethods();
0718: next_method: for (int i = 0; i < methods.length; i++) {
0719: if (!methods[i].getName().equals(methodName))
0720: /* method name doesn't match*/
0721: continue next_method;
0722:
0723: Type[] otherParamTypes = Type.tMethod(
0724: methods[i].getType()).getParameterTypes();
0725: if (otherParamTypes.length != myParamTypes.length) {
0726: /* parameter count doesn't match*/
0727: continue next_method;
0728: }
0729:
0730: if (myParamTypes[param - offset].isOfType(Type
0731: .tSubType(otherParamTypes[param - offset]))) {
0732: /* cast to myParamTypes cannot resolve any conflicts. */
0733: continue next_method;
0734: }
0735: for (int p = offset; p < paramTypes.length; p++) {
0736: if (!paramTypes[p].isOfType(Type
0737: .tSubType(otherParamTypes[p - offset]))) {
0738: /* No conflict here */
0739: continue next_method;
0740: }
0741: }
0742: /* There is a conflict that can be resolved by a cast. */
0743: return true;
0744: }
0745: clazz = clazz.getSuperclass();
0746: }
0747: return false;
0748: }
0749:
0750: public Expression simplify() {
0751: Expression expr = simplifyAccess();
0752: if (expr != null)
0753: return expr.simplify();
0754: expr = simplifyString();
0755: if (expr != this )
0756: return expr.simplify();
0757: return super .simplify();
0758: }
0759:
0760: /**
0761: * We add the named method scoped classes to the declarables, and
0762: * only fillDeclarables on the parameters we will print.
0763: */
0764: public void fillDeclarables(Collection used) {
0765: ClassInfo clazz = getClassInfo();
0766: InnerClassInfo outer = getOuterClassInfo(clazz);
0767: ClassAnalyzer clazzAna = methodAnalyzer.getClassAnalyzer(clazz);
0768:
0769: if ((Options.options & Options.OPTION_ANON) != 0
0770: && outer != null && outer.outer == null
0771: && outer.name != null && clazzAna != null
0772: && clazzAna.getParent() == methodAnalyzer) {
0773:
0774: /* This is a named method scope class, declare it.
0775: * But first declare all method scoped classes,
0776: * that are used inside; order does matter.
0777: */
0778: clazzAna.fillDeclarables(used);
0779: used.add(clazzAna);
0780: }
0781:
0782: if (!isConstructor() || isStatic()) {
0783: super .fillDeclarables(used);
0784: return;
0785: }
0786: int arg = 1;
0787: int length = subExpressions.length;
0788: boolean jikesAnonymousInner = false;
0789: boolean implicitOuterClass = false;
0790:
0791: if ((Options.options & Options.OPTION_ANON) != 0
0792: && clazzAna != null && outer != null
0793: && (outer.outer == null || outer.name == null)) {
0794:
0795: OuterValues ov = clazzAna.getOuterValues();
0796: arg += ov.getCount();
0797: jikesAnonymousInner = ov.isJikesAnonymousInner();
0798: implicitOuterClass = ov.isImplicitOuterClass();
0799:
0800: for (int i = 1; i < arg; i++) {
0801: Expression expr = subExpressions[i];
0802: if (expr instanceof CheckNullOperator) {
0803: CheckNullOperator cno = (CheckNullOperator) expr;
0804: expr = cno.subExpressions[0];
0805: }
0806: expr.fillDeclarables(used);
0807: }
0808:
0809: if (outer.name == null) {
0810: /* This is an anonymous class */
0811: ClassInfo super Clazz = clazz.getSuperclass();
0812: ClassInfo[] interfaces = clazz.getInterfaces();
0813: if (interfaces.length == 1
0814: && (super Clazz == null || super Clazz == ClassInfo.javaLangObject)) {
0815: clazz = interfaces[0];
0816: } else {
0817: clazz = (super Clazz != null ? super Clazz
0818: : ClassInfo.javaLangObject);
0819: }
0820: outer = getOuterClassInfo(clazz);
0821:
0822: }
0823: }
0824:
0825: if ((Options.options & Options.OPTION_INNER) != 0
0826: && outer != null && outer.outer != null
0827: && outer.name != null
0828: && !Modifier.isStatic(outer.modifiers)
0829: && !implicitOuterClass && arg < length) {
0830:
0831: Expression outerExpr = jikesAnonymousInner ? subExpressions[--length]
0832: : subExpressions[arg++];
0833: if (outerExpr instanceof CheckNullOperator) {
0834: CheckNullOperator cno = (CheckNullOperator) outerExpr;
0835: outerExpr = cno.subExpressions[0];
0836: }
0837: outerExpr.fillDeclarables(used);
0838: }
0839: for (int i = arg; i < length; i++)
0840: subExpressions[i].fillDeclarables(used);
0841: }
0842:
0843: /**
0844: * We add the named method scoped classes to the declarables, and
0845: * only fillDeclarables on the parameters we will print.
0846: */
0847: public void makeDeclaration(Set done) {
0848: super .makeDeclaration(done);
0849:
0850: if (isConstructor() && !isStatic()
0851: && (Options.options & Options.OPTION_ANON) != 0) {
0852: ClassInfo clazz = getClassInfo();
0853: InnerClassInfo outer = getOuterClassInfo(clazz);
0854: ClassAnalyzer clazzAna = methodAnalyzer
0855: .getClassAnalyzer(clazz);
0856: if (clazzAna != null && outer != null && outer.name == null) {
0857:
0858: /* call makeDeclaration on the anonymous class, since
0859: * _we_ will declare the anonymous class. */
0860: clazzAna.makeDeclaration(done);
0861: }
0862: }
0863: }
0864:
0865: public int getBreakPenalty() {
0866: return 5;
0867: }
0868:
0869: /* Invokes never equals: they may return different values even if
0870: * they have the same parameters.
0871: */
0872: public void dumpExpression(TabbedPrintWriter writer)
0873: throws java.io.IOException {
0874: int arg = 1;
0875: int length = subExpressions.length;
0876:
0877: boolean anonymousNew = false;
0878: ClassInfo clazz = getClassInfo();
0879: ClassAnalyzer clazzAna = null;
0880:
0881: Type[] paramTypes = new Type[subExpressions.length];
0882: for (int i = 0; i < subExpressions.length; i++)
0883: paramTypes[i] = subExpressions[i].getType().getCanonic();
0884:
0885: writer.startOp(writer.NO_PAREN, 0);
0886: switch (methodFlag) {
0887: case CONSTRUCTOR: {
0888:
0889: boolean qualifiedNew = false;
0890: boolean jikesAnonymousInner = false;
0891: boolean implicitOuterClass = false;
0892:
0893: /* Check if this is an anonymous constructor. In this case
0894: * clazz and outer will be changed to point to the
0895: * super class and anonymousNew will be set.
0896: */
0897: InnerClassInfo outer = getOuterClassInfo(clazz);
0898: if (outer != null && outer.name == null
0899: && (Options.options & Options.OPTION_ANON) != 0)
0900: anonymousNew = true;
0901: clazzAna = methodAnalyzer.getClassAnalyzer(clazz);
0902: if ((~Options.options & (Options.OPTION_ANON | Options.OPTION_CONTRAFO)) == 0
0903: && clazzAna != null
0904: && outer != null
0905: && (outer.outer == null || outer.name == null)) {
0906:
0907: /* This is a method scoped class, skip the outerValues */
0908: OuterValues ov = clazzAna.getOuterValues();
0909: arg += ov.getCount();
0910: jikesAnonymousInner = ov.isJikesAnonymousInner();
0911: implicitOuterClass = ov.isImplicitOuterClass();
0912:
0913: if (outer.name == null) {
0914: /* This is an anonymous class */
0915: ClassInfo super Clazz = clazz.getSuperclass();
0916: ClassInfo[] interfaces = clazz.getInterfaces();
0917: if (interfaces.length == 1
0918: && (super Clazz == null || super Clazz == ClassInfo.javaLangObject)) {
0919: clazz = interfaces[0];
0920: } else {
0921: if (interfaces.length > 0) {
0922: writer
0923: .print("too many supers in ANONYMOUS ");
0924: }
0925: clazz = (super Clazz != null ? super Clazz
0926: : ClassInfo.javaLangObject);
0927: }
0928: outer = getOuterClassInfo(clazz);
0929: if (jikesAnonymousInner && outer != null
0930: && outer.outer == null
0931: && outer.name != null) {
0932: Expression this Expr = subExpressions[--length];
0933: if (this Expr instanceof CheckNullOperator) {
0934: CheckNullOperator cno = (CheckNullOperator) this Expr;
0935: this Expr = cno.subExpressions[0];
0936: }
0937: if (!(this Expr instanceof ThisOperator)
0938: || (((ThisOperator) this Expr)
0939: .getClassInfo() != methodAnalyzer
0940: .getClazz()))
0941: writer.print("ILLEGAL ANON CONSTR");
0942: }
0943: }
0944: }
0945:
0946: /* Check if this is an inner class. It will dump the outer
0947: * class expression, except if its default.
0948: */
0949: if (outer != null
0950: && outer.outer != null
0951: && outer.name != null
0952: && !Modifier.isStatic(outer.modifiers)
0953: && (~Options.options & (Options.OPTION_INNER | Options.OPTION_CONTRAFO)) == 0) {
0954:
0955: if (implicitOuterClass) {
0956: /* Outer class is "this" and is not given
0957: * explicitly. No need to print something.
0958: */
0959: } else if (arg < length) {
0960: Expression outerExpr = jikesAnonymousInner ? subExpressions[--length]
0961: : subExpressions[arg++];
0962: if (outerExpr instanceof CheckNullOperator) {
0963: CheckNullOperator cno = (CheckNullOperator) outerExpr;
0964: outerExpr = cno.subExpressions[0];
0965: } else {
0966: /* We used to complain about MISSING CHECKNULL
0967: * here except for ThisOperators. But javac
0968: * v8 doesn't seem to create CHECKNULL ops.
0969: */
0970: }
0971:
0972: if (outerExpr instanceof ThisOperator) {
0973: Scope scope = writer.getScope(
0974: ((ThisOperator) outerExpr)
0975: .getClassInfo(),
0976: Scope.CLASSSCOPE);
0977: if (writer.conflicts(outer.name, scope,
0978: Scope.CLASSNAME)) {
0979: qualifiedNew = true;
0980: outerExpr.dumpExpression(writer, 950);
0981: writer.breakOp();
0982: writer.print(".");
0983: }
0984: } else {
0985: qualifiedNew = true;
0986: if (outerExpr.getType().getCanonic() instanceof NullType) {
0987: writer.print("(");
0988: writer.startOp(writer.EXPL_PAREN, 1);
0989: writer.print("(");
0990: writer.printType(Type.tClass(ClassInfo
0991: .forName(outer.outer)));
0992: writer.print(") ");
0993: writer.breakOp();
0994: outerExpr.dumpExpression(writer, 700);
0995: writer.endOp();
0996: writer.print(")");
0997: } else
0998: outerExpr.dumpExpression(writer, 950);
0999: writer.breakOp();
1000: writer.print(".");
1001: }
1002: } else
1003: writer.print("MISSING OUTEREXPR ");
1004: }
1005:
1006: if (subExpressions[0] instanceof NewOperator
1007: && paramTypes[0].equals(classType)) {
1008: writer.print("new ");
1009: if (qualifiedNew)
1010: writer.print(outer.name);
1011: else
1012: writer.printType(Type.tClass(clazz));
1013: break;
1014: }
1015:
1016: if (subExpressions[0] instanceof ThisOperator
1017: && (((ThisOperator) subExpressions[0])
1018: .getClassInfo() == methodAnalyzer
1019: .getClazz())) {
1020: if (isThis())
1021: writer.print("this");
1022: else
1023: writer.print("super");
1024: break;
1025: }
1026:
1027: writer.print("(");
1028: writer.startOp(writer.EXPL_PAREN, 0);
1029: writer.print("(UNCONSTRUCTED)");
1030: writer.breakOp();
1031: subExpressions[0].dumpExpression(writer, 700);
1032: writer.endOp();
1033: writer.print(")");
1034: writer.breakOp();
1035: writer.print(".");
1036: writer.printType(Type.tClass(clazz));
1037: break;
1038: }
1039: case SPECIAL:
1040: if (isSuperOrThis()
1041: && subExpressions[0] instanceof ThisOperator
1042: && (((ThisOperator) subExpressions[0])
1043: .getClassInfo() == methodAnalyzer
1044: .getClazz())) {
1045: if (!isThis()) {
1046: /* We don't have to check if this is the real super
1047: * class, as long as ACC_SUPER is set.
1048: */
1049: writer.print("super");
1050: ClassInfo super Clazz = getClassInfo()
1051: .getSuperclass();
1052: paramTypes[0] = super Clazz == null ? Type.tObject
1053: : Type.tClass(super Clazz);
1054: writer.breakOp();
1055: writer.print(".");
1056: } else {
1057: /* XXX check if this is a private method. */
1058: }
1059: } else if (isThis()) {
1060: /* XXX check if this is a private method. */
1061: if (needsCast(0, paramTypes)) {
1062: writer.print("(");
1063: writer.startOp(writer.EXPL_PAREN, 1);
1064: writer.print("(");
1065: writer.printType(classType);
1066: writer.print(") ");
1067: writer.breakOp();
1068: subExpressions[0].dumpExpression(writer, 700);
1069: writer.endOp();
1070: writer.print(")");
1071: paramTypes[0] = classType;
1072: } else
1073: subExpressions[0].dumpExpression(writer, 950);
1074: writer.breakOp();
1075: writer.print(".");
1076: } else {
1077: writer.print("(");
1078: writer.startOp(writer.EXPL_PAREN, 0);
1079: writer.print("(NON VIRTUAL ");
1080: writer.printType(classType);
1081: writer.print(") ");
1082: writer.breakOp();
1083: subExpressions[0].dumpExpression(writer, 700);
1084: writer.endOp();
1085: writer.print(")");
1086: writer.breakOp();
1087: writer.print(".");
1088: }
1089: writer.print(methodName);
1090: break;
1091:
1092: case ACCESSSPECIAL:
1093: /* Calling a private method in another class. (This is
1094: * allowed for inner classes.)
1095: */
1096: if (paramTypes[0].equals(classType))
1097: subExpressions[0].dumpExpression(writer, 950);
1098: else {
1099: writer.print("(");
1100: writer.startOp(writer.EXPL_PAREN, 0);
1101: writer.print("(");
1102: writer.printType(classType);
1103: writer.print(") ");
1104: writer.breakOp();
1105: paramTypes[0] = classType;
1106: subExpressions[0].dumpExpression(writer, 700);
1107: writer.endOp();
1108: writer.print(")");
1109: }
1110: writer.breakOp();
1111: writer.print(".");
1112: writer.print(methodName);
1113: break;
1114:
1115: case STATIC: {
1116: arg = 0;
1117: Scope scope = writer.getScope(getClassInfo(),
1118: Scope.CLASSSCOPE);
1119: if (scope == null
1120: || writer.conflicts(methodName, scope,
1121: Scope.METHODNAME)) {
1122: writer.printType(classType);
1123: writer.breakOp();
1124: writer.print(".");
1125: }
1126: writer.print(methodName);
1127: break;
1128: }
1129:
1130: case VIRTUAL:
1131: if (subExpressions[0] instanceof ThisOperator) {
1132: ThisOperator this Op = (ThisOperator) subExpressions[0];
1133: Scope scope = writer.getScope(this Op.getClassInfo(),
1134: Scope.CLASSSCOPE);
1135: if (writer.conflicts(methodName, scope,
1136: Scope.METHODNAME)
1137: || (/* This method is inherited from the parent of
1138: * an outer class, or it is inherited from the
1139: * parent of this class and there is a conflicting
1140: * method in some outer class.
1141: */
1142: getMethodAnalyzer() == null && (!isThis() || writer
1143: .conflicts(methodName, null,
1144: Scope.NOSUPERMETHODNAME)))) {
1145: this Op.dumpExpression(writer, 950);
1146: writer.breakOp();
1147: writer.print(".");
1148: }
1149: } else {
1150: if (needsCast(0, paramTypes)) {
1151: writer.print("(");
1152: writer.startOp(writer.EXPL_PAREN, 1);
1153: writer.print("(");
1154: writer.printType(classType);
1155: writer.print(") ");
1156: writer.breakOp();
1157: subExpressions[0].dumpExpression(writer, 700);
1158: writer.endOp();
1159: writer.print(")");
1160: paramTypes[0] = classType;
1161: } else
1162: subExpressions[0].dumpExpression(writer, 950);
1163: writer.breakOp();
1164: writer.print(".");
1165: }
1166: writer.print(methodName);
1167: }
1168:
1169: writer.endOp();
1170: writer.breakOp();
1171: if ((Options.outputStyle & Options.GNU_SPACING) != 0)
1172: writer.print(" ");
1173: writer.print("(");
1174: writer.startOp(writer.EXPL_PAREN, 0);
1175: boolean first = true;
1176: int offset = skippedArgs;
1177: while (arg < length) {
1178: if (!first) {
1179: writer.print(", ");
1180: writer.breakOp();
1181: } else
1182: first = false;
1183: int priority = 0;
1184: if (needsCast(arg, paramTypes)) {
1185: Type castType = methodType.getParameterTypes()[arg
1186: - offset];
1187: writer.startOp(writer.IMPL_PAREN, 1);
1188: writer.print("(");
1189: writer.printType(castType);
1190: writer.print(") ");
1191: writer.breakOp();
1192: paramTypes[arg] = castType;
1193: priority = 700;
1194: }
1195: subExpressions[arg++].dumpExpression(writer, priority);
1196: if (priority == 700)
1197: writer.endOp();
1198: }
1199: writer.endOp();
1200: writer.print(")");
1201:
1202: if (anonymousNew) {
1203: /* If this was an anonymous constructor call, we must now
1204: * dump the source code of the anonymous class.
1205: */
1206: Object state = writer.saveOps();
1207: writer.openBraceClass();
1208: writer.tab();
1209: clazzAna.dumpBlock(writer);
1210: writer.untab();
1211: writer.closeBraceClass();
1212: writer.restoreOps(state);
1213: }
1214: }
1215:
1216: public boolean opEquals(Operator o) {
1217: if (o instanceof InvokeOperator) {
1218: InvokeOperator i = (InvokeOperator) o;
1219: return classType.equals(i.classType)
1220: && methodName.equals(i.methodName)
1221: && methodType.equals(i.methodType)
1222: && methodFlag == i.methodFlag;
1223: }
1224: return false;
1225: }
1226: }
|