KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jode > expr > InvokeOperator


1 /* InvokeOperator Copyright (C) 1998-2002 Jochen Hoenicke.
2  *
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU Lesser General Public License as published by
5  * the Free Software Foundation; either version 2, or (at your option)
6  * any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; see the file COPYING.LESSER. If not, write to
15  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
16  *
17  * $Id: InvokeOperator.java.in,v 4.10.2.8 2002/05/28 17:34:06 hoenicke Exp $
18  */

19
20 package jode.expr;
21 import java.lang.reflect.Modifier JavaDoc;
22
23 import jode.decompiler.MethodAnalyzer;
24 import jode.decompiler.MethodAnalyzer;
25 import jode.decompiler.ClassAnalyzer;
26 import jode.decompiler.TabbedPrintWriter;
27 import jode.decompiler.Options;
28 import jode.decompiler.OuterValues;
29 import jode.decompiler.Scope;
30 import jode.GlobalOptions;
31 import jode.bytecode.*;
32 import jode.jvm.*;
33 import jode.type.*;
34 import jode.util.SimpleMap;
35
36 import java.lang.reflect.InvocationTargetException JavaDoc;
37 import java.util.Hashtable JavaDoc;
38 import java.util.Collections JavaDoc;
39 import java.util.Collection JavaDoc;
40 import java.util.Map JavaDoc;
41 import java.util.Iterator JavaDoc;
42 import java.util.Set JavaDoc;
43
44 public final class InvokeOperator extends Operator
45     implements MatchableOperator {
46
47     public final static int VIRTUAL = 0;
48     public final static int SPECIAL = 1;
49     public final static int STATIC = 2;
50     public final static int CONSTRUCTOR = 3;
51     public final static int ACCESSSPECIAL = 4;
52
53     /**
54      * The methodAnalyzer of the method, that contains this invocation.
55      * This is not the method that we should call.
56      */

57     MethodAnalyzer methodAnalyzer;
58     int methodFlag;
59     MethodType methodType;
60     String JavaDoc methodName;
61     Reference ref;
62     int skippedArgs;
63     Type classType;
64     Type[] hints;
65
66     /**
67      * This hashtable contains hints for every library method. Some
68      * library method take or return an int, but it should be a char
69      * instead. We will remember that here to give them the right
70      * hint.
71      *
72      * The key is the string: methodName + "." + methodType, the value
73      * is a map: It maps base class types for which this hint applies,
74      * to an array of hint types corresponding to the parameters: The
75      * first element is the hint type of the return value, the
76      * remaining entries are the hint types of the parameters. All
77      * hint types may be null, if that parameter shouldn't be hinted.
78      */

79     private final static Hashtable JavaDoc hintTypes = new Hashtable JavaDoc();
80
81     static {
82     /* Fill the hint type hashtable. For example, the first
83      * parameter of String.indexOf should be hinted as char, even
84      * though the formal parameter is an int.
85      * First hint is hint of return value (even if void)
86      * other hints are that of the parameters in order
87      *
88      * You only have to hint the base class. Other classes will
89      * inherit the hints.
90      *
91      * We reuse a lot of objects, since they are all unchangeable
92      * this is no problem. We only hint for chars; it doesn't
93      * make much sense to hint for byte, since its constant
94      * representation is more difficult than an int
95      * representation. If you have more hints to suggest, please
96      * write contact me. (see GlobalOptions.EMAIL)
97      */

98     Type tCharHint = new IntegerType(IntegerType.IT_I, IntegerType.IT_C);
99     Type[] hintC = new Type[] { tCharHint };
100     Type[] hint0C = new Type[] { null, tCharHint };
101     Type[] hint0C0 = new Type[] { null, tCharHint, null };
102
103     Map hintString0CMap = new SimpleMap
104         (Collections.singleton
105          (new SimpleMap.SimpleEntry(Type.tString, hint0C)));
106     Map hintString0C0Map = new SimpleMap
107         (Collections.singleton
108          (new SimpleMap.SimpleEntry(Type.tString, hint0C0)));
109     hintTypes.put("indexOf.(I)I", hintString0CMap);
110     hintTypes.put("lastIndexOf.(I)I", hintString0CMap);
111     hintTypes.put("indexOf.(II)I", hintString0C0Map);
112     hintTypes.put("lastIndexOf.(II)I", hintString0C0Map);
113     hintTypes.put("write.(I)V", new SimpleMap
114               (Collections.singleton
115                (new SimpleMap.SimpleEntry
116             (Type.tClass("java.io.Writer"), hint0C))));
117     hintTypes.put("read.()I", new SimpleMap
118               (Collections.singleton
119                (new SimpleMap.SimpleEntry
120             (Type.tClass("java.io.Reader"), hintC))));
121     hintTypes.put("unread.(I)V", new SimpleMap
122               (Collections.singleton
123                (new SimpleMap.SimpleEntry
124             (Type.tClass("java.io.PushbackReader"), hint0C))));
125     }
126
127
128     public InvokeOperator(MethodAnalyzer methodAnalyzer,
129               int methodFlag, Reference reference) {
130         super(Type.tUnknown, 0);
131     this.ref = reference;
132         this.methodType = Type.tMethod(reference.getType());
133         this.methodName = reference.getName();
134         this.classType = Type.tType(reference.getClazz());
135     this.hints = null;
136     Map allHints = (Map) hintTypes.get(methodName+"."+methodType);
137     if (allHints != null) {
138         for (Iterator JavaDoc i = allHints.entrySet().iterator(); i.hasNext();) {
139         Map.Entry e = (Map.Entry) i.next();
140         if (classType.isOfType(((Type)e.getKey()).getSubType())) {
141             this.hints = (Type[]) e.getValue();
142             break;
143         }
144         }
145     }
146     if (hints != null && hints[0] != null)
147         this.type = hints[0];
148     else
149         this.type = methodType.getReturnType();
150         this.methodAnalyzer = methodAnalyzer;
151     this.methodFlag = methodFlag;
152         if (methodFlag == STATIC)
153             methodAnalyzer.useType(classType);
154     skippedArgs = (methodFlag == STATIC ? 0 : 1);
155     initOperands(skippedArgs + methodType.getParameterTypes().length);
156     checkAnonymousClasses();
157     }
158
159     public final boolean isStatic() {
160         return methodFlag == STATIC;
161     }
162
163     public MethodType getMethodType() {
164         return methodType;
165     }
166
167     public String JavaDoc getMethodName() {
168         return methodName;
169     }
170
171     private static MethodInfo getMethodInfo(ClassInfo clazz,
172                         String JavaDoc name, String JavaDoc type) {
173     while (clazz != null) {
174         MethodInfo method = clazz.findMethod(name, type);
175         if (method != null)
176         return method;
177         clazz = clazz.getSuperclass();
178     }
179     return null;
180     }
181     public MethodInfo getMethodInfo() {
182     ClassInfo clazz;
183     if (ref.getClazz().charAt(0) == '[')
184         clazz = ClassInfo.javaLangObject;
185     else
186         clazz = TypeSignature.getClassInfo(ref.getClazz());
187         return getMethodInfo(clazz, ref.getName(), ref.getType());
188     }
189
190     public Type getClassType() {
191         return classType;
192     }
193
194     public int getPriority() {
195         return 950;
196     }
197
198     public void checkAnonymousClasses() {
199     if (methodFlag != CONSTRUCTOR
200         || (Options.options & Options.OPTION_ANON) == 0)
201         return;
202     InnerClassInfo outer = getOuterClassInfo(getClassInfo());
203     if (outer != null && (outer.outer == null || outer.name == null)) {
204         methodAnalyzer.addAnonymousConstructor(this);
205     }
206     }
207
208     public void updateSubTypes() {
209     int offset = 0;
210         if (!isStatic()) {
211         subExpressions[offset++].setType(Type.tSubType(getClassType()));
212         }
213     Type[] paramTypes = methodType.getParameterTypes();
214     for (int i=0; i < paramTypes.length; i++) {
215         Type pType = (hints != null && hints[i+1] != null)
216         ? hints[i+1] : paramTypes[i];
217         subExpressions[offset++].setType(Type.tSubType(pType));
218     }
219     }
220
221     public void updateType() {
222     }
223
224     /**
225      * Makes a non void expression, in case this is a constructor.
226      */

227     public void makeNonVoid() {
228         if (type != Type.tVoid)
229             throw new jode.AssertError("already non void");
230     ClassInfo clazz = getClassInfo();
231     InnerClassInfo outer = getOuterClassInfo(clazz);
232     if (outer != null && outer.name == null) {
233         /* This is an anonymous class */
234         if (clazz.getInterfaces().length > 0)
235         type = Type.tClass(clazz.getInterfaces()[0]);
236         else
237         type = Type.tClass(clazz.getSuperclass());
238     } else
239         type = subExpressions[0].getType();
240     }
241
242     public boolean isConstructor() {
243         return methodFlag == CONSTRUCTOR;
244     }
245
246     public ClassInfo getClassInfo() {
247     if (classType instanceof ClassInterfacesType)
248         return ((ClassInterfacesType) classType).getClassInfo();
249     return null;
250     }
251
252     /**
253      * Checks, whether this is a call of a method from this class.
254      */

255     public boolean isThis() {
256     return getClassInfo() == methodAnalyzer.getClazz();
257     }
258
259     public InnerClassInfo getOuterClassInfo(ClassInfo ci) {
260     if (ci != null) {
261         InnerClassInfo[] outers = ci.getOuterClasses();
262         if (outers != null)
263         return outers[0];
264     }
265     return null;
266     }
267
268     /**
269      * Tries to locate the class analyzer for the callee class. This
270      * is mainly useful for inner and anonymous classes.
271      *
272      * @return The class analyzer, if the callee class is declared
273      * inside the same base class as the caller class, null otherwise.
274      */

275     public ClassAnalyzer getClassAnalyzer() {
276     if ((Options.options &
277          (Options.OPTION_ANON | Options.OPTION_INNER)) == 0)
278         return null;
279
280     ClassInfo callee = getClassInfo();
281     if (callee == null)
282         return null;
283
284     int nested = 0;
285     InnerClassInfo[] outers = callee.getOuterClasses();
286     if ((Options.options & Options.OPTION_INNER) != 0
287         && outers != null) {
288         /* If the callee class is an inner class we take its
289          * (outermost) parent instead. This will assure that we
290          * find the callee class with one inner -> outer pass.
291          */

292         nested = outers.length;
293         if (outers[nested - 1].outer == null
294         || outers[nested - 1].name == null)
295         nested--;
296
297         if (nested > 0)
298         callee = ClassInfo.forName(outers[nested - 1].outer);
299     }
300
301     /* First check if it is an inner class */
302     ClassAnalyzer ana = methodAnalyzer.getClassAnalyzer(callee);
303
304     if (ana == null) {
305         /* Now we iterate the caller analyzer queue to find the class
306          * analyzer for callee
307          */

308         ana = methodAnalyzer.getClassAnalyzer();
309         while (callee != ana.getClazz()) {
310         if (ana.getParent() == null)
311             return null;
312         if (ana.getParent() instanceof MethodAnalyzer
313             && (Options.options & Options.OPTION_ANON) != 0)
314             ana = ((MethodAnalyzer) ana.getParent())
315             .getClassAnalyzer();
316         else if (ana.getParent() instanceof ClassAnalyzer
317              && (Options.options
318              & Options.OPTION_INNER) != 0)
319             ana = (ClassAnalyzer) ana.getParent();
320         else
321             throw new jode.AssertError
322             ("Unknown parent: "+ana+": "+ana.getParent());
323         }
324     }
325
326     /* Now get the ClassAnalyzer of the real callee */
327     while (nested > 0) {
328         nested--;
329         ana = ana.getInnerClassAnalyzer(outers[nested].name);
330         if (ana == null)
331         return null;
332     }
333     return ana;
334     }
335
336     /**
337      * Checks, whether this is a call of a method from this class or an
338      * outer instance.
339      */

340     public boolean isOuter() {
341     if (classType instanceof ClassInterfacesType) {
342         ClassInfo clazz = ((ClassInterfacesType) classType).getClassInfo();
343         ClassAnalyzer ana = methodAnalyzer.getClassAnalyzer();
344         while (true) {
345         if (clazz == ana.getClazz())
346             return true;
347         if (ana.getParent() == null)
348             break;
349         if (ana.getParent() instanceof MethodAnalyzer
350             && (Options.options & Options.OPTION_ANON) != 0)
351             ana = ((MethodAnalyzer) ana.getParent())
352             .getClassAnalyzer();
353         else if (ana.getParent() instanceof ClassAnalyzer
354              && (Options.options
355                  & Options.OPTION_INNER) != 0)
356             ana = (ClassAnalyzer) ana.getParent();
357         else
358             throw new jode.AssertError
359             ("Unknown parent: "+ana+": "+ana.getParent());
360         }
361     }
362     return false;
363     }
364
365     /**
366      * Tries to locate the method analyzer for the callee. This
367      * is mainly useful for inner and anonymous classes.
368      *
369      * @return The method analyzer, if the callee is declared
370      * inside the same base class as the caller class, null otherwise.
371      */

372     public MethodAnalyzer getMethodAnalyzer() {
373     ClassAnalyzer ana = getClassAnalyzer();
374     if (ana == null)
375         return null;
376     return ana.getMethod(methodName, methodType);
377     }
378
379     /**
380      * Checks, whether this is a call of a method from the super class.
381      * @XXX check, if its the first super class that implements the method.
382      */

383     public boolean isSuperOrThis() {
384     ClassInfo clazz = getClassInfo();
385     if (clazz != null) {
386         return clazz.superClassOf(methodAnalyzer.getClazz());
387     }
388     return false;
389     }
390
391     public boolean isConstant() {
392     if ((Options.options & Options.OPTION_ANON) == 0)
393         return super.isConstant();
394
395     ClassInfo clazz = getClassInfo();
396     InnerClassInfo outer = getOuterClassInfo(clazz);
397     ClassAnalyzer clazzAna = methodAnalyzer.getClassAnalyzer(clazz);
398     if (clazzAna != null
399         && outer != null && outer.outer == null && outer.name != null
400         && clazzAna.getParent() == methodAnalyzer) {
401         /* This is a named method scope class, it needs
402          * declaration. And therefore can't be moved into
403          * a field initializer. */

404         return false;
405     }
406     return super.isConstant();
407     }
408
409     /**
410      * Checks if the value of the operator can be changed by this expression.
411      */

412     public boolean matches(Operator loadop) {
413         return (loadop instanceof InvokeOperator
414         || loadop instanceof GetFieldOperator);
415     }
416
417     /**
418      * Checks if the method is the magic class$ method.
419      * @return true if this is the magic class$ method, false otherwise.
420      */

421     public boolean isGetClass() {
422     MethodAnalyzer mana = getMethodAnalyzer();
423     if (mana == null)
424         return false;
425     SyntheticAnalyzer synth = getMethodAnalyzer().getSynthetic();
426     return (synth != null
427         && synth.getKind() == SyntheticAnalyzer.GETCLASS);
428     }
429
430     class Environment extends SimpleRuntimeEnvironment {
431
432     Interpreter interpreter;
433     String JavaDoc classSig;
434
435     public Environment(String JavaDoc interpretedClassSig) {
436         classSig = interpretedClassSig.intern();
437     }
438
439     public Object JavaDoc invokeMethod(Reference ref, boolean isVirtual,
440                    Object JavaDoc cls, Object JavaDoc[] params)
441         throws InterpreterException, InvocationTargetException JavaDoc {
442         if (cls == null && ref.getClazz().equals(classSig)) {
443         String JavaDoc clazzName = ref.getClazz();
444         clazzName = clazzName.substring(1, ref.getClazz().length() - 1)
445             .replace('/', '.');
446         BytecodeInfo info = ClassInfo.forName(clazzName)
447             .findMethod(ref.getName(), ref.getType())
448             .getBytecode();
449         if (info != null)
450             return interpreter.interpretMethod(info, null, params);
451         throw new InterpreterException
452             ("Can't interpret static native method: "+ref);
453         } else
454         return super.invokeMethod(ref, isVirtual, cls, params);
455     }
456     }
457
458     public ConstOperator deobfuscateString(ConstOperator op) {
459     ClassAnalyzer clazz = methodAnalyzer.getClassAnalyzer();
460     MethodAnalyzer ma = clazz.getMethod(methodName, methodType);
461     if (ma == null)
462         return null;
463     Environment env = new Environment("L"+methodAnalyzer.getClazz()
464                       .getName().replace('.','/')+";");
465     Interpreter interpreter = new Interpreter(env);
466     env.interpreter = interpreter;
467
468     String JavaDoc result;
469     try {
470         result = (String JavaDoc) interpreter.interpretMethod
471         (ma.getBytecodeInfo(), null, new Object JavaDoc[] { op.getValue() });
472     } catch (InterpreterException ex) {
473         if ((GlobalOptions.debuggingFlags &
474          GlobalOptions.DEBUG_INTERPRT) != 0) {
475         GlobalOptions.err.println("Warning: Can't interpret method "
476                       +methodName);
477         ex.printStackTrace(GlobalOptions.err);
478         }
479         return null;
480     } catch (InvocationTargetException JavaDoc ex) {
481         if ((GlobalOptions.debuggingFlags &
482          GlobalOptions.DEBUG_INTERPRT) != 0) {
483         GlobalOptions.err.println("Warning: Interpreted method throws"
484                       +" an uncaught exception: ");
485         ex.getTargetException().printStackTrace(GlobalOptions.err);
486         }
487         return null;
488     }
489     return new ConstOperator(result);
490     }
491
492     public Expression simplifyStringBuffer() {
493     if (getClassType().equals(Type.tStringBuffer)) {
494         if (isConstructor()
495         && subExpressions[0] instanceof NewOperator) {
496         if (methodType.getParameterTypes().length == 0)
497             return EMPTYSTRING;
498         if (methodType.getParameterTypes().length == 1
499             && methodType.getParameterTypes()[0].equals(Type.tString))
500             return subExpressions[1].simplifyString();
501         }
502
503         if (!isStatic()
504         && getMethodName().equals("append")
505         && getMethodType().getParameterTypes().length == 1) {
506         
507         Expression firstOp = subExpressions[0].simplifyStringBuffer();
508         if (firstOp == null)
509             return null;
510         
511         subExpressions[1] = subExpressions[1].simplifyString();
512         
513         if (firstOp == EMPTYSTRING
514             && subExpressions[1].getType().isOfType(Type.tString))
515             return subExpressions[1];
516         
517         if (firstOp instanceof StringAddOperator
518             && (((Operator)firstOp).getSubExpressions()[0]
519             == EMPTYSTRING))
520             firstOp = ((Operator)firstOp).getSubExpressions()[1];
521         
522         Expression secondOp = subExpressions[1];
523         Type[] paramTypes = new Type[] {
524             Type.tStringBuffer, secondOp.getType().getCanonic()
525         };
526         if (needsCast(1, paramTypes)) {
527             Type castType = methodType.getParameterTypes()[0];
528             Operator castOp = new ConvertOperator(castType, castType);
529             castOp.addOperand(secondOp);
530             secondOp = castOp;
531         }
532         Operator result = new StringAddOperator();
533         result.addOperand(secondOp);
534         result.addOperand(firstOp);
535         return result;
536         }
537     }
538         return null;
539     }
540
541     public Expression simplifyString() {
542     if (getMethodName().equals("toString")
543         && !isStatic()
544         && getClassType().equals(Type.tStringBuffer)
545         && subExpressions.length == 1) {
546         Expression simple = subExpressions[0].simplifyStringBuffer();
547         if (simple != null)
548         return simple;
549     }
550     else if (getMethodName().equals("valueOf")
551          && isStatic()
552          && getClassType().equals(Type.tString)
553          && subExpressions.length == 1) {
554         
555         if (subExpressions[0].getType().isOfType(Type.tString))
556         return subExpressions[0];
557         
558         Operator op = new StringAddOperator();
559         op.addOperand(subExpressions[0]);
560         op.addOperand(EMPTYSTRING);
561     }
562     /* The pizza way (pizza is the compiler of kaffe) */
563     else if (getMethodName().equals("concat")
564          && !isStatic()
565          && getClassType().equals(Type.tString)) {
566         
567         Expression result = new StringAddOperator();
568         Expression right = subExpressions[1].simplify();
569         if (right instanceof StringAddOperator) {
570         Operator op = (Operator) right;
571         if (op.subExpressions != null
572             && op.subExpressions[0] == EMPTYSTRING)
573             right = op.subExpressions[1];
574         }
575         result.addOperand(right);
576         result.addOperand(subExpressions[0].simplify());
577     }
578     else if ((Options.options & Options.OPTION_DECRYPT) != 0
579          && isThis() && isStatic()
580          && methodType.getParameterTypes().length == 1
581          && methodType.getParameterTypes()[0].equals(Type.tString)
582          && methodType.getReturnType().equals(Type.tString)) {
583
584         Expression expr = subExpressions[0].simplifyString();
585         if (expr instanceof ConstOperator) {
586         expr = deobfuscateString((ConstOperator)expr);
587         if (expr != null)
588             return expr;
589         }
590     }
591         return this;
592     }
593
594     public Expression simplifyAccess() {
595     if (getMethodAnalyzer() != null) {
596         SyntheticAnalyzer synth = getMethodAnalyzer().getSynthetic();
597         if (synth != null) {
598         int unifyParam = synth.getUnifyParam();
599         Expression op = null;
600         switch (synth.getKind()) {
601         case SyntheticAnalyzer.ACCESSGETFIELD:
602             op = new GetFieldOperator(methodAnalyzer, false,
603                           synth.getReference());
604             break;
605         case SyntheticAnalyzer.ACCESSGETSTATIC:
606             op = new GetFieldOperator(methodAnalyzer, true,
607                           synth.getReference());
608             break;
609         case SyntheticAnalyzer.ACCESSPUTFIELD:
610         case SyntheticAnalyzer.ACCESSDUPPUTFIELD:
611             op = new StoreInstruction
612             (new PutFieldOperator(methodAnalyzer, false,
613                           synth.getReference()));
614             if (synth.getKind() == synth.ACCESSDUPPUTFIELD)
615             ((StoreInstruction) op).makeNonVoid();
616             break;
617         case SyntheticAnalyzer.ACCESSPUTSTATIC:
618         case SyntheticAnalyzer.ACCESSDUPPUTSTATIC:
619             op = new StoreInstruction
620             (new PutFieldOperator(methodAnalyzer, true,
621                           synth.getReference()));
622             if (synth.getKind() == synth.ACCESSDUPPUTSTATIC)
623             ((StoreInstruction) op).makeNonVoid();
624             break;
625         case SyntheticAnalyzer.ACCESSMETHOD:
626             op = new InvokeOperator(methodAnalyzer, ACCESSSPECIAL,
627                         synth.getReference());
628             break;
629         case SyntheticAnalyzer.ACCESSSTATICMETHOD:
630             op = new InvokeOperator(methodAnalyzer, STATIC,
631                         synth.getReference());
632             break;
633         case SyntheticAnalyzer.ACCESSCONSTRUCTOR:
634             if (subExpressions[unifyParam] instanceof ConstOperator
635             && ((ConstOperator)
636                 subExpressions[unifyParam]).getValue() == null) {
637             op = new InvokeOperator(methodAnalyzer, CONSTRUCTOR,
638                         synth.getReference());
639             }
640             break;
641         }
642
643         if (op != null) {
644             if (subExpressions != null) {
645             for (int i=subExpressions.length; i-- > 0; ) {
646                 if (i == unifyParam && synth.getKind()
647                 == SyntheticAnalyzer.ACCESSCONSTRUCTOR)
648                 // skip the null param.
649
continue;
650                 op = op.addOperand(subExpressions[i]);
651                 if (subExpressions[i].getFreeOperandCount() > 0)
652                 break;
653             }
654             }
655             return op;
656         }
657         }
658     }
659     return null;
660     }
661
662     public boolean needsCast(int param, Type[] paramTypes) {
663     Type realClassType;
664     if (methodFlag == STATIC)
665         realClassType = classType;
666     else if (param == 0) {
667         if (paramTypes[0] instanceof NullType)
668         return true;
669         if (!(paramTypes[0] instanceof ClassInterfacesType
670               && classType instanceof ClassInterfacesType))
671         return false;
672         
673         ClassInfo clazz = ((ClassInterfacesType) classType).getClassInfo();
674         ClassInfo parClazz
675         = ((ClassInterfacesType) paramTypes[0]).getClassInfo();
676         MethodInfo method = getMethodInfo();
677         if (method == null)
678         /* This is a NoSuchMethodError */
679         return false;
680         if (Modifier.isPrivate(method.getModifiers()))
681         return parClazz != clazz;
682         else if ((method.getModifiers()
683               & (Modifier.PROTECTED | Modifier.PUBLIC)) == 0) {
684         /* Method is protected. We need a cast if parClazz is in
685          * other package than clazz.
686          */

687         int lastDot = clazz.getName().lastIndexOf('.');
688         if (lastDot != parClazz.getName().lastIndexOf('.')
689             || !(parClazz.getName()
690              .startsWith(clazz.getName().substring(0,lastDot+1))))
691             return true;
692         }
693         return false;
694     } else {
695         realClassType = paramTypes[0];
696     }
697
698     if (!(realClassType instanceof ClassInterfacesType)) {
699         /* Arrays don't have overloaded methods, all okay */
700         return false;
701     }
702     ClassInfo clazz = ((ClassInterfacesType) realClassType).getClassInfo();
703     int offset = skippedArgs;
704     
705     Type[] myParamTypes = methodType.getParameterTypes();
706     if (myParamTypes[param-offset].equals(paramTypes[param])) {
707         /* Type at param is okay. */
708         return false;
709     }
710     /* Now check if there is a conflicting method in this class or
711      * a superclass. */

712     while (clazz != null) {
713         MethodInfo[] methods = clazz.getMethods();
714     next_method:
715         for (int i=0; i< methods.length; i++) {
716         if (!methods[i].getName().equals(methodName))
717             /* method name doesn't match*/
718             continue next_method;
719
720         Type[] otherParamTypes
721             = Type.tMethod(methods[i].getType()).getParameterTypes();
722         if (otherParamTypes.length != myParamTypes.length) {
723             /* parameter count doesn't match*/
724             continue next_method;
725         }
726
727         if (myParamTypes[param-offset].isOfType
728             (Type.tSubType(otherParamTypes[param-offset]))) {
729             /* cast to myParamTypes cannot resolve any conflicts. */
730             continue next_method;
731         }
732         for (int p = offset; p < paramTypes.length; p++) {
733             if (!paramTypes[p]
734             .isOfType(Type.tSubType(otherParamTypes[p-offset]))){
735             /* No conflict here */
736             continue next_method;
737             }
738         }
739         /* There is a conflict that can be resolved by a cast. */
740         return true;
741         }
742         clazz = clazz.getSuperclass();
743     }
744     return false;
745     }
746
747     public Expression simplify() {
748     Expression expr = simplifyAccess();
749     if (expr != null)
750         return expr.simplify();
751     expr = simplifyString();
752     if (expr != this)
753         return expr.simplify();
754     return super.simplify();
755     }
756
757
758     /**
759      * We add the named method scoped classes to the declarables, and
760      * only fillDeclarables on the parameters we will print.
761      */

762     public void fillDeclarables(Collection JavaDoc used) {
763     ClassInfo clazz = getClassInfo();
764     InnerClassInfo outer = getOuterClassInfo(clazz);
765     ClassAnalyzer clazzAna = methodAnalyzer.getClassAnalyzer(clazz);
766
767     if ((Options.options & Options.OPTION_ANON) != 0
768         && outer != null && outer.outer == null && outer.name != null
769         && clazzAna != null
770         && clazzAna.getParent() == methodAnalyzer) {
771
772         /* This is a named method scope class, declare it.
773          * But first declare all method scoped classes,
774          * that are used inside; order does matter.
775          */

776         clazzAna.fillDeclarables(used);
777         used.add(clazzAna);
778     }
779
780     if (!isConstructor() || isStatic()) {
781         super.fillDeclarables(used);
782         return;
783     }
784     int arg = 1;
785     int length = subExpressions.length;
786     boolean jikesAnonymousInner = false;
787     boolean implicitOuterClass = false;
788
789     if ((Options.options & Options.OPTION_ANON) != 0
790         && clazzAna != null
791         && outer != null && (outer.outer == null || outer.name == null)) {
792
793         OuterValues ov = clazzAna.getOuterValues();
794         arg += ov.getCount();
795         jikesAnonymousInner = ov.isJikesAnonymousInner();
796         implicitOuterClass = ov.isImplicitOuterClass();
797         
798         for (int i=1; i< arg; i++) {
799         Expression expr = subExpressions[i];
800         if (expr instanceof CheckNullOperator) {
801             CheckNullOperator cno = (CheckNullOperator) expr;
802             expr = cno.subExpressions[0];
803         }
804         expr.fillDeclarables(used);
805         }
806         
807         if (outer.name == null) {
808         /* This is an anonymous class */
809         ClassInfo superClazz = clazz.getSuperclass();
810         ClassInfo[] interfaces = clazz.getInterfaces();
811         if (interfaces.length == 1
812             && (superClazz == null
813             || superClazz == ClassInfo.javaLangObject)) {
814             clazz = interfaces[0];
815         } else {
816             clazz = (superClazz != null
817                  ? superClazz : ClassInfo.javaLangObject);
818         }
819         outer = getOuterClassInfo(clazz);
820         
821         }
822     }
823
824     if ((Options.options & Options.OPTION_INNER) != 0
825         && outer != null && outer.outer != null && outer.name != null
826         && !Modifier.isStatic(outer.modifiers)
827         && !implicitOuterClass
828         && arg < length) {
829         
830         Expression outerExpr = jikesAnonymousInner
831         ? subExpressions[--length]
832         : subExpressions[arg++];
833         if (outerExpr instanceof CheckNullOperator) {
834         CheckNullOperator cno = (CheckNullOperator) outerExpr;
835         outerExpr = cno.subExpressions[0];
836         }
837         outerExpr.fillDeclarables(used);
838     }
839     for (int i=arg; i < length; i++)
840         subExpressions[i].fillDeclarables(used);
841     }
842
843     /**
844      * We add the named method scoped classes to the declarables, and
845      * only fillDeclarables on the parameters we will print.
846      */

847     public void makeDeclaration(Set JavaDoc done) {
848     super.makeDeclaration(done);
849
850     if (isConstructor() && !isStatic()
851         && (Options.options & Options.OPTION_ANON) != 0) {
852         ClassInfo clazz = getClassInfo();
853         InnerClassInfo outer = getOuterClassInfo(clazz);
854         ClassAnalyzer clazzAna = methodAnalyzer.getClassAnalyzer(clazz);
855         if (clazzAna != null && outer != null && outer.name == null) {
856         
857         /* call makeDeclaration on the anonymous class, since
858          * _we_ will declare the anonymous class. */

859         clazzAna.makeDeclaration(done);
860         }
861     }
862     }
863
864     public int getBreakPenalty() {
865     return 5;
866     }
867     
868     /* Invokes never equals: they may return different values even if
869      * they have the same parameters.
870      */

871     public void dumpExpression(TabbedPrintWriter writer)
872     throws java.io.IOException JavaDoc {
873         int arg = 1;
874     int length = subExpressions.length;
875
876     boolean anonymousNew = false;
877     ClassInfo clazz = getClassInfo();
878     ClassAnalyzer clazzAna = null;
879
880     Type[] paramTypes = new Type[subExpressions.length];
881     for (int i=0; i< subExpressions.length; i++)
882         paramTypes[i] = subExpressions[i].getType().getCanonic();
883
884     writer.startOp(writer.NO_PAREN, 0);
885     switch (methodFlag) {
886     case CONSTRUCTOR: {
887
888         boolean qualifiedNew = false;
889         boolean jikesAnonymousInner = false;
890         boolean implicitOuterClass = false;
891         
892         
893         /* Check if this is an anonymous constructor. In this case
894          * clazz and outer will be changed to point to the
895          * super class and anonymousNew will be set.
896          */

897         InnerClassInfo outer = getOuterClassInfo(clazz);
898         if (outer != null && outer.name == null
899         && (Options.options & Options.OPTION_ANON) != 0)
900         anonymousNew = true;
901         clazzAna = methodAnalyzer.getClassAnalyzer(clazz);
902         if ((~Options.options &
903          (Options.OPTION_ANON | Options.OPTION_CONTRAFO)) == 0
904         && clazzAna != null
905         && outer != null
906         && (outer.outer == null || outer.name == null)) {
907         
908         /* This is a method scoped class, skip the outerValues */
909         OuterValues ov = clazzAna.getOuterValues();
910         arg += ov.getCount();
911         jikesAnonymousInner = ov.isJikesAnonymousInner();
912         implicitOuterClass = ov.isImplicitOuterClass();
913         
914         if (outer.name == null) {
915             /* This is an anonymous class */
916             ClassInfo superClazz = clazz.getSuperclass();
917             ClassInfo[] interfaces = clazz.getInterfaces();
918             if (interfaces.length == 1
919             && (superClazz == null
920                 || superClazz == ClassInfo.javaLangObject)) {
921             clazz = interfaces[0];
922             } else {
923             if (interfaces.length > 0) {
924                 writer.print("too many supers in ANONYMOUS ");
925             }
926             clazz = (superClazz != null
927                  ? superClazz : ClassInfo.javaLangObject);
928             }
929             outer = getOuterClassInfo(clazz);
930             if (jikesAnonymousInner && outer != null
931             && outer.outer == null && outer.name != null) {
932             Expression thisExpr = subExpressions[--length];
933             if (thisExpr instanceof CheckNullOperator) {
934                 CheckNullOperator cno
935                 = (CheckNullOperator) thisExpr;
936                 thisExpr = cno.subExpressions[0];
937             }
938             if (!(thisExpr instanceof ThisOperator)
939                 || (((ThisOperator) thisExpr).getClassInfo()
940                 != methodAnalyzer.getClazz()))
941                 writer.print("ILLEGAL ANON CONSTR");
942             }
943         }
944         }
945         
946         /* Check if this is an inner class. It will dump the outer
947          * class expression, except if its default.
948          */

949         if (outer != null && outer.outer != null && outer.name != null
950         && !Modifier.isStatic(outer.modifiers)
951         && (~Options.options &
952             (Options.OPTION_INNER
953              | Options.OPTION_CONTRAFO)) == 0) {
954
955         if (implicitOuterClass) {
956             /* Outer class is "this" and is not given
957              * explicitly. No need to print something.
958              */

959         } else if (arg < length) {
960             Expression outerExpr = jikesAnonymousInner
961             ? subExpressions[--length]
962             : subExpressions[arg++];
963             if (outerExpr instanceof CheckNullOperator) {
964             CheckNullOperator cno = (CheckNullOperator) outerExpr;
965             outerExpr = cno.subExpressions[0];
966             } else {
967             /* We used to complain about MISSING CHECKNULL
968              * here except for ThisOperators. But javac
969              * v8 doesn't seem to create CHECKNULL ops.
970              */

971             }
972
973             if (outerExpr instanceof ThisOperator) {
974             Scope scope = writer.getScope
975                 (((ThisOperator) outerExpr).getClassInfo(),
976                  Scope.CLASSSCOPE);
977             if (writer.conflicts(outer.name, scope, Scope.CLASSNAME)) {
978                 qualifiedNew = true;
979                 outerExpr.dumpExpression(writer, 950);
980                 writer.breakOp();
981                 writer.print(".");
982             }
983             } else {
984             qualifiedNew = true;
985             if (outerExpr.getType().getCanonic()
986                 instanceof NullType) {
987                 writer.print("(");
988                 writer.startOp(writer.EXPL_PAREN, 1);
989                 writer.print("(");
990                 writer.printType(Type.tClass
991                          (ClassInfo.forName(outer.outer)));
992                 writer.print(") ");
993                 writer.breakOp();
994                 outerExpr.dumpExpression(writer, 700);
995                 writer.endOp();
996                 writer.print(")");
997             } else
998                 outerExpr.dumpExpression(writer, 950);
999             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]).getClassInfo()
1018            == methodAnalyzer.getClazz())) {
1019        if (isThis())
1020            writer.print("this");
1021        else
1022            writer.print("super");
1023        break;
1024        }
1025
1026        writer.print("(");
1027        writer.startOp(writer.EXPL_PAREN, 0);
1028        writer.print("(UNCONSTRUCTED)");
1029        writer.breakOp();
1030        subExpressions[0].dumpExpression(writer, 700);
1031        writer.endOp();
1032        writer.print(")");
1033        writer.breakOp();
1034        writer.print(".");
1035        writer.printType(Type.tClass(clazz));
1036        break;
1037    }
1038    case SPECIAL:
1039        if (isSuperOrThis()
1040        && subExpressions[0] instanceof ThisOperator
1041        && (((ThisOperator)subExpressions[0]).getClassInfo()
1042            == methodAnalyzer.getClazz())) {
1043        if (!isThis()) {
1044            /* We don't have to check if this is the real super
1045             * class, as long as ACC_SUPER is set.
1046             */

1047            writer.print("super");
1048            ClassInfo superClazz = getClassInfo().getSuperclass();
1049            paramTypes[0] = superClazz == null
1050            ? Type.tObject : Type.tClass(superClazz);
1051            writer.breakOp();
1052            writer.print(".");
1053        } else {
1054            /* XXX check if this is a private method. */
1055        }
1056        } else if (isThis()) {
1057        /* XXX check if this is a private method. */
1058        if (needsCast(0, paramTypes)){
1059            writer.print("(");
1060            writer.startOp(writer.EXPL_PAREN, 1);
1061            writer.print("(");
1062            writer.printType(classType);
1063            writer.print(") ");
1064            writer.breakOp();
1065            subExpressions[0].dumpExpression(writer, 700);
1066            writer.endOp();
1067            writer.print(")");
1068            paramTypes[0] = classType;
1069        } else
1070            subExpressions[0].dumpExpression(writer, 950);
1071        writer.breakOp();
1072        writer.print(".");
1073        } else {
1074        writer.print("(");
1075        writer.startOp(writer.EXPL_PAREN, 0);
1076        writer.print("(NON VIRTUAL ");
1077        writer.printType(classType);
1078        writer.print(") ");
1079        writer.breakOp();
1080        subExpressions[0].dumpExpression(writer, 700);
1081        writer.endOp();
1082        writer.print(")");
1083        writer.breakOp();
1084        writer.print(".");
1085        }
1086        writer.print(methodName);
1087        break;
1088
1089    case ACCESSSPECIAL:
1090        /* Calling a private method in another class. (This is
1091             * allowed for inner classes.)
1092         */

1093        if (paramTypes[0].equals(classType))
1094        subExpressions[0].dumpExpression(writer, 950);
1095        else {
1096        writer.print("(");
1097        writer.startOp(writer.EXPL_PAREN, 0);
1098        writer.print("(");
1099        writer.printType(classType);
1100        writer.print(") ");
1101        writer.breakOp();
1102        paramTypes[0] = classType;
1103        subExpressions[0].dumpExpression(writer, 700);
1104        writer.endOp();
1105        writer.print(")");
1106        }
1107        writer.breakOp();
1108        writer.print(".");
1109        writer.print(methodName);
1110        break;
1111
1112    case STATIC: {
1113        arg = 0;
1114        Scope scope = writer.getScope(getClassInfo(),
1115                      Scope.CLASSSCOPE);
1116        if (scope == null
1117        ||writer.conflicts(methodName, scope, Scope.METHODNAME)) {
1118        writer.printType(classType);
1119        writer.breakOp();
1120        writer.print(".");
1121        }
1122        writer.print(methodName);
1123        break;
1124    }
1125
1126    case VIRTUAL:
1127        if (subExpressions[0] instanceof ThisOperator) {
1128        ThisOperator thisOp = (ThisOperator) subExpressions[0];
1129        Scope scope = writer.getScope(thisOp.getClassInfo(),
1130                          Scope.CLASSSCOPE);
1131        if (writer.conflicts(methodName, scope, Scope.METHODNAME)
1132            || (/* This method is inherited from the parent of
1133             * an outer class, or it is inherited from the
1134             * parent of this class and there is a conflicting
1135             * method in some outer class.
1136             */

1137            getMethodAnalyzer() == null
1138            && (!isThis() ||
1139                writer.conflicts(methodName, null,
1140                         Scope.NOSUPERMETHODNAME)))) {
1141            thisOp.dumpExpression(writer, 950);
1142            writer.breakOp();
1143            writer.print(".");
1144        }
1145        } else {
1146        if (needsCast(0, paramTypes)){
1147            writer.print("(");
1148            writer.startOp(writer.EXPL_PAREN, 1);
1149            writer.print("(");
1150            writer.printType(classType);
1151            writer.print(") ");
1152            writer.breakOp();
1153            subExpressions[0].dumpExpression(writer, 700);
1154            writer.endOp();
1155            writer.print(")");
1156            paramTypes[0] = classType;
1157        } else
1158            subExpressions[0].dumpExpression(writer, 950);
1159        writer.breakOp();
1160        writer.print(".");
1161        }
1162        writer.print(methodName);
1163    }
1164
1165    writer.endOp();
1166    writer.breakOp();
1167    if ((Options.outputStyle & Options.GNU_SPACING) != 0)
1168        writer.print(" ");
1169    writer.print("(");
1170    writer.startOp(writer.EXPL_PAREN, 0);
1171    boolean first = true;
1172    int offset = skippedArgs;
1173    while (arg < length) {
1174            if (!first) {
1175        writer.print(", ");
1176        writer.breakOp();
1177        } else
1178        first = false;
1179        int priority = 0;
1180        if (needsCast(arg, paramTypes)) {
1181        Type castType = methodType.getParameterTypes()[arg-offset];
1182        writer.startOp(writer.IMPL_PAREN, 1);
1183        writer.print("(");
1184        writer.printType(castType);
1185        writer.print(") ");
1186        writer.breakOp();
1187        paramTypes[arg] = castType;
1188        priority = 700;
1189        }
1190            subExpressions[arg++].dumpExpression(writer, priority);
1191        if (priority == 700)
1192        writer.endOp();
1193        }
1194    writer.endOp();
1195        writer.print(")");
1196
1197    if (anonymousNew) {
1198        /* If this was an anonymous constructor call, we must now
1199         * dump the source code of the anonymous class.
1200         */

1201        Object JavaDoc state = writer.saveOps();
1202        writer.openBraceClass();
1203        writer.tab();
1204        clazzAna.dumpBlock(writer);
1205        writer.untab();
1206        writer.closeBraceClass();
1207        writer.restoreOps(state);
1208    }
1209    }
1210
1211    public boolean opEquals(Operator o) {
1212    if (o instanceof InvokeOperator) {
1213        InvokeOperator i = (InvokeOperator)o;
1214        return classType.equals(i.classType)
1215        && methodName.equals(i.methodName)
1216        && methodType.equals(i.methodType)
1217        && methodFlag == i.methodFlag;
1218    }
1219    return false;
1220    }
1221}
1222
Popular Tags