1   // Copyright 2012- Bill Campbell, Swami Iyer and Bahar Akbal-Delibas
2   
3   package jminusminus;
4   
5   import java.util.Hashtable;
6   
7   import static jminusminus.CLConstants.*;
8   
9   /**
10   * The AST for an cast expression, which has both a cast (a type) and the expression to be cast.
11   */
12  class JCastOp extends JExpression {
13      // The cast.
14      private Type cast;
15  
16      // The expression we're casting.
17      private JExpression expr;
18  
19      // The conversions table.
20      private static Conversions conversions;
21  
22      // The converter to use for this cast.
23      private Converter converter;
24  
25      /**
26       * Constructs an AST node for a cast expression.
27       *
28       * @param line the line in which the expression occurs in the source file.
29       * @param cast the type we're casting our expression as.
30       * @param expr the expression we're casting.
31       */
32      public JCastOp(int line, Type cast, JExpression expr) {
33          super(line);
34          this.cast = cast;
35          this.expr = expr;
36          conversions = new Conversions();
37      }
38  
39      /**
40       * {@inheritDoc}
41       */
42      public JExpression analyze(Context context) {
43          expr = (JExpression) expr.analyze(context);
44          type = cast = cast.resolve(context);
45          if (cast.equals(expr.type())) {
46              converter = Converter.Identity;
47          } else if (cast.isJavaAssignableFrom(expr.type())) {
48              converter = Converter.WidenReference;
49          } else if (expr.type().isJavaAssignableFrom(cast)) {
50              converter = new NarrowReference(cast);
51          } else if (conversions.get(expr.type(), cast) != null) {
52              converter = conversions.get(expr.type(), cast);
53          } else {
54              JAST.compilationUnit.reportSemanticError(line,
55                      "Cannot cast a " + expr.type().toString() + " to a " + cast.toString());
56          }
57          return this;
58      }
59  
60      /**
61       * {@inheritDoc}
62       */
63      public void codegen(CLEmitter output) {
64          expr.codegen(output);
65          converter.codegen(output);
66      }
67  
68      /**
69       * {@inheritDoc}
70       */
71      public void toJSON(JSONElement json) {
72          JSONElement e = new JSONElement();
73          json.addChild("JCastOp:" + line, e);
74          e.addAttribute("type", cast == null ? "" : cast.toString());
75          JSONElement e1 = new JSONElement();
76          e.addChild("Expression", e1);
77          expr.toJSON(e1);
78      }
79  }
80  
81  /**
82   * A 2D table of conversions, from one type to another.
83   */
84  class Conversions {
85      // Table of conversions; maps a source and target type pair to its converter.
86      private Hashtable<String, Converter> table;
87  
88      /**
89       * Constructs a table of conversions and populates it.
90       */
91      public Conversions() {
92          table = new Hashtable<String, Converter>();
93  
94          // Populate the table.
95          put(Type.CHAR, Type.INT, Converter.Identity);
96          put(Type.INT, Type.CHAR, new I2C());
97  
98          // Boxing.
99          put(Type.CHAR, Type.BOXED_CHAR, new Boxing(Type.CHAR, Type.BOXED_CHAR));
100         put(Type.INT, Type.BOXED_INT, new Boxing(Type.INT, Type.BOXED_INT));
101         put(Type.BOOLEAN, Type.BOXED_BOOLEAN, new Boxing(Type.BOOLEAN, Type.BOXED_BOOLEAN));
102 
103         // Un-boxing.
104         put(Type.BOXED_CHAR, Type.CHAR, new UnBoxing(Type.BOXED_CHAR, Type.CHAR, "charValue"));
105         put(Type.BOXED_INT, Type.INT, new UnBoxing(Type.BOXED_INT, Type.INT, "intValue"));
106         put(Type.BOXED_BOOLEAN, Type.BOOLEAN, new UnBoxing(Type.BOXED_BOOLEAN, Type.BOOLEAN,
107                 "booleanValue"));
108     }
109 
110     /**
111      * Retrieves and returns a converter for converting from some original type to a target type.
112      *
113      * @param source the source type.
114      * @param target the target type.
115      * @return the converter.
116      */
117     public Converter get(Type source, Type target) {
118         return table.get(source.toDescriptor() + "2" + target.toDescriptor());
119     }
120 
121     // Defines a conversion. This is used for populating the conversions table.
122     private void put(Type source, Type target, Converter c) {
123         table.put(source.toDescriptor() + "2" + target.toDescriptor(), c);
124     }
125 }
126 
127 /**
128  * A Converter encapsulates any (possibly none) code necessary to perform a cast operation.
129  */
130 interface Converter {
131     /**
132      * For identity conversion (no run-time code needed).
133      */
134     public static Converter Identity = new Identity();
135 
136     /**
137      * For widening conversion (no run-time code needed).
138      */
139     public static Converter WidenReference = Identity;
140 
141     /**
142      * Emits code necessary to convert (cast) a source type to a target type.
143      *
144      * @param output the code emitter.
145      */
146     public void codegen(CLEmitter output);
147 }
148 
149 /**
150  * An identity converter.
151  */
152 class Identity implements Converter {
153     /**
154      * {@inheritDoc}
155      */
156     public void codegen(CLEmitter output) {
157         // Nothing here.
158     }
159 }
160 
161 /**
162  * A narrowing reference converter.
163  */
164 class NarrowReference implements Converter {
165     // The target type.
166     private Type target;
167 
168     /**
169      * Constructs a narrowing reference converter.
170      *
171      * @param target the target type.
172      */
173     public NarrowReference(Type target) {
174         this.target = target;
175     }
176 
177     /**
178      * {@inheritDoc}
179      */
180     public void codegen(CLEmitter output) {
181         output.addReferenceInstruction(CHECKCAST, target.jvmName());
182     }
183 }
184 
185 /**
186  * A boxing converter.
187  */
188 class Boxing implements Converter {
189     // The source type.
190     private Type source;
191 
192     // The target type.
193     private Type target;
194 
195     /**
196      * Constructs a Boxing converter.
197      *
198      * @param source the source type.
199      * @param target the target type.
200      */
201     public Boxing(Type source, Type target) {
202         this.source = source;
203         this.target = target;
204     }
205 
206     /**
207      * {@inheritDoc}
208      */
209     public void codegen(CLEmitter output) {
210         output.addMemberAccessInstruction(INVOKESTATIC, target.jvmName(), "valueOf",
211                 "(" + source.toDescriptor() + ")" + target.toDescriptor());
212     }
213 }
214 
215 /**
216  * An un-boxing converter.
217  */
218 class UnBoxing implements Converter {
219     // The source type.
220     private Type source;
221 
222     // The target type.
223     private Type target;
224 
225     // The Java method to invoke for the conversion.
226     private String methodName;
227 
228     /**
229      * Constructs an UnBoxing converter.
230      *
231      * @param source     the source type.
232      * @param target     the target type.
233      * @param methodName the Java method to invoke for the conversion.
234      */
235     public UnBoxing(Type source, Type target, String methodName) {
236         this.source = source;
237         this.target = target;
238         this.methodName = methodName;
239     }
240 
241     /**
242      * {@inheritDoc}
243      */
244     public void codegen(CLEmitter output) {
245         output.addMemberAccessInstruction(INVOKEVIRTUAL, source.jvmName(), methodName,
246                 "()" + target.toDescriptor());
247     }
248 }
249 
250 /**
251  * An int to char converter.
252  */
253 class I2C implements Converter {
254     /**
255      * {@inheritDoc}
256      */
257     public void codegen(CLEmitter output) {
258         output.addNoArgInstruction(I2C);
259     }
260 }
261