KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > google > gwt > dev > jjs > ast > JProgram


1 /*
2  * Copyright 2007 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */

16 package com.google.gwt.dev.jjs.ast;
17
18 import com.google.gwt.core.ext.TreeLogger;
19 import com.google.gwt.core.ext.UnableToCompleteException;
20 import com.google.gwt.dev.jdt.FindDeferredBindingSitesVisitor;
21 import com.google.gwt.dev.jdt.RebindOracle;
22 import com.google.gwt.dev.jjs.InternalCompilerException;
23 import com.google.gwt.dev.jjs.SourceInfo;
24 import com.google.gwt.dev.jjs.ast.js.JClassSeed;
25 import com.google.gwt.dev.jjs.ast.js.JsniMethod;
26
27 import java.util.ArrayList JavaDoc;
28 import java.util.Collection JavaDoc;
29 import java.util.HashMap JavaDoc;
30 import java.util.IdentityHashMap JavaDoc;
31 import java.util.Iterator JavaDoc;
32 import java.util.List JavaDoc;
33 import java.util.Map JavaDoc;
34
35 /**
36  * Root for the AST representing an entire Java program.
37  */

38 public class JProgram extends JNode {
39
40   private static final int IS_ARRAY = 2;
41   private static final int IS_CLASS = 3;
42   private static final int IS_INTERFACE = 1;
43   private static final int IS_NULL = 0;
44
45   public static boolean methodsDoMatch(JMethod method1, JMethod method2) {
46     // static methods cannot match each other
47
if (method1.isStatic() || method2.isStatic()) {
48       return false;
49     }
50
51     // names must be identical
52
if (!method1.getName().equals(method2.getName())) {
53       return false;
54     }
55
56     // original parameter types must be identical
57
List JavaDoc/* <JType> */params1 = method1.getOriginalParamTypes();
58     List JavaDoc/* <JType> */params2 = method2.getOriginalParamTypes();
59     int params1size = params1.size();
60     if (params1size != params2.size()) {
61       return false;
62     }
63
64     for (int i = 0; i < params1size; ++i) {
65       if (params1.get(i) != params2.get(i)) {
66         return false;
67       }
68     }
69     return true;
70   }
71
72   private static String JavaDoc dotify(char[][] name) {
73     StringBuffer JavaDoc result = new StringBuffer JavaDoc();
74     for (int i = 0; i < name.length; ++i) {
75       if (i > 0) {
76         result.append('.');
77       }
78
79       result.append(name[i]);
80     }
81     return result.toString();
82   }
83
84   public final List JavaDoc/* <JMethod> */entryMethods = new ArrayList JavaDoc/* <JMethod> */();
85
86   public final Map JavaDoc/* <String, HasEnclosingType> */jsniMap = new HashMap JavaDoc/*
87                                                                          * <String,
88                                                                          * HasEnclosingType>
89                                                                          */
();
90
91   public final List JavaDoc/* <JClassType> */specialTypes = new ArrayList JavaDoc/* <JClassType> */();
92
93   public final JTypeOracle typeOracle = new JTypeOracle(this);
94
95   private final List JavaDoc/* <JArrayType> */allArrayTypes = new ArrayList JavaDoc/* <JArrayType> */();
96
97   private final List JavaDoc/* <JReferenceType> */allTypes = new ArrayList JavaDoc/* <JReferenceType> */();
98
99   /**
100    * Each entry is a HashMap(JType => JArrayType), arranged such that the number
101    * of dimensions is that index (plus one) at which the JArrayTypes having that
102    * number of dimensions resides.
103    */

104   private final ArrayList JavaDoc/* <HashMap<JType, JArrayType>> */dimensions = new ArrayList JavaDoc/*
105                                                                                        * <HashMap<JType,
106                                                                                        * JArrayType>>
107                                                                                        */
();
108
109   private final Map JavaDoc/* <JMethod, JMethod> */instanceToStaticMap = new IdentityHashMap JavaDoc/*
110                                                                                      * <JMethod,
111                                                                                      * JMethod>
112                                                                                      */
();
113
114   private List JavaDoc/* <JsonObject> */jsonTypeTable;
115
116   private final JAbsentArrayDimension literalAbsentArrayDim = new JAbsentArrayDimension(
117       this);
118
119   private final JBooleanLiteral literalFalse = new JBooleanLiteral(this, false);
120
121   private final JIntLiteral literalIntNegOne = new JIntLiteral(this, -1);
122
123   private final JIntLiteral literalIntOne = new JIntLiteral(this, 1);
124
125   private final JIntLiteral literalIntZero = new JIntLiteral(this, 0);
126
127   private final JNullLiteral literalNull = new JNullLiteral(this);
128
129   private final JBooleanLiteral literalTrue = new JBooleanLiteral(this, true);
130
131   private final TreeLogger logger;
132
133   private JField nullField;
134
135   private JMethod nullMethod;
136
137   private Map JavaDoc/* <JReferenceType, Integer> */queryIds;
138
139   private JMethod rebindCreateMethod;
140
141   private final RebindOracle rebindOracle;
142
143   private final Map JavaDoc/* <String, JField> */specialFields = new HashMap JavaDoc/*
144                                                                      * <String,
145                                                                      * JField>
146                                                                      */
();
147
148   private final Map JavaDoc/* <String, JMethod> */specialMethods = new HashMap JavaDoc/*
149                                                                        * <String,
150                                                                        * JMethod>
151                                                                        */
();
152
153   private final Map JavaDoc/* <JMethod, JMethod> */staticToInstanceMap = new IdentityHashMap JavaDoc/*
154                                                                                      * <JMethod,
155                                                                                      * JMethod>
156                                                                                      */
();
157
158   private final JPrimitiveType typeBoolean = new JPrimitiveType(this,
159       "boolean", "Z", literalFalse);
160
161   private final JPrimitiveType typeByte = new JPrimitiveType(this, "byte", "B",
162       literalIntZero);
163
164   private final JPrimitiveType typeChar = new JPrimitiveType(this, "char", "C",
165       getLiteralChar((char) 0));
166
167   private JClassType typeClass;
168
169   private final JPrimitiveType typeDouble = new JPrimitiveType(this, "double",
170       "D", getLiteralDouble(0));
171
172   private final JPrimitiveType typeFloat = new JPrimitiveType(this, "float",
173       "F", getLiteralFloat(0));
174
175   private Map JavaDoc/* <JClassType, Integer> */typeIdMap = new HashMap JavaDoc/*
176                                                                  * <JClassType,
177                                                                  * Integer>
178                                                                  */
();
179
180   private final JPrimitiveType typeInt = new JPrimitiveType(this, "int", "I",
181       literalIntZero);
182
183   private JClassType typeJavaLangObject;
184
185   private final JPrimitiveType typeLong = new JPrimitiveType(this, "long", "J",
186       getLiteralLong(0));
187
188   private final Map JavaDoc/* <String, JReferenceType> */typeNameMap = new HashMap JavaDoc/*
189                                                                            * <String,
190                                                                            * JReferenceType>
191                                                                            */
();
192
193   private final JNullType typeNull = new JNullType(this);
194
195   private final JPrimitiveType typeShort = new JPrimitiveType(this, "short",
196       "S", literalIntZero);
197
198   private JClassType typeSpecialArray;
199
200   private JClassType typeSpecialCast;
201
202   private JClassType typeSpecialExceptions;
203
204   private JClassType typeSpecialJavaScriptObject;
205
206   private JClassType typeString;
207
208   private final JPrimitiveType typeVoid = new JPrimitiveType(this, "void", "V",
209       null);
210
211   public JProgram(TreeLogger logger, RebindOracle rebindOracle) {
212     super(null, null);
213     this.logger = logger;
214     this.rebindOracle = rebindOracle;
215   }
216
217   public void addEntryMethod(JMethod entryPoint) {
218     if (!entryMethods.contains(entryPoint)) {
219       entryMethods.add(entryPoint);
220     }
221   }
222
223   /**
224    * Helper to create an assignment, used to initalize fields, etc.
225    */

226   public JExpressionStatement createAssignmentStmt(SourceInfo info,
227       JExpression lhs, JExpression rhs) {
228     JBinaryOperation assign = new JBinaryOperation(this, info, lhs.getType(),
229         JBinaryOperator.ASG, lhs, rhs);
230     return assign.makeStatement();
231   }
232
233   public JClassType createClass(SourceInfo info, char[][] name,
234       boolean isAbstract, boolean isFinal) {
235     String JavaDoc sname = dotify(name);
236     JClassType x = new JClassType(this, info, sname, isAbstract, isFinal);
237
238     allTypes.add(x);
239     putIntoTypeMap(sname, x);
240
241     if (sname.equals("java.lang.Object")) {
242       typeJavaLangObject = x;
243       specialTypes.add(x);
244     } else if (sname.equals("java.lang.String")) {
245       typeString = x;
246     } else if (sname.equals("java.lang.Class")) {
247       typeClass = x;
248     } else if (sname.equals("com.google.gwt.core.client.JavaScriptObject")) {
249       typeSpecialJavaScriptObject = x;
250     } else if (sname.equals("com.google.gwt.lang.Array")) {
251       typeSpecialArray = x;
252       specialTypes.add(x);
253     } else if (sname.equals("com.google.gwt.lang.Cast")) {
254       typeSpecialCast = x;
255       specialTypes.add(x);
256     } else if (sname.equals("com.google.gwt.lang.Exceptions")) {
257       typeSpecialExceptions = x;
258       specialTypes.add(x);
259     }
260
261     return x;
262   }
263
264   public JField createField(SourceInfo info, char[] name,
265       JReferenceType enclosingType, JType type, boolean isStatic,
266       boolean isFinal, boolean hasInitializer) {
267     assert (name != null);
268     assert (enclosingType != null);
269     assert (type != null);
270
271     /*
272      * MAGIC: special fields are filled in during code gen, don't bother
273      * synthesizing dummy initializations.
274      */

275     boolean isSpecialField = specialTypes.contains(enclosingType);
276
277     if (isSpecialField) {
278       hasInitializer = true;
279     }
280
281     String JavaDoc sname = String.valueOf(name);
282     JField x = new JField(this, info, sname, enclosingType, type, isStatic,
283         isFinal, hasInitializer);
284
285     if (isSpecialField) {
286       specialFields.put(enclosingType.getShortName() + '.' + sname, x);
287     }
288
289     enclosingType.fields.add(x);
290
291     return x;
292   }
293
294   public JInterfaceType createInterface(SourceInfo info, char[][] name) {
295     String JavaDoc sname = dotify(name);
296     JInterfaceType x = new JInterfaceType(this, info, sname);
297
298     allTypes.add(x);
299     putIntoTypeMap(sname, x);
300
301     return x;
302   }
303
304   public JLocal createLocal(SourceInfo info, char[] name, JType type,
305       boolean isFinal, JMethod enclosingMethod) {
306     assert (name != null);
307     assert (type != null);
308     assert (enclosingMethod != null);
309
310     JLocal x = new JLocal(this, info, String.valueOf(name), type, isFinal,
311         enclosingMethod);
312
313     enclosingMethod.locals.add(x);
314
315     return x;
316   }
317
318   public JMethod createMethod(SourceInfo info, char[] name,
319       JReferenceType enclosingType, JType returnType, boolean isAbstract,
320       boolean isStatic, boolean isFinal, boolean isPrivate, boolean isNative) {
321     assert (name != null);
322     assert (returnType != null);
323     assert (!isAbstract || !isNative);
324
325     JMethod x;
326     String JavaDoc sname = String.valueOf(name);
327     if (isNative) {
328       x = new JsniMethod(this, info, sname, enclosingType, returnType,
329           isStatic, isFinal, isPrivate);
330     } else {
331       x = new JMethod(this, info, sname, enclosingType, returnType, isAbstract,
332           isStatic, isFinal, isPrivate);
333     }
334
335     if (sname.equals(FindDeferredBindingSitesVisitor.REBIND_MAGIC_METHOD)
336         && enclosingType.getName().equals(
337             FindDeferredBindingSitesVisitor.REBIND_MAGIC_CLASS)) {
338       rebindCreateMethod = x;
339     } else if (!isPrivate && specialTypes.contains(enclosingType)) {
340       specialMethods.put(enclosingType.getShortName() + '.' + sname, x);
341     }
342
343     if (enclosingType != null) {
344       enclosingType.methods.add(x);
345     }
346
347     return x;
348   }
349
350   public JParameter createParameter(SourceInfo info, char[] name, JType type,
351       boolean isFinal, JMethod enclosingMethod) {
352     assert (name != null);
353     assert (type != null);
354     assert (enclosingMethod != null);
355
356     JParameter x = new JParameter(this, info, String.valueOf(name), type,
357         isFinal, enclosingMethod);
358
359     enclosingMethod.params.add(x);
360
361     return x;
362   }
363
364   public JReferenceType generalizeTypes(Collection JavaDoc/* <JReferenceType> */types) {
365     assert (types != null);
366     assert (!types.isEmpty());
367     Iterator JavaDoc/* <JReferenceType> */it = types.iterator();
368     JReferenceType curType = (JReferenceType) it.next();
369     while (it.hasNext()) {
370       curType = generalizeTypes(curType, (JReferenceType) it.next());
371     }
372     return curType;
373   }
374
375   public List JavaDoc/* <JArrayType> */getAllArrayTypes() {
376     return allArrayTypes;
377   }
378
379   public List JavaDoc/* <JReferenceType> */getDeclaredTypes() {
380     return allTypes;
381   }
382
383   public JThisRef getExprThisRef(SourceInfo info, JClassType enclosingType) {
384     return new JThisRef(this, info, enclosingType);
385   }
386
387   public JReferenceType getFromTypeMap(String JavaDoc qualifiedBinaryOrSourceName) {
388     String JavaDoc srcTypeName = qualifiedBinaryOrSourceName.replace('$', '.');
389     return (JReferenceType) typeNameMap.get(srcTypeName);
390   }
391
392   public List JavaDoc/* <JsonObject> */getJsonTypeTable() {
393     return jsonTypeTable;
394   }
395
396   public JAbsentArrayDimension getLiteralAbsentArrayDimension() {
397     return literalAbsentArrayDim;
398   }
399
400   public JBooleanLiteral getLiteralBoolean(boolean z) {
401     return z ? literalTrue : literalFalse;
402   }
403
404   public JCharLiteral getLiteralChar(char c) {
405     // could be interned
406
return new JCharLiteral(this, c);
407   }
408
409   public JClassLiteral getLiteralClass(JType type) {
410     // could be interned
411
return new JClassLiteral(this, type);
412   }
413
414   public JClassSeed getLiteralClassSeed(JClassType type) {
415     // could be interned
416
return new JClassSeed(this, type);
417   }
418
419   public JDoubleLiteral getLiteralDouble(double d) {
420     // could be interned
421
return new JDoubleLiteral(this, d);
422   }
423
424   public JFloatLiteral getLiteralFloat(float f) {
425     // could be interned
426
return new JFloatLiteral(this, f);
427   }
428
429   public JIntLiteral getLiteralInt(int i) {
430     switch (i) {
431       case -1:
432         return literalIntNegOne;
433       case 0:
434         return literalIntZero;
435       case 1:
436         return literalIntOne;
437       default:
438         // could be interned
439
return new JIntLiteral(this, i);
440     }
441   }
442
443   public JLongLiteral getLiteralLong(long l) {
444     return new JLongLiteral(this, l);
445   }
446
447   public JNullLiteral getLiteralNull() {
448     return literalNull;
449   }
450
451   public JStringLiteral getLiteralString(char[] s) {
452     // should conslidate so we can build a string table in output code later?
453
return new JStringLiteral(this, String.valueOf(s));
454   }
455
456   public JStringLiteral getLiteralString(String JavaDoc s) {
457     // should conslidate so we can build a string table in output code later?
458
return new JStringLiteral(this, s);
459   }
460
461   public JField getNullField() {
462     if (nullField == null) {
463       nullField = new JField(this, null, "nullField", null, typeNull, false,
464           true, true);
465     }
466     return nullField;
467   }
468
469   public JMethod getNullMethod() {
470     if (nullMethod == null) {
471       nullMethod = new JsniMethod(this, null, "nullMethod", null, typeNull,
472           false, true, true);
473     }
474     return nullMethod;
475   }
476
477   public int getQueryId(JReferenceType elementType) {
478     Integer JavaDoc integer = (Integer JavaDoc) queryIds.get(elementType);
479     if (integer == null) {
480       return 0;
481     }
482
483     return integer.intValue();
484   }
485
486   public JMethod getRebindCreateMethod() {
487     return rebindCreateMethod;
488   }
489
490   public JClassType getSpecialArray() {
491     return typeSpecialArray;
492   }
493
494   public JClassType getSpecialCast() {
495     return typeSpecialCast;
496   }
497
498   public JClassType getSpecialExceptions() {
499     return typeSpecialExceptions;
500   }
501
502   public JField getSpecialField(String JavaDoc string) {
503     return (JField) specialFields.get(string);
504   }
505
506   public JClassType getSpecialJavaScriptObject() {
507     return typeSpecialJavaScriptObject;
508   }
509
510   public JMethod getSpecialMethod(String JavaDoc string) {
511     return (JMethod) specialMethods.get(string);
512   }
513
514   public JMethod getStaticImpl(JMethod method) {
515     return (JMethod) instanceToStaticMap.get(method);
516   }
517
518   public JArrayType getTypeArray(JType leafType, int dimensions) {
519     HashMap JavaDoc/* <JType, JArrayType> */typeToArrayType;
520
521     // Create typeToArrayType maps for index slots that don't exist yet.
522
//
523
for (int i = this.dimensions.size(); i < dimensions; ++i) {
524       typeToArrayType = new HashMap JavaDoc();
525       this.dimensions.add(typeToArrayType);
526     }
527
528     // Get the map for array having this number of dimensions (biased by one
529
// since we don't store non-arrays in there -- thus index 0 => 1 dim).
530
//
531
typeToArrayType = (HashMap JavaDoc) this.dimensions.get(dimensions - 1);
532
533     JArrayType arrayType = (JArrayType) typeToArrayType.get(leafType);
534     if (arrayType == null) {
535       arrayType = new JArrayType(this, leafType, dimensions);
536       arrayType.extnds = typeJavaLangObject;
537       allArrayTypes.add(arrayType);
538
539       /*
540        * TODO(later): should we setup the various array types as an inheritance
541        * heirarchy? Currently we're just doing all the heavy lifting in
542        * JTypeOracle. If we tried to setup inheritance, we'd have to recompute
543        * JTypeOracle if anything changed, so maybe this is better.
544        */

545       typeToArrayType.put(leafType, arrayType);
546     }
547
548     return arrayType;
549   }
550
551   public int getTypeId(JClassType classType) {
552     Integer JavaDoc integer = (Integer JavaDoc) typeIdMap.get(classType);
553     if (integer == null) {
554       return 0;
555     }
556
557     return integer.intValue();
558   }
559
560   public JClassType getTypeJavaLangClass() {
561     return typeClass;
562   }
563
564   public JClassType getTypeJavaLangObject() {
565     return typeJavaLangObject;
566   }
567
568   public JClassType getTypeJavaLangString() {
569     return typeString;
570   }
571
572   public JNullType getTypeNull() {
573     return typeNull;
574   }
575
576   public JPrimitiveType getTypePrimitiveBoolean() {
577     return typeBoolean;
578   }
579
580   public JPrimitiveType getTypePrimitiveByte() {
581     return typeByte;
582   }
583
584   public JPrimitiveType getTypePrimitiveChar() {
585     return typeChar;
586   }
587
588   public JPrimitiveType getTypePrimitiveDouble() {
589     return typeDouble;
590   }
591
592   public JPrimitiveType getTypePrimitiveFloat() {
593     return typeFloat;
594   }
595
596   public JPrimitiveType getTypePrimitiveInt() {
597     return typeInt;
598   }
599
600   public JPrimitiveType getTypePrimitiveLong() {
601     return typeLong;
602   }
603
604   public JPrimitiveType getTypePrimitiveShort() {
605     return typeShort;
606   }
607
608   public JType getTypeVoid() {
609     return typeVoid;
610   }
611
612   public void initTypeInfo(List JavaDoc/* <JClassType> */classes,
613       List JavaDoc/* <JsonObject> */jsonObjects) {
614     for (int i = 0, c = classes.size(); i < c; ++i) {
615       typeIdMap.put(classes.get(i), new Integer JavaDoc(i));
616     }
617     this.jsonTypeTable = jsonObjects;
618   }
619
620   public boolean isJavaScriptObject(JType type) {
621     if (type instanceof JClassType) {
622       return typeOracle.canTriviallyCast((JClassType) type,
623           typeSpecialJavaScriptObject);
624     }
625     return false;
626   }
627
628   public boolean isStaticImpl(JMethod method) {
629     return staticToInstanceMap.containsKey(method);
630   }
631
632   public void putIntoTypeMap(String JavaDoc qualifiedBinaryName, JReferenceType type) {
633     // Make it into a source type name.
634
//
635
String JavaDoc srcTypeName = qualifiedBinaryName.replace('$', '.');
636     typeNameMap.put(srcTypeName, type);
637   }
638
639   public void putStaticImpl(JMethod method, JMethod staticImpl) {
640     instanceToStaticMap.put(method, staticImpl);
641     staticToInstanceMap.put(staticImpl, method);
642   }
643
644   public JClassType rebind(JType type) {
645     JType result = type;
646     // Rebinds are always on a source type name.
647
String JavaDoc reqType = type.getName().replace('$', '.');
648     String JavaDoc reboundClassName;
649     try {
650       reboundClassName = rebindOracle.rebind(logger, reqType);
651     } catch (UnableToCompleteException e) {
652       // The fact that we already compute every rebind permutation before
653
// compiling should prevent this case from ever happening in real life.
654
//
655
throw new IllegalStateException JavaDoc("Unexpected failure to rebind '"
656           + reqType + "'");
657     }
658     if (reboundClassName != null) {
659       result = getFromTypeMap(reboundClassName);
660     }
661     assert (result != null);
662     assert (result instanceof JClassType);
663     return (JClassType) result;
664   }
665
666   public void recordQueryIds(Map JavaDoc/* <JReferenceType, Integer> */queryIds) {
667     this.queryIds = queryIds;
668   }
669
670   /**
671    * If <code>method</code> is a static impl method, returns the instance
672    * method that <code>method</code> is the implementation of. Otherwise,
673    * returns <code>null</code>.
674    */

675   public JMethod staticImplFor(JMethod method) {
676     return (JMethod) staticToInstanceMap.get(method);
677   }
678
679   public JReferenceType strongerType(JReferenceType type1, JReferenceType type2) {
680     if (type1 == type2) {
681       return type1;
682     }
683
684     if (typeOracle.canTriviallyCast(type1, type2)) {
685       return type1;
686     }
687
688     if (typeOracle.canTriviallyCast(type2, type1)) {
689       return type2;
690     }
691
692     // canot determine a strong type, just return the first one (this makes two
693
// "unrelated" interfaces work correctly in TypeTightener
694
return type1;
695   }
696
697   public void traverse(JVisitor visitor, Context ctx) {
698     if (visitor.visit(this, ctx)) {
699       visitor.accept(entryMethods);
700       visitor.accept(allTypes);
701     }
702     visitor.endVisit(this, ctx);
703   }
704
705   JReferenceType generalizeTypes(JReferenceType type1, JReferenceType type2) {
706     if (type1 == type2) {
707       return type1;
708     }
709
710     int classify1 = classifyType(type1);
711     int classify2 = classifyType(type2);
712
713     if (classify1 == IS_NULL) {
714       return type2;
715     }
716
717     if (classify2 == IS_NULL) {
718       return type1;
719     }
720
721     if (classify1 == classify2) {
722
723       // same basic kind of type
724
if (classify1 == IS_INTERFACE) {
725
726         if (typeOracle.canTriviallyCast(type1, type2)) {
727           return type2;
728         }
729
730         if (typeOracle.canTriviallyCast(type2, type1)) {
731           return type1;
732         }
733
734         // unrelated
735
return typeJavaLangObject;
736
737       } else if (classify1 == IS_ARRAY) {
738
739         JArrayType aType1 = (JArrayType) type1;
740         JArrayType aType2 = (JArrayType) type2;
741         int dims1 = aType1.getDims();
742         int dims2 = aType2.getDims();
743
744         int minDims = Math.min(dims1, dims2);
745         /*
746          * At a bare minimum, any two arrays generalize to an Object array with
747          * one less dim than the lesser of the two; that is, int[][][][] and
748          * String[][][] generalize to Object[][]. If minDims is 1, then they
749          * just generalize to Object.
750          */

751         JReferenceType minimalGeneralType;
752         if (minDims > 1) {
753           minimalGeneralType = getTypeArray(typeJavaLangObject, minDims - 1);
754         } else {
755           minimalGeneralType = typeJavaLangObject;
756         }
757
758         if (dims1 == dims2) {
759
760           // Try to generalize by leaf types
761
JType leafType1 = aType1.getLeafType();
762           JType leafType2 = aType2.getLeafType();
763
764           if (!(leafType1 instanceof JReferenceType)
765               || !(leafType2 instanceof JReferenceType)) {
766             return minimalGeneralType;
767           }
768
769           /*
770            * Both are reference types; the result is the generalization of the
771            * leaf types combined with the number of dims; that is, Foo[] and
772            * Bar[] generalize to X[] where X is the generalization of Foo and
773            * Bar.
774            */

775           JReferenceType leafRefType1 = (JReferenceType) leafType1;
776           JReferenceType leafRefType2 = (JReferenceType) leafType2;
777           JReferenceType leafGeneralization = generalizeTypes(leafRefType1,
778               leafRefType2);
779           return getTypeArray(leafGeneralization, dims1);
780
781         } else {
782
783           // Conflicting number of dims
784

785           // int[][] and Object[] generalize to Object[]
786
JArrayType lesser = dims1 < dims2 ? aType1 : aType2;
787           if (lesser.getLeafType() == typeJavaLangObject) {
788             return lesser;
789           }
790
791           // Totally unrelated
792
return minimalGeneralType;
793         }
794
795       } else {
796
797         assert (classify1 == IS_CLASS);
798
799         /*
800          * see how far each type is from object; walk the one who's farther up
801          * until they're even; then walk them up together until they meet (worst
802          * case at Object)
803          */

804         int distance1 = countSuperTypes(type1);
805         int distance2 = countSuperTypes(type2);
806         for (; distance1 > distance2; --distance1) {
807           type1 = type1.extnds;
808         }
809
810         for (; distance1 < distance2; --distance2) {
811           type2 = type2.extnds;
812         }
813
814         while (type1 != type2) {
815           type1 = type1.extnds;
816           type2 = type2.extnds;
817         }
818
819         return type1;
820       }
821     } else {
822
823       // different kinds of types
824
int lesser = Math.min(classify1, classify2);
825       int greater = Math.max(classify1, classify2);
826
827       JReferenceType tLesser = classify1 > classify2 ? type1 : type2;
828       JReferenceType tGreater = classify1 < classify2 ? type1 : type2;
829
830       if (lesser == IS_INTERFACE && greater == IS_CLASS) {
831
832         // just see if the class implements the interface
833
if (typeOracle.canTriviallyCast(tGreater, tLesser)) {
834           return tLesser;
835         }
836
837         // unrelated
838
return typeJavaLangObject;
839
840       } else {
841
842         // unrelated: the best commonality between an interface and array, or
843
// between an array and a class is Object
844
return typeJavaLangObject;
845       }
846     }
847   }
848
849   private int classifyType(JReferenceType type) {
850     if (type instanceof JNullType) {
851       return IS_NULL;
852     } else if (type instanceof JInterfaceType) {
853       return IS_INTERFACE;
854     } else if (type instanceof JArrayType) {
855       return IS_ARRAY;
856     } else if (type instanceof JClassType) {
857       return IS_CLASS;
858     }
859     throw new InternalCompilerException("Unknown reference type");
860   }
861
862   private int countSuperTypes(JReferenceType type) {
863     if (type instanceof JArrayType) {
864       JType leafType = ((JArrayType) type).getLeafType();
865       if (leafType instanceof JReferenceType) {
866         // however many steps from Foo[] -> Object[] + 1 for Object[]->Object
867
return countSuperTypes((JReferenceType) leafType) + 1;
868       } else {
869         // primitive array types can only cast up to object
870
return 1;
871       }
872     }
873     int count = 0;
874     while ((type = type.extnds) != null) {
875       ++count;
876     }
877     return count;
878   }
879
880 }
881
Popular Tags