001    /*
002     *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003     *
004     *  This file is licensed to You under the Eclipse Public License (EPL);
005     *  You may not use this file except in compliance with the License. You
006     *  may obtain a copy of the License at
007     *
008     *      http://www.opensource.org/licenses/eclipse-1.0.php
009     *
010     *  See the COPYRIGHT.txt file distributed with this work for information
011     *  regarding copyright ownership.
012     */
013    package org.jikesrvm.compilers.opt;
014    
015    import java.lang.reflect.Field;
016    import org.jikesrvm.VM;
017    import org.jikesrvm.SizeConstants;
018    import org.jikesrvm.classloader.RVMField;
019    import org.jikesrvm.classloader.RVMType;
020    import org.jikesrvm.classloader.TypeReference;
021    import org.jikesrvm.compilers.opt.ir.operand.AddressConstantOperand;
022    import org.jikesrvm.compilers.opt.ir.operand.ClassConstantOperand;
023    import org.jikesrvm.compilers.opt.ir.operand.ConstantOperand;
024    import org.jikesrvm.compilers.opt.ir.operand.DoubleConstantOperand;
025    import org.jikesrvm.compilers.opt.ir.operand.FloatConstantOperand;
026    import org.jikesrvm.compilers.opt.ir.operand.IntConstantOperand;
027    import org.jikesrvm.compilers.opt.ir.operand.LongConstantOperand;
028    import org.jikesrvm.compilers.opt.ir.operand.NullConstantOperand;
029    import org.jikesrvm.compilers.opt.ir.operand.ObjectConstantOperand;
030    import org.jikesrvm.compilers.opt.ir.operand.StringConstantOperand;
031    import org.jikesrvm.runtime.Magic;
032    import org.jikesrvm.runtime.Statics;
033    import org.vmmagic.unboxed.Address;
034    import org.vmmagic.unboxed.Extent;
035    import org.vmmagic.unboxed.Offset;
036    import org.vmmagic.unboxed.Word;
037    
038    /**
039     * Code for accessing the value of a static field at
040     * compile time.  This is used to optimize
041     * getstatic's of initialized static fields
042     * by replacing the getstatic with a constant operand.
043     */
044    public abstract class StaticFieldReader implements SizeConstants {
045    
046      /**
047       * Read the field from obj and return as the appropriate constant
048       */
049      public static ConstantOperand getFieldValueAsConstant(RVMField field, Object obj) throws NoSuchFieldException {
050        if (VM.VerifyAssertions) VM._assert(field.isFinal(), "Error reading field " + field);
051        if (VM.VerifyAssertions) {
052          VM._assert(field.getDeclaringClass().isInitialized() || field.getDeclaringClass().isInBootImage(),
053                     "Error reading field " + field);
054        }
055    
056        TypeReference type = field.getType();
057        if (VM.runningVM) {
058          if (type.isReferenceType() && (!type.isMagicType() || type.isUnboxedArrayType())) {
059            Object value = field.getObjectValueUnchecked(obj);
060            if (value != null) {
061              return new ObjectConstantOperand(value, Offset.zero());
062            } else {
063              return new NullConstantOperand();
064            }
065          } else if (type.isWordLikeType()) {
066            return new AddressConstantOperand(field.getWordValueUnchecked(obj).toAddress());
067          } else if (type.isIntType()) {
068            return new IntConstantOperand(field.getIntValueUnchecked(obj));
069          } else if (type.isBooleanType()) {
070            return new IntConstantOperand(field.getBooleanValueUnchecked(obj) ? 1 : 0);
071          } else if (type.isByteType()) {
072            return new IntConstantOperand(field.getByteValueUnchecked(obj));
073          } else if (type.isCharType()) {
074            return new IntConstantOperand(field.getCharValueUnchecked(obj));
075          } else if (type.isDoubleType()) {
076            return new DoubleConstantOperand(field.getDoubleValueUnchecked(obj));
077          } else if (type.isFloatType()) {
078            return new FloatConstantOperand(field.getFloatValueUnchecked(obj));
079          } else if (type.isLongType()) {
080            return new LongConstantOperand(field.getLongValueUnchecked(obj));
081          } else if (type.isShortType()) {
082            return new IntConstantOperand(field.getShortValueUnchecked(obj));
083          } else {
084            OptimizingCompilerException.UNREACHABLE("Unknown type " + type);
085            return null;
086          }
087        } else {
088          try {
089            String cn = field.getDeclaringClass().toString();
090            Field f = Class.forName(cn).getDeclaredField(field.getName().toString());
091            f.setAccessible(true);
092            if (type.isReferenceType() && (!type.isMagicType() || type.isUnboxedArrayType())) {
093              Object value = f.get(obj);
094              if (value != null) {
095                return new ObjectConstantOperand(value, Offset.zero());
096              } else {
097                return new NullConstantOperand();
098              }
099            } else if (type.isWordLikeType()) {
100              Object value = f.get(obj);
101              if (type.equals(TypeReference.Word))
102                return new AddressConstantOperand((Word)value);
103              else if (type.equals(TypeReference.Address))
104                return new AddressConstantOperand((Address)value);
105              else if (type.equals(TypeReference.Offset))
106                return new AddressConstantOperand((Offset)value);
107              else if (type.equals(TypeReference.Extent))
108                return new AddressConstantOperand((Extent)value);
109              else {
110                OptimizingCompilerException.UNREACHABLE("Unknown word type " + type);
111                return null;
112              }
113            } else if (type.isIntType()) {
114              return new IntConstantOperand(f.getInt(obj));
115            } else if (type.isBooleanType()) {
116              return new IntConstantOperand(f.getBoolean(obj) ? 1 : 0);
117            } else if (type.isByteType()) {
118              return new IntConstantOperand(f.getByte(obj));
119            } else if (type.isCharType()) {
120              return new IntConstantOperand(f.getChar(obj));
121            } else if (type.isDoubleType()) {
122              return new DoubleConstantOperand(f.getDouble(obj));
123            } else if (type.isFloatType()) {
124              return new FloatConstantOperand(f.getFloat(obj));
125            } else if (type.isLongType()) {
126              return new LongConstantOperand(f.getLong(obj));
127            } else if (type.isShortType()) {
128              return new IntConstantOperand(f.getShort(obj));
129            } else {
130              OptimizingCompilerException.UNREACHABLE(cn + "." + f.getName() + " has unknown type " + type);
131              return null;
132            }
133          } catch (IllegalArgumentException e) {
134            throw new NoSuchFieldException(field.toString());
135          } catch (IllegalAccessException e) {
136            throw new NoSuchFieldException(field.toString());
137          } catch (NoSuchFieldError e) {
138            throw new NoSuchFieldException(field.toString());
139          } catch (ClassNotFoundException e) {
140            throw new NoSuchFieldException(field.toString());
141          } catch (NoClassDefFoundError e) {
142            throw new NoSuchFieldException(field.toString());
143          } catch (IllegalAccessError e) {
144            throw new NoSuchFieldException(field.toString());
145          }
146        }
147      }
148    
149      /**
150       * Returns a constant operand with the current value of a static field.
151       *
152       * @param field the static field whose current value we want to read
153       * @return a constant operand representing the current value of the field.
154       */
155      public static ConstantOperand getStaticFieldValue(RVMField field) throws NoSuchFieldException {
156        if (VM.VerifyAssertions) VM._assert(field.isFinal(), "Error reading field " + field);
157        if (VM.VerifyAssertions) VM._assert(field.isStatic(), "Error reading field " + field);
158        if (VM.VerifyAssertions) {
159          VM._assert(field.getDeclaringClass().isInitialized() || field.getDeclaringClass().isInBootImage(),
160                     "Error reading field " + field);
161        }
162    
163        TypeReference fieldType = field.getType();
164        Offset off = field.getOffset();
165        if ((fieldType == TypeReference.Address) ||
166            (fieldType == TypeReference.Word) ||
167            (fieldType == TypeReference.Offset) ||
168            (fieldType == TypeReference.Extent)) {
169          Address val = getAddressStaticFieldValue(field);
170          return new AddressConstantOperand(val);
171        } else if (fieldType.isIntLikeType()) {
172          int val = getIntStaticFieldValue(field);
173          return new IntConstantOperand(val);
174        } else if (fieldType.isLongType()) {
175          long val = getLongStaticFieldValue(field);
176          return new LongConstantOperand(val, off);
177        } else if (fieldType.isFloatType()) {
178          float val = getFloatStaticFieldValue(field);
179          return new FloatConstantOperand(val, off);
180        } else if (fieldType.isDoubleType()) {
181          double val = getDoubleStaticFieldValue(field);
182          return new DoubleConstantOperand(val, off);
183        } else { // Reference type
184          if (VM.VerifyAssertions) VM._assert(fieldType.isReferenceType());
185          Object val = getObjectStaticFieldValue(field);
186          if (val == null) {
187            return new NullConstantOperand();
188          } else if (fieldType == TypeReference.JavaLangString) {
189            return new StringConstantOperand((String) val, off);
190          } else if (fieldType == TypeReference.JavaLangClass) {
191            Class<?> klass = (Class<?>) getObjectStaticFieldValue(field);
192            RVMType type;
193            if (VM.runningVM) {
194              type = java.lang.JikesRVMSupport.getTypeForClass(klass);
195            } else {
196              type = TypeReference.findOrCreate(klass).resolve();
197            }
198            return new ClassConstantOperand(type.getClassForType(), off);
199          } else {
200            return new ObjectConstantOperand(val, off);
201          }
202        }
203      }
204    
205      /**
206       * Returns the current contents of an int-like static field.
207       *
208       * @param field a static field
209       * @return the current value of the field
210       */
211      public static int getIntStaticFieldValue(RVMField field) throws NoSuchFieldException {
212        if (VM.runningVM) {
213          return Statics.getSlotContentsAsInt(field.getOffset());
214        } else {
215          try {
216            Field f = getJDKField(field);
217            TypeReference fieldType = field.getType();
218            if (fieldType.isBooleanType()) {
219              boolean val = f.getBoolean(null);
220              return val ? 1 : 0;
221            } else if (fieldType.isByteType()) {
222              return f.getByte(null);
223            } else if (fieldType.isShortType()) {
224              return f.getShort(null);
225            } else if (fieldType.isIntType()) {
226              return f.getInt(null);
227            } else if (fieldType.isCharType()) {
228              return f.getChar(null);
229            } else {
230              throw new OptimizingCompilerException("Unsupported type " + field + "\n");
231            }
232          } catch (IllegalAccessException e) {
233            throw new OptimizingCompilerException("Accessing " + field + " caused " + e);
234          } catch (IllegalArgumentException e) {
235            throw new OptimizingCompilerException("Accessing " + field + " caused " + e);
236          }
237        }
238      }
239    
240      /**
241       * Returns the current contents of a float static field.
242       *
243       * @param field a static field
244       * @return the current value of the field
245       */
246      public static float getFloatStaticFieldValue(RVMField field) throws NoSuchFieldException {
247        if (VM.runningVM) {
248          int bits = Statics.getSlotContentsAsInt(field.getOffset());
249          return Magic.intBitsAsFloat(bits);
250        } else {
251          try {
252            return getJDKField(field).getFloat(null);
253          } catch (IllegalAccessException e) {
254            throw new OptimizingCompilerException("Accessing " + field + " caused " + e);
255          } catch (IllegalArgumentException e) {
256            throw new OptimizingCompilerException("Accessing " + field + " caused " + e);
257          }
258        }
259      }
260    
261      /**
262       * Returns the current contents of a long static field.
263       *
264       * @param field a static field
265       * @return the current value of the field
266       */
267      public static long getLongStaticFieldValue(RVMField field) throws NoSuchFieldException {
268        if (VM.runningVM) {
269          return Statics.getSlotContentsAsLong(field.getOffset());
270        } else {
271          try {
272            return getJDKField(field).getLong(null);
273          } catch (IllegalAccessException e) {
274            throw new OptimizingCompilerException("Accessing " + field + " caused " + e);
275          } catch (IllegalArgumentException e) {
276            throw new OptimizingCompilerException("Accessing " + field + " caused " + e);
277          }
278        }
279      }
280    
281      /**
282       * Returns the current contents of a double static field.
283       *
284       * @param field a static field
285       * @return the current value of the field
286       */
287      public static double getDoubleStaticFieldValue(RVMField field) throws NoSuchFieldException {
288        if (VM.runningVM) {
289          long bits = Statics.getSlotContentsAsLong(field.getOffset());
290          return Magic.longBitsAsDouble(bits);
291        } else {
292          try {
293            return getJDKField(field).getDouble(null);
294          } catch (IllegalAccessException e) {
295            throw new OptimizingCompilerException("Accessing " + field + " caused " + e);
296          } catch (IllegalArgumentException e) {
297            throw new OptimizingCompilerException("Accessing " + field + " caused " + e);
298          }
299        }
300      }
301    
302      /**
303       * Returns the current contents of a reference static field.
304       *
305       * @param field a static field
306       * @return the current value of the field
307       */
308      public static Object getObjectStaticFieldValue(RVMField field) throws NoSuchFieldException {
309        if (VM.runningVM) {
310          return Statics.getSlotContentsAsObject(field.getOffset());
311        } else {
312          try {
313            return getJDKField(field).get(null);
314          } catch (IllegalAccessException e) {
315            throw new OptimizingCompilerException("Accessing " + field + " caused " + e);
316          } catch (IllegalArgumentException e) {
317            throw new OptimizingCompilerException("Accessing " + field + " caused " + e);
318          }
319        }
320      }
321    
322      /**
323       * Returns the current contents of a Address static field.
324       *
325       * @param field a static field
326       * @return the current value of the field
327       */
328      public static Address getAddressStaticFieldValue(RVMField field) throws NoSuchFieldException {
329        if (VM.runningVM) {
330          return Statics.getSlotContentsAsAddress(field.getOffset());
331        } else {
332          try {
333            Object unboxed = getJDKField(field).get(null);
334            if (unboxed instanceof Address) {
335              return (Address) unboxed;
336            } else if (unboxed instanceof Word) {
337              return ((Word) unboxed).toAddress();
338            } else if (unboxed instanceof Extent) {
339              return ((Extent) unboxed).toWord().toAddress();
340            } else if (unboxed instanceof Offset) {
341              return ((Offset) unboxed).toWord().toAddress();
342            } else {
343              if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED);
344              return Address.zero();
345            }
346          } catch (IllegalAccessException e) {
347            throw new OptimizingCompilerException("Accessing " + field + " caused " + e);
348          } catch (IllegalArgumentException e) {
349            throw new OptimizingCompilerException("Accessing " + field + " caused " + e);
350          }
351        }
352      }
353    
354      /**
355       * Does a static field null contain {@code null}?
356       *
357       * @param field a static field
358       * @return {@code true} if the field contains {@code null}, {@code false} otherwise
359       */
360      public static boolean isStaticFieldNull(RVMField field) throws NoSuchFieldException {
361        return getObjectStaticFieldValue(field) == null;
362      }
363    
364      /**
365       * Get the type of an object contained in a static field.
366       *
367       * @param field a static field
368       * @return type of value contained in the field
369       */
370      public static TypeReference getTypeFromStaticField(RVMField field) throws NoSuchFieldException {
371        Object o = getObjectStaticFieldValue(field);
372        if (o == null) return TypeReference.NULL_TYPE;
373        if (VM.runningVM) {
374          return Magic.getObjectType(o).getTypeRef();
375        } else {
376          return TypeReference.findOrCreate(o.getClass());
377        }
378      }
379    
380      /**
381       * Utilitiy to convert a RVMField to a java.lang.reflect.Field
382       */
383      private static Field getJDKField(RVMField field) throws NoSuchFieldException {
384        try {
385          String cn = field.getDeclaringClass().toString();
386          if (VM.BuildForGnuClasspath &&
387              field.getDeclaringClass().getClassForType().equals(java.lang.reflect.Proxy.class) &&
388              field.getName().toString().equals("proxyClasses")) {
389            // Avoid confusing bootstrap JVM and classpath fields
390            throw new NoSuchFieldException(field.toString());
391          }
392          Field f = Class.forName(cn).getDeclaredField(field.getName().toString());
393          f.setAccessible(true);
394          return f;
395        } catch (NoSuchFieldError e) {
396          throw new NoSuchFieldException(field.toString());
397        } catch (ClassNotFoundException e) {
398          throw new NoSuchFieldException(field.toString());
399        } catch (NoClassDefFoundError e) {
400          throw new NoSuchFieldException(field.toString());
401        } catch (IllegalAccessError e) {
402          throw new NoSuchFieldException(field.toString());
403        } catch (UnsatisfiedLinkError e) {
404          throw new NoSuchFieldException(field.toString());
405        }
406      }
407    }