001: /*
002: * Copyright 2007 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.dev.jjs.ast;
017:
018: import com.google.gwt.core.ext.TreeLogger;
019: import com.google.gwt.core.ext.UnableToCompleteException;
020: import com.google.gwt.dev.jdt.FindDeferredBindingSitesVisitor;
021: import com.google.gwt.dev.jdt.RebindOracle;
022: import com.google.gwt.dev.jjs.InternalCompilerException;
023: import com.google.gwt.dev.jjs.SourceInfo;
024: import com.google.gwt.dev.jjs.ast.js.JClassSeed;
025: import com.google.gwt.dev.jjs.ast.js.JsniMethodBody;
026: import com.google.gwt.dev.jjs.ast.js.JsonObject;
027:
028: import java.util.ArrayList;
029: import java.util.Arrays;
030: import java.util.Collection;
031: import java.util.Comparator;
032: import java.util.HashMap;
033: import java.util.HashSet;
034: import java.util.IdentityHashMap;
035: import java.util.Iterator;
036: import java.util.List;
037: import java.util.Map;
038: import java.util.Set;
039: import java.util.TreeSet;
040:
041: /**
042: * Root for the AST representing an entire Java program.
043: */
044: public class JProgram extends JNode {
045:
046: private static final Set<String> CODEGEN_TYPES_SET = new HashSet<String>(
047: Arrays.asList(new String[] { "com.google.gwt.lang.Array",
048: "com.google.gwt.lang.Cast",
049: "com.google.gwt.lang.Exceptions", }));
050:
051: private static final Set<String> INDEX_TYPES_SET = new HashSet<String>(
052: Arrays.asList(new String[] { "java.lang.Object",
053: "java.lang.String", "java.lang.Class",
054: "java.lang.CharSequence", "java.lang.Comparable",
055: "java.lang.Enum", "java.lang.Iterable",
056: "java.util.Iterator",
057: "com.google.gwt.core.client.JavaScriptObject",
058: "com.google.gwt.lang.Array" }));
059:
060: private static final int IS_ARRAY = 2;
061:
062: private static final int IS_CLASS = 3;
063:
064: private static final int IS_INTERFACE = 1;
065:
066: private static final int IS_NULL = 0;
067:
068: static {
069: INDEX_TYPES_SET.addAll(CODEGEN_TYPES_SET);
070: }
071:
072: public static boolean methodsDoMatch(JMethod method1,
073: JMethod method2) {
074: // static methods cannot match each other
075: if (method1.isStatic() || method2.isStatic()) {
076: return false;
077: }
078:
079: // names must be identical
080: if (!method1.getName().equals(method2.getName())) {
081: return false;
082: }
083:
084: // original parameter types must be identical
085: List<JType> params1 = method1.getOriginalParamTypes();
086: List<JType> params2 = method2.getOriginalParamTypes();
087: int params1size = params1.size();
088: if (params1size != params2.size()) {
089: return false;
090: }
091:
092: for (int i = 0; i < params1size; ++i) {
093: if (params1.get(i) != params2.get(i)) {
094: return false;
095: }
096: }
097: return true;
098: }
099:
100: private static String dotify(char[][] name) {
101: StringBuffer result = new StringBuffer();
102: for (int i = 0; i < name.length; ++i) {
103: if (i > 0) {
104: result.append('.');
105: }
106:
107: result.append(name[i]);
108: }
109: return result.toString();
110: }
111:
112: public final List<JClassType> codeGenTypes = new ArrayList<JClassType>();
113:
114: public final List<JMethod> entryMethods = new ArrayList<JMethod>();
115:
116: public final Map<String, HasEnclosingType> jsniMap = new HashMap<String, HasEnclosingType>();
117:
118: public final JTypeOracle typeOracle = new JTypeOracle(this );
119:
120: /**
121: * Sorted to avoid nondeterministic iteration.
122: */
123: private final Set<JArrayType> allArrayTypes = new TreeSet<JArrayType>(
124: new Comparator<JArrayType>() {
125: public int compare(JArrayType o1, JArrayType o2) {
126: int comp = o1.getDims() - o2.getDims();
127: if (comp != 0) {
128: return comp;
129: }
130: return o1.getName().compareTo(o2.getName());
131: }
132: });
133:
134: private final List<JReferenceType> allTypes = new ArrayList<JReferenceType>();
135:
136: private Map<JType, JClassLiteral> classLiterals = new HashMap<JType, JClassLiteral>();
137:
138: /**
139: * Each entry is a HashMap(JType => JArrayType), arranged such that the number
140: * of dimensions is that index (plus one) at which the JArrayTypes having that
141: * number of dimensions resides.
142: */
143: private final ArrayList<HashMap<JType, JArrayType>> dimensions = new ArrayList<HashMap<JType, JArrayType>>();
144:
145: private final Map<String, JField> indexedFields = new HashMap<String, JField>();
146:
147: private final Map<String, JMethod> indexedMethods = new HashMap<String, JMethod>();
148:
149: private final Map<String, JReferenceType> indexedTypes = new HashMap<String, JReferenceType>();
150:
151: private final Map<JMethod, JMethod> instanceToStaticMap = new IdentityHashMap<JMethod, JMethod>();
152:
153: private List<JsonObject> jsonTypeTable;
154:
155: private final JAbsentArrayDimension literalAbsentArrayDim = new JAbsentArrayDimension(
156: this );
157:
158: private final JBooleanLiteral literalFalse = new JBooleanLiteral(
159: this , false);
160:
161: private final JIntLiteral literalIntNegOne = new JIntLiteral(this ,
162: -1);
163:
164: private final JIntLiteral literalIntOne = new JIntLiteral(this , 1);
165:
166: private final JIntLiteral literalIntZero = new JIntLiteral(this , 0);
167:
168: private final JNullLiteral literalNull = new JNullLiteral(this );
169:
170: private final JBooleanLiteral literalTrue = new JBooleanLiteral(
171: this , true);
172:
173: private final TreeLogger logger;
174:
175: private JField nullField;
176:
177: private JMethod nullMethod;
178:
179: private Map<JReferenceType, Integer> queryIds;
180:
181: private JMethod rebindCreateMethod;
182:
183: private final RebindOracle rebindOracle;
184:
185: private final Map<JMethod, JMethod> staticToInstanceMap = new IdentityHashMap<JMethod, JMethod>();
186:
187: private final JPrimitiveType typeBoolean = new JPrimitiveType(this ,
188: "boolean", "Z", "java.lang.Boolean", literalFalse);
189:
190: private final JPrimitiveType typeByte = new JPrimitiveType(this ,
191: "byte", "B", "java.lang.Byte", literalIntZero);
192:
193: private final JPrimitiveType typeChar = new JPrimitiveType(this ,
194: "char", "C", "java.lang.Character",
195: getLiteralChar((char) 0));
196:
197: private JClassType typeClass;
198:
199: private final JPrimitiveType typeDouble = new JPrimitiveType(this ,
200: "double", "D", "java.lang.Double", getLiteralDouble(0));
201:
202: private final JPrimitiveType typeFloat = new JPrimitiveType(this ,
203: "float", "F", "java.lang.Float", getLiteralFloat(0));
204:
205: private Map<JClassType, Integer> typeIdMap = new HashMap<JClassType, Integer>();
206:
207: private final JPrimitiveType typeInt = new JPrimitiveType(this ,
208: "int", "I", "java.lang.Integer", literalIntZero);
209:
210: private JClassType typeJavaLangEnum;
211:
212: private JClassType typeJavaLangObject;
213:
214: private final JPrimitiveType typeLong = new JPrimitiveType(this ,
215: "long", "J", "java.lang.Long", getLiteralLong(0));
216:
217: private final Map<String, JReferenceType> typeNameMap = new HashMap<String, JReferenceType>();
218:
219: private final JNullType typeNull = new JNullType(this );
220:
221: private final JPrimitiveType typeShort = new JPrimitiveType(this ,
222: "short", "S", "java.lang.Short", literalIntZero);
223:
224: private JClassType typeSpecialJavaScriptObject;
225:
226: private JClassType typeString;
227:
228: private final JPrimitiveType typeVoid = new JPrimitiveType(this ,
229: "void", "V", "java.lang.Void", null);
230:
231: public JProgram(TreeLogger logger, RebindOracle rebindOracle) {
232: super (null, null);
233: this .logger = logger;
234: this .rebindOracle = rebindOracle;
235: }
236:
237: public void addEntryMethod(JMethod entryPoint) {
238: if (!entryMethods.contains(entryPoint)) {
239: entryMethods.add(entryPoint);
240: }
241: }
242:
243: /**
244: * Helper to create an assignment, used to initalize fields, etc.
245: */
246: public JExpressionStatement createAssignmentStmt(SourceInfo info,
247: JExpression lhs, JExpression rhs) {
248: JBinaryOperation assign = new JBinaryOperation(this , info, lhs
249: .getType(), JBinaryOperator.ASG, lhs, rhs);
250: return assign.makeStatement();
251: }
252:
253: public JClassType createClass(SourceInfo info, char[][] name,
254: boolean isAbstract, boolean isFinal) {
255: String sname = dotify(name);
256: JClassType x = new JClassType(this , info, sname, isAbstract,
257: isFinal);
258:
259: allTypes.add(x);
260: putIntoTypeMap(sname, x);
261:
262: if (CODEGEN_TYPES_SET.contains(sname)) {
263: codeGenTypes.add(x);
264: }
265: if (INDEX_TYPES_SET.contains(sname)) {
266: indexedTypes.put(x.getShortName(), x);
267: if (sname.equals("java.lang.Object")) {
268: typeJavaLangObject = x;
269: } else if (sname.equals("java.lang.String")) {
270: typeString = x;
271: } else if (sname.equals("java.lang.Enum")) {
272: typeJavaLangEnum = x;
273: } else if (sname.equals("java.lang.Class")) {
274: typeClass = x;
275: } else if (sname
276: .equals("com.google.gwt.core.client.JavaScriptObject")) {
277: typeSpecialJavaScriptObject = x;
278: }
279: }
280:
281: return x;
282: }
283:
284: public JEnumType createEnum(SourceInfo info, char[][] name) {
285: String sname = dotify(name);
286: JEnumType x = new JEnumType(this , info, sname);
287:
288: allTypes.add(x);
289: putIntoTypeMap(sname, x);
290:
291: return x;
292: }
293:
294: public JField createEnumField(SourceInfo info, char[] name,
295: JEnumType enclosingType, JClassType type, int ordinal) {
296: assert (name != null);
297: assert (type != null);
298: assert (ordinal >= 0);
299:
300: String sname = String.valueOf(name);
301: JEnumField x = new JEnumField(this , info, sname, ordinal,
302: enclosingType, type);
303: List<JEnumField> enumList = enclosingType.enumList;
304: while (ordinal >= enumList.size()) {
305: enumList.add(null);
306: }
307:
308: enumList.set(ordinal, x);
309:
310: enclosingType.fields.add(x);
311: return x;
312: }
313:
314: public JField createField(SourceInfo info, char[] name,
315: JReferenceType enclosingType, JType type, boolean isStatic,
316: boolean isFinal, boolean hasInitializer) {
317: assert (name != null);
318: assert (enclosingType != null);
319: assert (type != null);
320:
321: /*
322: * MAGIC: filled in during code gen, don't bother synthesizing dummy
323: * initializations.
324: */
325: if (enclosingType == typeJavaLangObject) {
326: hasInitializer = true;
327: }
328:
329: String sname = String.valueOf(name);
330: JField x = new JField(this , info, sname, enclosingType, type,
331: isStatic, isFinal, hasInitializer);
332:
333: if (indexedTypes.containsValue(enclosingType)) {
334: indexedFields.put(enclosingType.getShortName() + '.'
335: + sname, x);
336: }
337:
338: enclosingType.fields.add(x);
339: return x;
340: }
341:
342: public JInterfaceType createInterface(SourceInfo info, char[][] name) {
343: String sname = dotify(name);
344: JInterfaceType x = new JInterfaceType(this , info, sname);
345:
346: allTypes.add(x);
347: putIntoTypeMap(sname, x);
348:
349: if (INDEX_TYPES_SET.contains(sname)) {
350: indexedTypes.put(x.getShortName(), x);
351: }
352:
353: return x;
354: }
355:
356: public JLocal createLocal(SourceInfo info, char[] name, JType type,
357: boolean isFinal, JMethodBody enclosingMethodBody) {
358: assert (name != null);
359: assert (type != null);
360: assert (enclosingMethodBody != null);
361:
362: JLocal x = new JLocal(this , info, String.valueOf(name), type,
363: isFinal, enclosingMethodBody);
364:
365: enclosingMethodBody.locals.add(x);
366: return x;
367: }
368:
369: public JMethod createMethod(SourceInfo info, char[] name,
370: JReferenceType enclosingType, JType returnType,
371: boolean isAbstract, boolean isStatic, boolean isFinal,
372: boolean isPrivate, boolean isNative) {
373: assert (name != null);
374: assert (returnType != null);
375: assert (!isAbstract || !isNative);
376:
377: String sname = String.valueOf(name);
378: JMethod x = new JMethod(this , info, sname, enclosingType,
379: returnType, isAbstract, isStatic, isFinal, isPrivate);
380: if (isNative) {
381: x.setBody(new JsniMethodBody(this , info));
382: } else if (!isAbstract) {
383: x.setBody(new JMethodBody(this , info));
384: }
385:
386: if (sname
387: .equals(FindDeferredBindingSitesVisitor.REBIND_MAGIC_METHOD)
388: && enclosingType
389: .getName()
390: .equals(
391: FindDeferredBindingSitesVisitor.REBIND_MAGIC_CLASS)) {
392: rebindCreateMethod = x;
393: } else if (!isPrivate
394: && indexedTypes.containsValue(enclosingType)) {
395: indexedMethods.put(enclosingType.getShortName() + '.'
396: + sname, x);
397: }
398:
399: if (enclosingType != null) {
400: enclosingType.methods.add(x);
401: }
402:
403: return x;
404: }
405:
406: public JParameter createParameter(SourceInfo info, char[] name,
407: JType type, boolean isFinal, boolean isThis,
408: JMethod enclosingMethod) {
409: assert (name != null);
410: assert (type != null);
411: assert (enclosingMethod != null);
412:
413: JParameter x = new JParameter(this , info, String.valueOf(name),
414: type, isFinal, isThis, enclosingMethod);
415:
416: enclosingMethod.params.add(x);
417: return x;
418: }
419:
420: public JReferenceType generalizeTypes(
421: Collection<JReferenceType> types) {
422: assert (types != null);
423: assert (!types.isEmpty());
424: Iterator<JReferenceType> it = types.iterator();
425: JReferenceType curType = it.next();
426: while (it.hasNext()) {
427: curType = generalizeTypes(curType, it.next());
428: }
429: return curType;
430: }
431:
432: /**
433: * Returns a sorted set of array types, so the returned set can be iterated
434: * over without introducing nondeterminism.
435: */
436: public Set<JArrayType> getAllArrayTypes() {
437: return allArrayTypes;
438: }
439:
440: public List<JReferenceType> getDeclaredTypes() {
441: return allTypes;
442: }
443:
444: public JThisRef getExprThisRef(SourceInfo info,
445: JClassType enclosingType) {
446: return new JThisRef(this , info, enclosingType);
447: }
448:
449: public JReferenceType getFromTypeMap(
450: String qualifiedBinaryOrSourceName) {
451: String srcTypeName = qualifiedBinaryOrSourceName.replace('$',
452: '.');
453: return typeNameMap.get(srcTypeName);
454: }
455:
456: public JField getIndexedField(String string) {
457: return indexedFields.get(string);
458: }
459:
460: public JMethod getIndexedMethod(String string) {
461: return indexedMethods.get(string);
462: }
463:
464: public JReferenceType getIndexedType(String string) {
465: return indexedTypes.get(string);
466: }
467:
468: public JClassType getJavaScriptObject() {
469: return typeSpecialJavaScriptObject;
470: }
471:
472: public List<JsonObject> getJsonTypeTable() {
473: return jsonTypeTable;
474: }
475:
476: public JAbsentArrayDimension getLiteralAbsentArrayDimension() {
477: return literalAbsentArrayDim;
478: }
479:
480: public JBooleanLiteral getLiteralBoolean(boolean z) {
481: return z ? literalTrue : literalFalse;
482: }
483:
484: public JCharLiteral getLiteralChar(char c) {
485: // could be interned
486: return new JCharLiteral(this , c);
487: }
488:
489: public JClassLiteral getLiteralClass(JType type) {
490: JClassLiteral result = classLiterals.get(type);
491: if (result == null) {
492: result = new JClassLiteral(this , type);
493: classLiterals.put(type, result);
494: }
495: return result;
496: }
497:
498: public JClassSeed getLiteralClassSeed(JClassType type) {
499: // could be interned
500: return new JClassSeed(this , type);
501: }
502:
503: public JDoubleLiteral getLiteralDouble(double d) {
504: // could be interned
505: return new JDoubleLiteral(this , d);
506: }
507:
508: public JFloatLiteral getLiteralFloat(float f) {
509: // could be interned
510: return new JFloatLiteral(this , f);
511: }
512:
513: public JIntLiteral getLiteralInt(int i) {
514: switch (i) {
515: case -1:
516: return literalIntNegOne;
517: case 0:
518: return literalIntZero;
519: case 1:
520: return literalIntOne;
521: default:
522: // could be interned
523: return new JIntLiteral(this , i);
524: }
525: }
526:
527: public JLongLiteral getLiteralLong(long l) {
528: return new JLongLiteral(this , l);
529: }
530:
531: public JNullLiteral getLiteralNull() {
532: return literalNull;
533: }
534:
535: public JStringLiteral getLiteralString(char[] s) {
536: // should conslidate so we can build a string table in output code later?
537: return new JStringLiteral(this , String.valueOf(s));
538: }
539:
540: public JStringLiteral getLiteralString(String s) {
541: // should conslidate so we can build a string table in output code later?
542: return new JStringLiteral(this , s);
543: }
544:
545: public JField getNullField() {
546: if (nullField == null) {
547: nullField = new JField(this , null, "nullField", null,
548: typeNull, false, true, true);
549: }
550: return nullField;
551: }
552:
553: public JMethod getNullMethod() {
554: if (nullMethod == null) {
555: nullMethod = new JMethod(this , null, "nullMethod", null,
556: typeNull, false, false, true, true);
557: }
558: return nullMethod;
559: }
560:
561: public int getQueryId(JReferenceType elementType) {
562: Integer integer = queryIds.get(elementType);
563: if (integer == null) {
564: return 0;
565: }
566:
567: return integer.intValue();
568: }
569:
570: public JMethod getRebindCreateMethod() {
571: return rebindCreateMethod;
572: }
573:
574: public JMethod getStaticImpl(JMethod method) {
575: return instanceToStaticMap.get(method);
576: }
577:
578: public JArrayType getTypeArray(JType leafType, int dimensions) {
579: HashMap<JType, JArrayType> typeToArrayType;
580:
581: // Create typeToArrayType maps for index slots that don't exist yet.
582: //
583: for (int i = this .dimensions.size(); i < dimensions; ++i) {
584: typeToArrayType = new HashMap<JType, JArrayType>();
585: this .dimensions.add(typeToArrayType);
586: }
587:
588: // Get the map for array having this number of dimensions (biased by one
589: // since we don't store non-arrays in there -- thus index 0 => 1 dim).
590: //
591: typeToArrayType = this .dimensions.get(dimensions - 1);
592:
593: JArrayType arrayType = typeToArrayType.get(leafType);
594: if (arrayType == null) {
595: arrayType = new JArrayType(this , leafType, dimensions);
596: arrayType.extnds = typeJavaLangObject;
597: allArrayTypes.add(arrayType);
598:
599: /*
600: * TODO(later): should we setup the various array types as an inheritance
601: * heirarchy? Currently we're just doing all the heavy lifting in
602: * JTypeOracle. If we tried to setup inheritance, we'd have to recompute
603: * JTypeOracle if anything changed, so maybe this is better.
604: */
605: typeToArrayType.put(leafType, arrayType);
606: }
607:
608: return arrayType;
609: }
610:
611: public int getTypeId(JClassType classType) {
612: Integer integer = typeIdMap.get(classType);
613: if (integer == null) {
614: return 0;
615: }
616:
617: return integer.intValue();
618: }
619:
620: public JClassType getTypeJavaLangClass() {
621: return typeClass;
622: }
623:
624: public JClassType getTypeJavaLangEnum() {
625: return typeJavaLangEnum;
626: }
627:
628: public JClassType getTypeJavaLangObject() {
629: return typeJavaLangObject;
630: }
631:
632: public JClassType getTypeJavaLangString() {
633: return typeString;
634: }
635:
636: public JNullType getTypeNull() {
637: return typeNull;
638: }
639:
640: public JPrimitiveType getTypePrimitiveBoolean() {
641: return typeBoolean;
642: }
643:
644: public JPrimitiveType getTypePrimitiveByte() {
645: return typeByte;
646: }
647:
648: public JPrimitiveType getTypePrimitiveChar() {
649: return typeChar;
650: }
651:
652: public JPrimitiveType getTypePrimitiveDouble() {
653: return typeDouble;
654: }
655:
656: public JPrimitiveType getTypePrimitiveFloat() {
657: return typeFloat;
658: }
659:
660: public JPrimitiveType getTypePrimitiveInt() {
661: return typeInt;
662: }
663:
664: public JPrimitiveType getTypePrimitiveLong() {
665: return typeLong;
666: }
667:
668: public JPrimitiveType getTypePrimitiveShort() {
669: return typeShort;
670: }
671:
672: public JType getTypeVoid() {
673: return typeVoid;
674: }
675:
676: public void initTypeInfo(List<JClassType> classes,
677: List<JsonObject> jsonObjects) {
678: for (int i = 0, c = classes.size(); i < c; ++i) {
679: typeIdMap.put(classes.get(i), new Integer(i));
680: }
681: this .jsonTypeTable = jsonObjects;
682: }
683:
684: public boolean isClinit(JMethod method) {
685: JReferenceType enclosingType = method.getEnclosingType();
686: return (enclosingType != null)
687: && (method == enclosingType.methods.get(0));
688: }
689:
690: public boolean isJavaScriptObject(JType type) {
691: if (type instanceof JClassType) {
692: return typeOracle.canTriviallyCast((JClassType) type,
693: typeSpecialJavaScriptObject);
694: }
695: return false;
696: }
697:
698: public boolean isStaticImpl(JMethod method) {
699: return staticToInstanceMap.containsKey(method);
700: }
701:
702: public void putIntoTypeMap(String qualifiedBinaryName,
703: JReferenceType type) {
704: // Make it into a source type name.
705: //
706: String srcTypeName = qualifiedBinaryName.replace('$', '.');
707: typeNameMap.put(srcTypeName, type);
708: }
709:
710: public void putStaticImpl(JMethod method, JMethod staticImpl) {
711: instanceToStaticMap.put(method, staticImpl);
712: staticToInstanceMap.put(staticImpl, method);
713: }
714:
715: public JClassType rebind(JType type) {
716: JType result = type;
717: // Rebinds are always on a source type name.
718: String reqType = type.getName().replace('$', '.');
719: String reboundClassName;
720: try {
721: reboundClassName = rebindOracle.rebind(logger, reqType);
722: } catch (UnableToCompleteException e) {
723: // The fact that we already compute every rebind permutation before
724: // compiling should prevent this case from ever happening in real life.
725: //
726: throw new IllegalStateException(
727: "Unexpected failure to rebind '" + reqType + "'");
728: }
729: if (reboundClassName != null) {
730: result = getFromTypeMap(reboundClassName);
731: }
732: assert (result != null);
733: assert (result instanceof JClassType);
734: return (JClassType) result;
735: }
736:
737: public void recordQueryIds(Map<JReferenceType, Integer> queryIds) {
738: this .queryIds = queryIds;
739: }
740:
741: /**
742: * If <code>method</code> is a static impl method, returns the instance
743: * method that <code>method</code> is the implementation of. Otherwise,
744: * returns <code>null</code>.
745: */
746: public JMethod staticImplFor(JMethod method) {
747: return staticToInstanceMap.get(method);
748: }
749:
750: public JReferenceType strongerType(JReferenceType type1,
751: JReferenceType type2) {
752: if (type1 == type2) {
753: return type1;
754: }
755:
756: if (typeOracle.canTriviallyCast(type1, type2)) {
757: return type1;
758: }
759:
760: if (typeOracle.canTriviallyCast(type2, type1)) {
761: return type2;
762: }
763:
764: // cannot determine a strong type, just return the first one (this makes two
765: // "unrelated" interfaces work correctly in TypeTightener
766: return type1;
767: }
768:
769: public void traverse(JVisitor visitor, Context ctx) {
770: if (visitor.visit(this , ctx)) {
771: visitor.accept(entryMethods);
772: visitor.accept(allTypes);
773: }
774: visitor.endVisit(this , ctx);
775: }
776:
777: JReferenceType generalizeTypes(JReferenceType type1,
778: JReferenceType type2) {
779: if (type1 == type2) {
780: return type1;
781: }
782:
783: int classify1 = classifyType(type1);
784: int classify2 = classifyType(type2);
785:
786: if (classify1 == IS_NULL) {
787: return type2;
788: }
789:
790: if (classify2 == IS_NULL) {
791: return type1;
792: }
793:
794: if (classify1 == classify2) {
795:
796: // same basic kind of type
797: if (classify1 == IS_INTERFACE) {
798:
799: if (typeOracle.canTriviallyCast(type1, type2)) {
800: return type2;
801: }
802:
803: if (typeOracle.canTriviallyCast(type2, type1)) {
804: return type1;
805: }
806:
807: // unrelated
808: return typeJavaLangObject;
809:
810: } else if (classify1 == IS_ARRAY) {
811:
812: JArrayType aType1 = (JArrayType) type1;
813: JArrayType aType2 = (JArrayType) type2;
814: int dims1 = aType1.getDims();
815: int dims2 = aType2.getDims();
816:
817: int minDims = Math.min(dims1, dims2);
818: /*
819: * At a bare minimum, any two arrays generalize to an Object array with
820: * one less dim than the lesser of the two; that is, int[][][][] and
821: * String[][][] generalize to Object[][]. If minDims is 1, then they
822: * just generalize to Object.
823: */
824: JReferenceType minimalGeneralType;
825: if (minDims > 1) {
826: minimalGeneralType = getTypeArray(
827: typeJavaLangObject, minDims - 1);
828: } else {
829: minimalGeneralType = typeJavaLangObject;
830: }
831:
832: if (dims1 == dims2) {
833:
834: // Try to generalize by leaf types
835: JType leafType1 = aType1.getLeafType();
836: JType leafType2 = aType2.getLeafType();
837:
838: if (!(leafType1 instanceof JReferenceType)
839: || !(leafType2 instanceof JReferenceType)) {
840: return minimalGeneralType;
841: }
842:
843: /*
844: * Both are reference types; the result is the generalization of the
845: * leaf types combined with the number of dims; that is, Foo[] and
846: * Bar[] generalize to X[] where X is the generalization of Foo and
847: * Bar.
848: */
849: JReferenceType leafRefType1 = (JReferenceType) leafType1;
850: JReferenceType leafRefType2 = (JReferenceType) leafType2;
851: JReferenceType leafGeneralization = generalizeTypes(
852: leafRefType1, leafRefType2);
853: return getTypeArray(leafGeneralization, dims1);
854:
855: } else {
856:
857: // Conflicting number of dims
858:
859: // int[][] and Object[] generalize to Object[]
860: JArrayType lesser = dims1 < dims2 ? aType1 : aType2;
861: if (lesser.getLeafType() == typeJavaLangObject) {
862: return lesser;
863: }
864:
865: // Totally unrelated
866: return minimalGeneralType;
867: }
868:
869: } else {
870:
871: assert (classify1 == IS_CLASS);
872:
873: /*
874: * see how far each type is from object; walk the one who's farther up
875: * until they're even; then walk them up together until they meet (worst
876: * case at Object)
877: */
878: int distance1 = countSuperTypes(type1);
879: int distance2 = countSuperTypes(type2);
880: for (; distance1 > distance2; --distance1) {
881: type1 = type1.extnds;
882: }
883:
884: for (; distance1 < distance2; --distance2) {
885: type2 = type2.extnds;
886: }
887:
888: while (type1 != type2) {
889: type1 = type1.extnds;
890: type2 = type2.extnds;
891: }
892:
893: return type1;
894: }
895: } else {
896:
897: // different kinds of types
898: int lesser = Math.min(classify1, classify2);
899: int greater = Math.max(classify1, classify2);
900:
901: JReferenceType tLesser = classify1 > classify2 ? type1
902: : type2;
903: JReferenceType tGreater = classify1 < classify2 ? type1
904: : type2;
905:
906: if (lesser == IS_INTERFACE && greater == IS_CLASS) {
907:
908: // just see if the class implements the interface
909: if (typeOracle.canTriviallyCast(tGreater, tLesser)) {
910: return tLesser;
911: }
912:
913: // unrelated
914: return typeJavaLangObject;
915:
916: } else {
917:
918: // unrelated: the best commonality between an interface and array, or
919: // between an array and a class is Object
920: return typeJavaLangObject;
921: }
922: }
923: }
924:
925: private int classifyType(JReferenceType type) {
926: if (type instanceof JNullType) {
927: return IS_NULL;
928: } else if (type instanceof JInterfaceType) {
929: return IS_INTERFACE;
930: } else if (type instanceof JArrayType) {
931: return IS_ARRAY;
932: } else if (type instanceof JClassType) {
933: return IS_CLASS;
934: }
935: throw new InternalCompilerException("Unknown reference type");
936: }
937:
938: private int countSuperTypes(JReferenceType type) {
939: if (type instanceof JArrayType) {
940: JType leafType = ((JArrayType) type).getLeafType();
941: if (leafType instanceof JReferenceType) {
942: // however many steps from Foo[] -> Object[] + 1 for Object[]->Object
943: return countSuperTypes((JReferenceType) leafType) + 1;
944: } else {
945: // primitive array types can only cast up to object
946: return 1;
947: }
948: }
949: int count = 0;
950: while ((type = type.extnds) != null) {
951: ++count;
952: }
953: return count;
954: }
955:
956: }
|