1   // Copyright 2013 Bill Campbell, Swami Iyer and Bahar Akbal-Delibas
2   
3   package jminusminus;
4   
5   import java.util.Hashtable;
6   import static jminusminus.CLConstants.*;
7   
8   /**
9    * The AST for an cast expression, which has both a cast (a type) and the
10   * expression to be cast.
11   */
12  
13  class JCastOp extends JExpression {
14  
15      /** The cast. */
16      private Type cast;
17  
18      /** The expression we're casting. */
19      private JExpression expr;
20  
21      /** The conversions table. */
22      private static Conversions conversions;
23  
24      /** The converter to use for this cast. */
25      private Converter converter;
26  
27      /**
28       * Construct an AST node for a cast operation from its line number, cast,
29       * and expression.
30       * 
31       * @param line
32       *            the line in which the operation occurs in the source file.
33       * @param cast
34       *            the type we're casting our expression as.
35       * @param expr
36       *            the expression we're casting.
37       */
38  
39      public JCastOp(int line, Type cast, JExpression expr) {
40          super(line);
41          this.cast = cast;
42          this.expr = expr;
43          conversions = new Conversions();
44      }
45  
46      /**
47       * Analyzing a cast expression means, resolving the type (to which we are
48       * casting), checking the legality of the cast, and computing a (possibly
49       * null) conversion for use in code generation.
50       * 
51       * @param context
52       *            context in which names are resolved.
53       * @return the analyzed (and possibly rewritten) AST subtree.
54       */
55  
56      public JExpression analyze(Context context) {
57          expr = (JExpression) expr.analyze(context);
58          type = cast = cast.resolve(context);
59          if (cast.equals(expr.type())) {
60              converter = Converter.Identity;
61          } else if (cast.isJavaAssignableFrom(expr.type())) {
62              converter = Converter.WidenReference;
63          } else if (expr.type().isJavaAssignableFrom(cast)) {
64              converter = new NarrowReference(cast);
65          } else if ((converter = conversions.get(expr.type(), cast)) != null) {
66          } else {
67              JAST.compilationUnit.reportSemanticError(line, "Cannot cast a "
68                      + expr.type().toString() + " to a " + cast.toString());
69          }
70          return this;
71      }
72  
73      /**
74       * Generating code for a cast expression involves generating code for the
75       * original expr, and then for any necessary conversion.
76       * 
77       * @param output
78       *            the code emitter (basically an abstraction for producing the
79       *            .class file).
80       */
81  
82      public void codegen(CLEmitter output) {
83          expr.codegen(output);
84          converter.codegen(output);
85      }
86  
87      /**
88       * @inheritDoc
89       */
90  
91      public void writeToStdOut(PrettyPrinter p) {
92          p.printf("<JCastOp line=\"%d\" type=\"%s\"/>\n", line(),
93                  ((cast == null) ? "" : cast.toString()));
94          p.indentRight();
95          if (expr != null) {
96              p.println("<Expression>");
97              p.indentRight();
98              expr.writeToStdOut(p);
99              p.indentLeft();
100             p.println("</Expression>");
101         }
102         p.indentLeft();
103         p.println("</JCastOp>");
104     }
105 
106 }
107 
108 /**
109  * A 2-D table of conversions, from one type to another.
110  */
111 
112 class Conversions {
113 
114     /**
115      * Table of conversions; maps a source and target type pair to its
116      * converter.
117      */
118     private Hashtable<String, Converter> table;
119 
120     /**
121      * Construct a table of conversions and populate it.
122      */
123 
124     public Conversions() {
125         table = new Hashtable<String, Converter>();
126 
127         // Populate the table
128 
129         put(Type.CHAR, Type.INT, Converter.Identity);
130         put(Type.INT, Type.CHAR, new I2C());
131 
132         // Boxing
133         put(Type.CHAR, Type.BOXED_CHAR, new Boxing(Type.CHAR, Type.BOXED_CHAR));
134         put(Type.INT, Type.BOXED_INT, new Boxing(Type.INT, Type.BOXED_INT));
135         put(Type.BOOLEAN, Type.BOXED_BOOLEAN, new Boxing(Type.BOOLEAN,
136                 Type.BOXED_BOOLEAN));
137 
138         // Un-boxing
139         put(Type.BOXED_CHAR, Type.CHAR, new UnBoxing(Type.BOXED_CHAR,
140                 Type.CHAR, "charValue"));
141         put(Type.BOXED_INT, Type.INT, new UnBoxing(Type.BOXED_INT, Type.INT,
142                 "intValue"));
143         put(Type.BOXED_BOOLEAN, Type.BOOLEAN, new UnBoxing(Type.BOXED_BOOLEAN,
144                 Type.BOOLEAN, "booleanValue"));
145     }
146 
147     /**
148      * Define a conversion. This is used locally, for populating the table.
149      * 
150      * @param source
151      *            the original type.
152      * @param target
153      *            the target type.
154      * @param c
155      *            the converter necessary.
156      */
157 
158     private void put(Type source, Type target, Converter c) {
159         table.put(source.toDescriptor() + "2" + target.toDescriptor(), c);
160     }
161 
162     /**
163      * Retrieve a converter for converting from some original type to a target
164      * type; the converter may be empty (requiring no code for run-time
165      * execution).
166      * 
167      * @param source
168      *            the original type.
169      * @param target
170      *            the target type.
171      * @return the converter.
172      */
173 
174     public Converter get(Type source, Type target) {
175         return table.get(source.toDescriptor() + "2" + target.toDescriptor());
176     }
177 
178 }
179 
180 /**
181  * A Converter encapusates any (possibly none) code necessary to perform a cast
182  * operation.
183  */
184 
185 interface Converter {
186 
187     /** For identity conversion (no run-time code needed). */
188     public static Converter Identity = new Identity();
189 
190     /** For widening conversion (no run-time code needed). */
191     public static Converter WidenReference = Identity;
192 
193     /**
194      * Emit code necessary to convert (cast) a source type to a target type.
195      * 
196      * @param output
197      *            the code emitter (basically an abstraction for producing the
198      *            .class file).
199      */
200 
201     public void codegen(CLEmitter output);
202 
203 }
204 
205 /**
206  * The identity conversion requires no run-time code.
207  */
208 
209 class Identity implements Converter {
210 
211     /**
212      * @inheritDoc
213      */
214 
215     public void codegen(CLEmitter output) {
216         // Nothing
217     }
218 
219 }
220 
221 /**
222  * A narrowing conversion on a reference type requires a run-time check on the
223  * type.
224  */
225 
226 class NarrowReference implements Converter {
227 
228     /** The target type. */
229     private Type target;
230 
231     /**
232      * Construct a narrowing converter.
233      * 
234      * @param target
235      *            the target type.
236      */
237 
238     public NarrowReference(Type target) {
239         this.target = target;
240     }
241 
242     /**
243      * @inheritDoc
244      */
245 
246     public void codegen(CLEmitter output) {
247         output.addReferenceInstruction(CHECKCAST, target.jvmName());
248     }
249 
250 }
251 
252 /**
253  * Boxing requires invoking the appropriate conversion method from the (Java)
254  * API.
255  */
256 
257 class Boxing implements Converter {
258 
259     /** The source type. */
260     private Type source;
261 
262     /** The target type. */
263     private Type target;
264 
265     /**
266      * Construct a Boxing converter.
267      * 
268      * @param source
269      *            the source type.
270      * @param target
271      *            the target type.
272      */
273 
274     public Boxing(Type source, Type target) {
275         this.source = source;
276         this.target = target;
277     }
278 
279     /**
280      * @inheritDoc
281      */
282 
283     public void codegen(CLEmitter output) {
284         output.addMemberAccessInstruction(INVOKESTATIC, target.jvmName(),
285                 "valueOf", "(" + source.toDescriptor() + ")"
286                         + target.toDescriptor());
287     }
288 
289 }
290 
291 /**
292  * Unboxing requires invoking the appropriate conversion method from the (Java)
293  * API.
294  */
295 
296 class UnBoxing implements Converter {
297 
298     /** The source type. */
299     private Type source;
300 
301     /** The target type. */
302     private Type target;
303 
304     /** The (Java) method to invoke for the conversion. */
305     private String methodName;
306 
307     /**
308      * Construct an UnBoxing converter.
309      * 
310      * @param source
311      *            the source type.
312      * @param target
313      *            the target type.
314      * @param methodName
315      *            the (Java) method to invoke for the conversion.
316      */
317 
318     public UnBoxing(Type source, Type target, String methodName) {
319         this.source = source;
320         this.target = target;
321         this.methodName = methodName;
322     }
323 
324     /**
325      * @inheritDoc
326      */
327 
328     public void codegen(CLEmitter output) {
329         output.addMemberAccessInstruction(INVOKEVIRTUAL, source.jvmName(),
330                 methodName, "()" + target.toDescriptor());
331     }
332 
333 }
334 
335 /**
336  * Converting from an int to a char requires an I2C instruction.
337  */
338 
339 class I2C implements Converter {
340 
341     /**
342      * @inheritDoc
343      */
344 
345     public void codegen(CLEmitter output) {
346         output.addNoArgInstruction(I2C);
347     }
348 
349 }
350