1   // Copyright 2013 Bill Campbell, Swami Iyer and Bahar Akbal-Delibas
2   
3   package jminusminus;
4   
5   import static jminusminus.CLConstants.*;
6   
7   /**
8    * The AST node for a field selection operation. It has a target object, a field
9    * name, and the Field it defines.
10   */
11  
12  class JFieldSelection extends JExpression implements JLhs {
13  
14      /** The target expression. */
15      protected JExpression target;
16  
17      /** The ambiguous part that is reclassified in analyze(). */
18      private AmbiguousName ambiguousPart;
19  
20      /** The field name. */
21      private String fieldName;
22  
23      /** The Field representing this field. */
24      private Field field;
25  
26      /**
27       * Construct an AST node for a field selection without an ambiguous part.
28       * 
29       * @param line
30       *            the line number of the selection.
31       * @param target
32       *            the target of the selection.
33       * @param fieldName
34       *            the field name.
35       */
36  
37      public JFieldSelection(int line, JExpression target, String fieldName) {
38          this(line, null, target, fieldName);
39      }
40  
41      /**
42       * Construct an AST node for a field selection having an ambiguous part.
43       * 
44       * @param line
45       *            line in which the field selection occurs in the source file.
46       * @param ambiguousPart
47       *            the ambiguous part.
48       * @param target
49       *            the target of the selection.
50       * @param fieldName
51       *            the field name.
52       */
53  
54      public JFieldSelection(int line, AmbiguousName ambiguousPart,
55              JExpression target, String fieldName) {
56          super(line);
57          this.ambiguousPart = ambiguousPart;
58          this.target = target;
59          this.fieldName = fieldName;
60      }
61  
62      /**
63       * Analyzing a field selection expression involves, (1) reclassifying any
64       * ambiguous part, (2) analyzing the target, (3) treating "length" field of
65       * arrays specially, or computing the Field object, (4) checking the access
66       * rules, and (5) computing the resultant type.
67       * 
68       * @param context
69       *            context in which names are resolved.
70       * @return the analyzed (and possibly rewritten) AST subtree.
71       */
72  
73      public JExpression analyze(Context context) {
74          // Reclassify the ambiguous part.
75          if (ambiguousPart != null) {
76              JExpression expr = ambiguousPart.reclassify(context);
77              if (expr != null) {
78                  if (target == null)
79                      target = expr;
80                  else {
81                      // Can't even happen syntactically
82                      JAST.compilationUnit.reportSemanticError(line(),
83                              "Badly formed suffix");
84                  }
85              }
86          }
87          target = (JExpression) target.analyze(context);
88          Type targetType = target.type();
89  
90          // We use a workaround for the "length" field of arrays.
91          if ((targetType.isArray()) && fieldName.equals("length")) {
92              type = Type.INT;
93          } else {
94              // Other than that, targetType has to be a
95              // ReferenceType
96              if (targetType.isPrimitive()) {
97                  JAST.compilationUnit.reportSemanticError(line(),
98                          "Target of a field selection must "
99                                  + "be a defined type");
100                 type = Type.ANY;
101                 return this;
102             }
103             field = targetType.fieldFor(fieldName);
104             if (field == null) {
105                 JAST.compilationUnit.reportSemanticError(line(),
106                         "Cannot find a field: " + fieldName);
107                 type = Type.ANY;
108             } else {
109                 context.definingType().checkAccess(line, (Member) field);
110                 type = field.type();
111 
112                 // Non-static field cannot be referenced from a static context.
113                 if (!field.isStatic()) {
114                     if (target instanceof JVariable
115                             && ((JVariable) target).iDefn() instanceof TypeNameDefn) {
116                         JAST.compilationUnit
117                                 .reportSemanticError(
118                                         line(),
119                                         "Non-static field "
120                                                 + fieldName
121                                                 + " cannot be referenced from a static context");
122                     }
123                 }
124             }
125         }
126         return this;
127     }
128 
129     /**
130      * Analyze the field selection expression for use on the lhs of an
131      * assignment. Although the final keyword is not in j--, we do make use of
132      * the Java api and so must repect its constraints.
133      * 
134      * @param context
135      *            context in which names are resolved.
136      * @return the analyzed (and possibly rewritten) AST subtree.
137      */
138 
139     public JExpression analyzeLhs(Context context) {
140         JExpression result = analyze(context);
141         if (field.isFinal()) {
142             JAST.compilationUnit.reportSemanticError(line, "The field "
143                     + fieldName + " in type " + target.type.toString()
144                     + " is declared final.");
145         }
146         return result;
147     }
148 
149     /**
150      * Generate the code necessary to load the Rvalue for this field selection.
151      * 
152      * @param output
153      *            the code emitter (basically an abstraction for producing the
154      *            .class file).
155      */
156 
157     public void codegen(CLEmitter output) {
158         target.codegen(output);
159 
160         // We use a workaround for the "length" field of arrays
161         if ((target.type().isArray()) && fieldName.equals("length")) {
162             output.addNoArgInstruction(ARRAYLENGTH);
163         } else {
164             int mnemonic = field.isStatic() ? GETSTATIC : GETFIELD;
165             output.addMemberAccessInstruction(mnemonic,
166                     target.type().jvmName(), fieldName, type.toDescriptor());
167         }
168     }
169 
170     /**
171      * The semantics of j-- require that we implement short-circuiting branching
172      * in implementing field selections.
173      * 
174      * @param output
175      *            the code emitter (basically an abstraction for producing the
176      *            .class file).
177      * @param targetLabel
178      *            the label to which we should branch.
179      * @param onTrue
180      *            do we branch on true?
181      */
182 
183     public void codegen(CLEmitter output, String targetLabel, boolean onTrue) {
184         // Push the value
185         codegen(output);
186 
187         if (onTrue) {
188             // Branch on true
189             output.addBranchInstruction(IFNE, targetLabel);
190         } else {
191             // Branch on false
192             output.addBranchInstruction(IFEQ, targetLabel);
193         }
194     }
195 
196     /**
197      * Generate the code required for setting up an Lvalue, eg, for use in an
198      * assignment.
199      * 
200      * @param output
201      *            the code emitter (basically an abstraction for producing the
202      *            .class file).
203      */
204 
205     public void codegenLoadLhsLvalue(CLEmitter output) {
206         // Nothing to do for static fields.
207         if (!field.isStatic()) {
208             // Just load the target
209             target.codegen(output);
210         }
211     }
212 
213     /**
214      * Generate the code required for loading an Rvalue for this variable, eg
215      * for use in a +=. Here, this requires either a getstatic or getfield.
216      * 
217      * @param output
218      *            the code emitter (basically an abstraction for producing the
219      *            .class file).
220      */
221 
222     public void codegenLoadLhsRvalue(CLEmitter output) {
223         String descriptor = field.type().toDescriptor();
224         if (field.isStatic()) {
225             output.addMemberAccessInstruction(GETSTATIC, target.type()
226                     .jvmName(), fieldName, descriptor);
227         } else {
228             output.addNoArgInstruction(type == Type.STRING ? DUP_X1 : DUP);
229             output.addMemberAccessInstruction(GETFIELD,
230                     target.type().jvmName(), fieldName, descriptor);
231         }
232     }
233 
234     /**
235      * Generate the code required for duplicating the Rvalue that is on the
236      * stack becuase it is to be used in a surrounding expression, as in a[i] =
237      * x = <expr> or x = y--. Here this means copying it down
238      * 
239      * @param output
240      *            the code emitter (basically an abstraction for producing the
241      *            .class file).
242      */
243 
244     public void codegenDuplicateRvalue(CLEmitter output) {
245         if (field.isStatic()) {
246             output.addNoArgInstruction(DUP);
247         } else {
248             output.addNoArgInstruction(DUP_X1);
249         }
250     }
251 
252     /**
253      * Generate the code required for doing the actual assignment.
254      * 
255      * @param output
256      *            the code emitter (basically an abstraction for producing the
257      *            .class file).
258      */
259 
260     public void codegenStore(CLEmitter output) {
261         String descriptor = field.type().toDescriptor();
262         if (field.isStatic()) {
263             output.addMemberAccessInstruction(PUTSTATIC, target.type()
264                     .jvmName(), fieldName, descriptor);
265         } else {
266             output.addMemberAccessInstruction(PUTFIELD,
267                     target.type().jvmName(), fieldName, descriptor);
268         }
269     }
270 
271     /**
272      * @inheritDoc
273      */
274 
275     public void writeToStdOut(PrettyPrinter p) {
276         p.printf("<JFieldSelection line=\"%d\" name=\"%s\"/>\n", line(),
277                 fieldName);
278         p.indentRight();
279         if (target != null) {
280             p.println("<Target>");
281             p.indentRight();
282             target.writeToStdOut(p);
283             p.indentLeft();
284             p.println("</Target>");
285         }
286         p.indentLeft();
287         p.println("</JFieldSelection>");
288     }
289 
290 }
291