1   // Copyright 2012- 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 name, and the
9    * field it defines.
10   */
11  class JFieldSelection extends JExpression implements JLhs {
12      /**
13       * The target expression.
14       */
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       * Constructs an AST node for a field selection without an ambiguous part.
28       *
29       * @param line      the line number of the selection.
30       * @param target    the target of the selection.
31       * @param fieldName the field name.
32       */
33      public JFieldSelection(int line, JExpression target, String fieldName) {
34          this(line, null, target, fieldName);
35      }
36  
37      /**
38       * Construct an AST node for a field selection having an ambiguous part.
39       *
40       * @param line          line in which the field selection occurs in the source file.
41       * @param ambiguousPart the ambiguous part.
42       * @param target        the target of the selection.
43       * @param fieldName     the field name.
44       */
45      public JFieldSelection(int line, AmbiguousName ambiguousPart, JExpression target,
46                             String fieldName) {
47          super(line);
48          this.ambiguousPart = ambiguousPart;
49          this.target = target;
50          this.fieldName = fieldName;
51      }
52  
53      /**
54       * {@inheritDoc}
55       */
56      public JExpression analyze(Context context) {
57          // Reclassify the ambiguous part.
58          if (ambiguousPart != null) {
59              JExpression expr = ambiguousPart.reclassify(context);
60              if (expr != null) {
61                  if (target == null) {
62                      target = expr;
63                  } else {
64                      // Can't even happen syntactically.
65                      JAST.compilationUnit.reportSemanticError(line(), "Badly formed suffix");
66                  }
67              }
68          }
69          target = (JExpression) target.analyze(context);
70          Type targetType = target.type();
71  
72          // We use a workaround for the "length" field of arrays.
73          if ((targetType.isArray()) && fieldName.equals("length")) {
74              type = Type.INT;
75          } else {
76              // Other than that, targetType has to be a reference type.
77              if (targetType.isPrimitive()) {
78                  JAST.compilationUnit.reportSemanticError(line(),
79                          "Target of a field selection must be a reference type");
80                  type = Type.ANY;
81                  return this;
82              }
83              field = targetType.fieldFor(fieldName);
84              if (field == null) {
85                  JAST.compilationUnit.reportSemanticError(line(),
86                          "Cannot find a field: " + fieldName);
87                  type = Type.ANY;
88              } else {
89                  context.definingType().checkAccess(line, (Member) field);
90                  type = field.type();
91  
92                  // Non-static field cannot be referenced from a static context.
93                  if (!field.isStatic()) {
94                      if (target instanceof JVariable &&
95                              ((JVariable) target).iDefn() instanceof TypeNameDefn) {
96                          JAST.compilationUnit.reportSemanticError(line(), "Non-static field " +
97                                  fieldName + " cannot be referenced from a static context");
98                      }
99                  }
100             }
101         }
102         return this;
103     }
104 
105     /**
106      * {@inheritDoc}
107      */
108     public JExpression analyzeLhs(Context context) {
109         JExpression result = analyze(context);
110         if (field.isFinal()) {
111             JAST.compilationUnit.reportSemanticError(line, "The field " + fieldName + " in type " +
112                     target.type.toString() + " is final");
113         }
114         return result;
115     }
116 
117     /**
118      * {@inheritDoc}
119      */
120     public void codegen(CLEmitter output) {
121         target.codegen(output);
122 
123         // We use a workaround for the "length" field of arrays.
124         if ((target.type().isArray()) && fieldName.equals("length")) {
125             output.addNoArgInstruction(ARRAYLENGTH);
126         } else {
127             int mnemonic = field.isStatic() ? GETSTATIC : GETFIELD;
128             output.addMemberAccessInstruction(mnemonic, target.type().jvmName(), fieldName,
129                     type.toDescriptor());
130         }
131     }
132 
133     /**
134      * {@inheritDoc}
135      */
136     public void codegen(CLEmitter output, String targetLabel, boolean onTrue) {
137         codegen(output);
138         if (onTrue) {
139             output.addBranchInstruction(IFNE, targetLabel);
140         } else {
141             output.addBranchInstruction(IFEQ, targetLabel);
142         }
143     }
144 
145     /**
146      * {@inheritDoc}
147      */
148     public void codegenLoadLhsLvalue(CLEmitter output) {
149         // Nothing to do for static fields.
150         if (!field.isStatic()) {
151             target.codegen(output);
152         }
153     }
154 
155     /**
156      * {@inheritDoc}
157      */
158     public void codegenLoadLhsRvalue(CLEmitter output) {
159         if (field.isStatic()) {
160             output.addMemberAccessInstruction(GETSTATIC, target.type()
161                     .jvmName(), fieldName, field.type().toDescriptor());
162         } else {
163             output.addNoArgInstruction(type == Type.STRING ? DUP_X1 : DUP);
164             output.addMemberAccessInstruction(GETFIELD,
165                     target.type().jvmName(), fieldName, field.type().toDescriptor());
166         }
167     }
168 
169     /**
170      * {@inheritDoc}
171      */
172     public void codegenDuplicateRvalue(CLEmitter output) {
173         if (field.isStatic()) {
174             output.addNoArgInstruction(DUP);
175         } else {
176             output.addNoArgInstruction(DUP_X1);
177         }
178     }
179 
180     /**
181      * {@inheritDoc}
182      */
183     public void codegenStore(CLEmitter output) {
184         String descriptor = field.type().toDescriptor();
185         if (field.isStatic()) {
186             output.addMemberAccessInstruction(PUTSTATIC, target.type().jvmName(), fieldName,
187                     descriptor);
188         } else {
189             output.addMemberAccessInstruction(PUTFIELD, target.type().jvmName(), fieldName,
190                     descriptor);
191         }
192     }
193 
194     /**
195      * {@inheritDoc}
196      */
197     public void toJSON(JSONElement json) {
198         JSONElement e = new JSONElement();
199         json.addChild("JFieldSelection:" +  line, e);
200         e.addAttribute("ambiguousPart", ambiguousPart == null ? "null" : ambiguousPart.toString());
201         e.addAttribute("name", fieldName);
202         if (target != null) {
203             JSONElement e1 = new JSONElement();
204             e.addChild("Target", e1);
205             target.toJSON(e1);
206         }
207     }
208 }
209