| Type.java |
1 // Copyright 2012- Bill Campbell, Swami Iyer and Bahar Akbal-Delibas
2
3 package jminusminus;
4
5 import java.lang.reflect.Array;
6 import java.lang.reflect.Modifier;
7
8 import java.util.Arrays;
9 import java.util.ArrayList;
10 import java.util.Hashtable;
11
12 /**
13 * A class for representing j-- types. All types are represented underneath (in the classRep
14 * field) by Java objects of type Class. These objects represent types in Java, so this should
15 * ease our interfacing with existing Java classes.
16 * <p>
17 * Class types (reference types that are represented by the identifiers introduced in class
18 * declarations) are represented using TypeName. So for now, every TypeName represents a class.
19 * In the future, TypeName could be extended to represent interfaces or enumerations.
20 * <p>
21 * IdentifierTypes must be "resolved" at some point, so that all Types having the same name refer
22 * to the same Type object. The resolve() method does this.
23 */
24 class Type {
25 // The Type's internal (Java) representation.
26 private Class<?> classRep;
27
28 // Maps type names to their Type representations.
29 private static Hashtable<String, Type> types = new Hashtable<String, Type>();
30
31 /**
32 * The int type.
33 */
34 public final static Type INT = typeFor(int.class);
35
36 /**
37 * The char type.
38 */
39 public final static Type CHAR = typeFor(char.class);
40
41 /**
42 * The boolean type.
43 */
44 public final static Type BOOLEAN = typeFor(boolean.class);
45
46 /**
47 * The long type.
48 */
49 public final static Type LONG = typeFor(long.class);
50
51 /**
52 * The double type.
53 */
54 public final static Type DOUBLE = typeFor(double.class);
55
56 /**
57 * The java.lang.Integer type.
58 */
59 public final static Type BOXED_INT = typeFor(java.lang.Integer.class);
60
61 /**
62 * The java.lang.Character type.
63 */
64 public final static Type BOXED_CHAR = typeFor(java.lang.Character.class);
65
66 /**
67 * The java.lang.Boolean type.
68 */
69 public final static Type BOXED_BOOLEAN = typeFor(java.lang.Boolean.class);
70
71 /**
72 * The java.lang.Long type.
73 */
74 public final static Type BOXED_LONG = typeFor(java.lang.Long.class);
75
76 /**
77 * The java.lang.Double type.
78 */
79 public final static Type BOXED_DOUBLE = typeFor(java.lang.Double.class);
80
81 /**
82 * The java.lang.String type.
83 */
84 public final static Type STRING = typeFor(java.lang.String.class);
85
86 /**
87 * The java.lang.Object type.
88 */
89 public final static Type OBJECT = typeFor(java.lang.Object.class);
90
91 /**
92 * The void type.
93 */
94 public final static Type VOID = typeFor(void.class);
95
96 /**
97 * The null type.
98 */
99 public final static Type NULLTYPE = new Type(java.lang.Object.class);
100
101 /**
102 * The "any" type (denotes wild expressions).
103 */
104 public final static Type ANY = new Type(null);
105
106 /**
107 * A type marker indicating a constructor (having no return type).
108 */
109 public final static Type CONSTRUCTOR = new Type(null);
110
111 /**
112 * This constructor is to keep the compiler happy.
113 */
114 protected Type() {
115 super();
116 }
117
118 /**
119 * Constructs and returns a representation for a type from its (Java) class representation,
120 * making sure there is a unique representation for each unique type.
121 *
122 * @param classRep the Java class representation.
123 * @return a type representation of classRep.
124 */
125 public static Type typeFor(Class<?> classRep) {
126 if (types.get(descriptorFor(classRep)) == null) {
127 types.put(descriptorFor(classRep), new Type(classRep));
128 }
129 return types.get(descriptorFor(classRep));
130 }
131
132 /**
133 * Returns the class representation for this type.
134 *
135 * @return the class representation for this type.
136 */
137 public Class<?> classRep() {
138 return classRep;
139 }
140
141 /**
142 * Sets the class representation of this type to the specified partial class.
143 *
144 * @param classRep the partial class.
145 */
146 public void setClassRep(Class<?> classRep) {
147 this.classRep = classRep;
148 }
149
150 /**
151 * Returns true if this type has the same descriptor as other, and false otherwise.
152 *
153 * @param other the other type.
154 * @return true if this type has the same descriptor as other, and false otherwise.
155 */
156 public boolean equals(Type other) {
157 return this.toDescriptor().equals(other.toDescriptor());
158 }
159
160 /**
161 * Returns true if this is an array type, and false otherwise.
162 *
163 * @return true if this is an array type, and false otherwise.
164 */
165 public boolean isArray() {
166 return classRep.isArray();
167 }
168
169 /**
170 * Returns an array type's component type.
171 *
172 * @return an array type's component type.
173 */
174 public Type componentType() {
175 return typeFor(classRep.getComponentType());
176 }
177
178 /**
179 * Returns this type's super type, or null.
180 *
181 * @return this type's super type, or null.
182 */
183 public Type superClass() {
184 return classRep == null || classRep.getSuperclass() == null ? null :
185 typeFor(classRep.getSuperclass());
186 }
187
188 /**
189 * Returns true if this is a primitive type, and false otherwise.
190 *
191 * @return true if this is a primitive type, and false otherwise.
192 */
193 public boolean isPrimitive() {
194 return classRep.isPrimitive();
195 }
196
197 /**
198 * Returns true if this is an interface type, and false otherwise.
199 *
200 * @return true if this is an interface type, and false otherwise.
201 */
202 public boolean isInterface() {
203 return classRep.isInterface();
204 }
205
206 /**
207 * Returns true if this is a reference type, and false otherwise.
208 *
209 * @return true if this is a reference type, and false otherwise.
210 */
211 public boolean isReference() {
212 return !isPrimitive();
213 }
214
215 /**
216 * Returns true of this type is declared final, and false otherwise.
217 *
218 * @return true of this type is declared final, and false otherwise.
219 */
220 public boolean isFinal() {
221 return Modifier.isFinal(classRep.getModifiers());
222 }
223
224 /**
225 * Returns true of this type is declared abstract, and false otherwise.
226 *
227 * @return true of this type is declared abstract, and false otherwise.
228 */
229 public boolean isAbstract() {
230 return Modifier.isAbstract(classRep.getModifiers());
231 }
232
233 /**
234 * Returns true if this is a supertype of other, and false otherwise.
235 *
236 * @param that the candidate subtype.
237 * @return true if this is a supertype of other, and false otherwise.
238 */
239 public boolean isJavaAssignableFrom(Type that) {
240 return this.classRep.isAssignableFrom(that.classRep);
241 }
242
243 /**
244 * Returns a list of this class' abstract methods.
245 * <p>
246 * It has abstract methods if:
247 * <ol>
248 * <li>Any method declared in the class is abstract or
249 * <li>its superclass has an abstract method which is not overridden here.
250 * </ol>
251 *
252 * @return a list of this class' abstract methods.
253 */
254 public ArrayList<Method> abstractMethods() {
255 ArrayList<Method> inheritedAbstractMethods = superClass() == null ? new ArrayList<Method>()
256 : superClass().abstractMethods();
257 ArrayList<Method> abstractMethods = new ArrayList<Method>();
258 ArrayList<Method> declaredConcreteMethods = declaredConcreteMethods();
259 ArrayList<Method> declaredAbstractMethods = declaredAbstractMethods();
260 abstractMethods.addAll(declaredAbstractMethods);
261 for (Method method : inheritedAbstractMethods) {
262 if (!declaredConcreteMethods.contains(method) &&
263 !declaredAbstractMethods.contains(method)) {
264 abstractMethods.add(method);
265 }
266 }
267 return abstractMethods;
268 }
269
270 /**
271 * Returns a list of this class' declared abstract methods.
272 *
273 * @return a list of this class' declared abstract methods.
274 */
275 private ArrayList<Method> declaredAbstractMethods() {
276 ArrayList<Method> declaredAbstractMethods = new ArrayList<Method>();
277 for (java.lang.reflect.Method method : classRep.getDeclaredMethods()) {
278 if (Modifier.isAbstract(method.getModifiers())) {
279 declaredAbstractMethods.add(new Method(method));
280 }
281 }
282 return declaredAbstractMethods;
283 }
284
285 /**
286 * Returns a list of this class' declared concrete methods.
287 *
288 * @return a list of this class' declared concrete methods.
289 */
290 private ArrayList<Method> declaredConcreteMethods() {
291 ArrayList<Method> declaredConcreteMethods = new ArrayList<Method>();
292 for (java.lang.reflect.Method method : classRep.getDeclaredMethods()) {
293 if (!Modifier.isAbstract(method.getModifiers())) {
294 declaredConcreteMethods.add(new Method(method));
295 }
296 }
297 return declaredConcreteMethods;
298 }
299
300 /**
301 * An assertion that this type matches one of the specified types. If there is no match, an
302 * error is reported.
303 *
304 * @param line the line near which the mismatch occurs.
305 * @param expectedTypes expected types.
306 */
307 public void mustMatchOneOf(int line, Type... expectedTypes) {
308 if (this == Type.ANY) {
309 return;
310 }
311 for (Type type : expectedTypes) {
312 if (matchesExpected(type)) {
313 return;
314 }
315 }
316 JAST.compilationUnit.reportSemanticError(line,
317 "Type %s doesn't match any of the expected types %s", this,
318 Arrays.toString(expectedTypes));
319 }
320
321 /**
322 * An assertion that this type matches the specified type. If there is no match, an error is
323 * reported.
324 *
325 * @param line the line near which the mismatch occurs.
326 * @param expectedType type with which to match.
327 */
328 public void mustMatchExpected(int line, Type expectedType) {
329 if (!matchesExpected(expectedType)) {
330 JAST.compilationUnit.reportSemanticError(line, "Type %s doesn't match type %s", this,
331 expectedType);
332 }
333 }
334
335 /**
336 * Returns true if this type matches expected, and false otherwise.
337 *
338 * @param expected the type that this might match.
339 * @return true if this type matches expected, and false otherwise.
340 */
341 public boolean matchesExpected(Type expected) {
342 return this == Type.ANY || expected == Type.ANY ||
343 (this == Type.NULLTYPE && expected.isReference()) || this.equals(expected);
344 }
345
346 /**
347 * Returns true if the argument types match, and false otherwise.
348 *
349 * @param argTypes1 arguments (classReps) of one method.
350 * @param argTypes2 arguments (classReps) of another method.
351 * @return true if the argument types match, and false otherwise.
352 */
353 public static boolean argTypesMatch(Class<?>[] argTypes1, Class<?>[] argTypes2) {
354 if (argTypes1.length != argTypes2.length) {
355 return false;
356 }
357 for (int i = 0; i < argTypes1.length; i++) {
358 if (!Type.descriptorFor(argTypes1[i]).equals(Type.descriptorFor(argTypes2[i]))) {
359 return false;
360 }
361 }
362 return true;
363 }
364
365 /**
366 * Returns the simple (unqualified) name of this type.
367 *
368 * @return the simple (unqualified) name of this type.
369 */
370 public String simpleName() {
371 return classRep.getSimpleName();
372 }
373
374 /**
375 * Returns a string representation of this type.
376 *
377 * @return a string representation of this type.
378 */
379 public String toString() {
380 return toJava(this.classRep);
381 }
382
383 /**
384 * Returns the JVM descriptor of this type.
385 *
386 * @return the JVM descriptor of this type.
387 */
388 public String toDescriptor() {
389 return descriptorFor(classRep);
390 }
391
392 /**
393 * Returns the JVM representation of this type's name.
394 *
395 * @return the JVM representation of this type's name.
396 */
397 public String jvmName() {
398 return this.isArray() || this.isPrimitive() ?
399 this.toDescriptor() : classRep.getName().replace('.', '/');
400 }
401
402 /**
403 * Returns this type's package name.
404 *
405 * @return this type's package name.
406 */
407 public String packageName() {
408 String name = toString();
409 return name.lastIndexOf('.') == -1 ? "" : name.substring(0, name.lastIndexOf('.') - 1);
410 }
411
412 /**
413 * Returns a string representation for a type being appended to a StringBuffer (for the + and
414 * += operations over strings).
415 *
416 * @return a string representation for a type being appended to a StringBuffer.
417 */
418 public String argumentTypeForAppend() {
419 return this == Type.STRING || this.isPrimitive() ? toDescriptor() : "Ljava/lang/Object;";
420 }
421
422 /**
423 * Finds and returns a method in this type having the given name and argument types, or null.
424 *
425 * @param name the method name.
426 * @param argTypes the argument types.
427 * @return a method in this type having the given name and argument types, or null.
428 */
429 public Method methodFor(String name, Type[] argTypes) {
430 Class[] classes = new Class[argTypes.length];
431 for (int i = 0; i < argTypes.length; i++) {
432 classes[i] = argTypes[i].classRep;
433 }
434 Class cls = classRep;
435
436 // Search this class and all superclasses.
437 while (cls != null) {
438 java.lang.reflect.Method[] methods = cls.getDeclaredMethods();
439 for (java.lang.reflect.Method method : methods) {
440 if (method.getName().equals(name) && Type.argTypesMatch(classes,
441 method.getParameterTypes())) {
442 return new Method(method);
443 }
444 }
445 cls = cls.getSuperclass();
446 }
447
448 return null;
449 }
450
451 /**
452 * Finds and returns a constructor in this type having the given argument types, or null.
453 *
454 * @param argTypes the argument types.
455 * @return a constructor in this type having the given argument types, or null.
456 */
457 public Constructor constructorFor(Type[] argTypes) {
458 Class[] classes = new Class[argTypes.length];
459 for (int i = 0; i < argTypes.length; i++) {
460 classes[i] = argTypes[i].classRep;
461 }
462
463 // Search only this class (we don't inherit constructors).
464 java.lang.reflect.Constructor[] constructors = classRep.getDeclaredConstructors();
465 for (java.lang.reflect.Constructor constructor : constructors) {
466 if (argTypesMatch(classes, constructor.getParameterTypes())) {
467 return new Constructor(constructor);
468 }
469 }
470
471 return null;
472 }
473
474 /**
475 * Finds and returns a field in this type having the given name, or null.
476 *
477 * @param name the name of the field we want.
478 * @return a field in this type having the given name, or null.
479 */
480 public Field fieldFor(String name) {
481 Class<?> cls = classRep;
482 while (cls != null) {
483 java.lang.reflect.Field[] fields = cls.getDeclaredFields();
484 for (java.lang.reflect.Field field : fields) {
485 if (field.getName().equals(name)) {
486 return new Field(field);
487 }
488 }
489 cls = cls.getSuperclass();
490 }
491 return null;
492 }
493
494 /**
495 * Returns a string representation of an array of argument types.
496 *
497 * @param argTypes the array of argument types.
498 * @return a string representation of an array of argument types.
499 */
500 public static String argTypesAsString(Type[] argTypes) {
501 if (argTypes.length == 0) {
502 return "()";
503 } else {
504 String str = "(" + argTypes[0].toString();
505 for (int i = 1; i < argTypes.length; i++) {
506 str += "," + argTypes[i];
507 }
508 str += ")";
509 return str;
510 }
511 }
512
513 /**
514 * Returns true if the member is accessible from this type, and false otherwise.
515 *
516 * @param line the line in which the access occurs.
517 * @param member the member being accessed.
518 * @return true if the member is accessible from this type, and false otherwise.
519 */
520 public boolean checkAccess(int line, Member member) {
521 if (!checkAccess(line, classRep, member.declaringType().classRep)) {
522 return false;
523 }
524 // The member must be either public, protected, or private.
525 if (member.isPublic()) {
526 return true;
527 }
528 java.lang.Package p1 = classRep.getPackage();
529 java.lang.Package p2 = member.declaringType().classRep.getPackage();
530 if ((p1 == null ? "" : p1.getName()).equals((p2 == null ? "" : p2.getName()))) {
531 return true;
532 }
533 if (member.isProtected()) {
534 if (classRep.getPackage().getName().equals(
535 member.declaringType().classRep.getPackage().getName())
536 || typeFor(member.getClass().getDeclaringClass())
537 .isJavaAssignableFrom(this)) {
538 return true;
539 } else {
540 JAST.compilationUnit.reportSemanticError(line,
541 "The protected member, " + member.name() + ", is not accessible.");
542 return false;
543 }
544 }
545 if (member.isPrivate()) {
546 if (descriptorFor(classRep).equals(
547 descriptorFor(member.member().getDeclaringClass()))) {
548 return true;
549 } else {
550 JAST.compilationUnit.reportSemanticError(line,
551 "The private member, " + member.name() + ", is not accessible.");
552 return false;
553 }
554 }
555
556 // Otherwise, the member has default access.
557 if (packageName().equals(member.declaringType().packageName())) {
558 return true;
559 } else {
560 JAST.compilationUnit.reportSemanticError(line, "The member, " + member.name() +
561 ", is not accessible because it's in a different package.");
562 return false;
563 }
564 }
565
566 /**
567 * Returns true if the target type is accessible from this type, and false otherwise.
568 *
569 * @param line line in which the access occurs.
570 * @param targetType the type being accessed.
571 * @return true if the target type is accessible from this type, and false otherwise.
572 */
573 public boolean checkAccess(int line, Type targetType) {
574 if (targetType.isPrimitive()) {
575 return true;
576 }
577 if (targetType.isArray()) {
578 return this.checkAccess(line, targetType.componentType());
579 }
580 return checkAccess(line, classRep, targetType.classRep);
581 }
582
583 /**
584 * Returns true if the referenced type is accessible from the referencing type, and false
585 * otherwise.
586 *
587 * @param line the line in which the access occurs.
588 * @param referencingType the type attempting the access.
589 * @param type the type that we want to access.
590 * @return true if the referenced type is accessible from the referencing type, and false
591 * otherwise.
592 */
593 public static boolean checkAccess(int line, Class referencingType, Class type) {
594 java.lang.Package p1 = referencingType.getPackage();
595 java.lang.Package p2 = type.getPackage();
596 if (Modifier.isPublic(type.getModifiers()) ||
597 (p1 == null ? "" : p1.getName()).equals((p2 == null ? "" : p2.getName()))) {
598 return true;
599 } else {
600 JAST.compilationUnit.reportSemanticError(line, "The type, " + type.getCanonicalName() +
601 ", is not accessible from " + referencingType.getCanonicalName());
602 return false;
603 }
604 }
605
606 /**
607 * Resolves this type in the given context and returns the resolved type.
608 *
609 * @param context context in which the names are resolved.
610 * @return the resolved type.
611 */
612 public Type resolve(Context context) {
613 return this;
614 }
615
616 /**
617 * Returns a signature for reporting unfound methods and constructors.
618 *
619 * @param name the message or type name.
620 * @param argTypes the actual argument types.
621 * @return a signature for reporting unfound methods and constructors.
622 */
623 public static String signatureFor(String name, Type[] argTypes) {
624 String signature = name + "(";
625 if (argTypes.length > 0) {
626 signature += argTypes[0].toString();
627 for (int i = 1; i < argTypes.length; i++) {
628 signature += "," + argTypes[i].toString();
629 }
630 }
631 signature += ")";
632 return signature;
633 }
634
635 // Constructs a representation for a type from its Java (Class) representation. Use typeFor()
636 // that maps types having like classReps to like Types.
637 private Type(Class<?> classRep) {
638 this.classRep = classRep;
639 }
640
641 // Returns the JVM descriptor of a type's class representation.
642 private static String descriptorFor(Class<?> classRep) {
643 return classRep == null ? "V" : classRep == void.class ? "V"
644 : classRep.isArray() ? "[" + descriptorFor(classRep.getComponentType())
645 : classRep.isPrimitive() ? (classRep == int.class ? "I"
646 : classRep == char.class ? "C"
647 : classRep == boolean.class ? "Z"
648 : classRep == double.class ? "D"
649 : classRep == long.class ? "J" : "?")
650 : "L" + classRep.getName().replace('.', '/') + ";";
651 }
652
653 // Returns the Java (and so j--) denotation for the specified type.
654 private static String toJava(Class classRep) {
655 return classRep.isArray() ? toJava(classRep.getComponentType()) + "[]" : classRep.getName();
656 }
657 }
658
659 /**
660 * A representation of any reference type that can be denoted as a (possibly qualified) identifier.
661 */
662 class TypeName extends Type {
663 // The line in which the identifier occurs in the source file.
664 private int line;
665
666 // The identifier's name.
667 private String name;
668
669 /**
670 * Constructs a TypeName.
671 *
672 * @param line the line in which the identifier occurs in the source file.
673 * @param name fully qualified name for the identifier.
674 */
675 public TypeName(int line, String name) {
676 this.line = line;
677 this.name = name;
678 }
679
680 /**
681 * Returns the line in which the identifier occurs in the source file.
682 *
683 * @return the line in which the identifier occurs in the source file.
684 */
685 public int line() {
686 return line;
687 }
688
689 /**
690 * {@inheritDoc}
691 */
692 public String jvmName() {
693 return name.replace('.', '/');
694 }
695
696 /**
697 * {@inheritDoc}
698 */
699 public String toDescriptor() {
700 return "L" + jvmName() + ";";
701 }
702
703 /**
704 * {@inheritDoc}
705 */
706 public String toString() {
707 return name;
708 }
709
710 /**
711 * {@inheritDoc}
712 */
713 public String simpleName() {
714 return name.substring(name.lastIndexOf('.') + 1);
715 }
716
717 /**
718 * {@inheritDoc}
719 */
720 public Type resolve(Context context) {
721 Type resolvedType = context.lookupType(name);
722 if (resolvedType == null) {
723 // Try loading a type with the given fullname.
724 try {
725 resolvedType = typeFor(Class.forName(name));
726 context.addType(line, resolvedType);
727 } catch (Exception e) {
728 JAST.compilationUnit.reportSemanticError(line, "Unable to locate %s", name);
729 resolvedType = Type.ANY;
730 }
731 }
732 return resolvedType;
733 }
734 }
735
736 /**
737 * A representation of an array type. It is built by the Parser to stand in for a Type until the
738 * analyze() phase, at which point it is resolved to an actual Type object (having a Class that
739 * identifies it).
740 */
741 class ArrayTypeName extends Type {
742 // The array's base or component type.
743 private Type componentType;
744
745 /**
746 * Constructs an ArrayTypeName given its component type.
747 *
748 * @param componentType the type of the array's elements.
749 */
750 public ArrayTypeName(Type componentType) {
751 this.componentType = componentType;
752 }
753
754 /**
755 * {@inheritDoc}
756 */
757 public Type componentType() {
758 return componentType;
759 }
760
761 /**
762 * {@inheritDoc}
763 */
764 public String toDescriptor() {
765 return "[" + componentType.toDescriptor();
766 }
767
768 /**
769 * {@inheritDoc}
770 */
771 public String toString() {
772 return componentType.toString() + "[]";
773 }
774
775 /**
776 * {@inheritDoc}
777 */
778 public Type resolve(Context context) {
779 componentType = componentType.resolve(context);
780 Class classRep = Array.newInstance(componentType().classRep(), 0).getClass();
781 return Type.typeFor(classRep);
782 }
783 }
784