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 */
013package org.jikesrvm.compilers.opt;
014
015import java.lang.reflect.Field;
016
017import org.jikesrvm.VM;
018import org.jikesrvm.classloader.RVMField;
019import org.jikesrvm.classloader.RVMType;
020import org.jikesrvm.classloader.TypeReference;
021import org.jikesrvm.compilers.opt.ir.operand.AddressConstantOperand;
022import org.jikesrvm.compilers.opt.ir.operand.ClassConstantOperand;
023import org.jikesrvm.compilers.opt.ir.operand.ConstantOperand;
024import org.jikesrvm.compilers.opt.ir.operand.DoubleConstantOperand;
025import org.jikesrvm.compilers.opt.ir.operand.FloatConstantOperand;
026import org.jikesrvm.compilers.opt.ir.operand.IntConstantOperand;
027import org.jikesrvm.compilers.opt.ir.operand.LongConstantOperand;
028import org.jikesrvm.compilers.opt.ir.operand.NullConstantOperand;
029import org.jikesrvm.compilers.opt.ir.operand.ObjectConstantOperand;
030import org.jikesrvm.compilers.opt.ir.operand.StringConstantOperand;
031import org.jikesrvm.runtime.Magic;
032import org.jikesrvm.runtime.Statics;
033import org.vmmagic.unboxed.Address;
034import org.vmmagic.unboxed.Extent;
035import org.vmmagic.unboxed.Offset;
036import 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 */
044public abstract class StaticFieldReader {
045
046  public static ConstantOperand getFieldValueAsConstant(RVMField field, Object obj) throws NoSuchFieldException {
047    if (VM.VerifyAssertions) {
048      boolean isFinalField = field.isFinal();
049      boolean isInitializedField = field.getDeclaringClass().isInitialized() || field.getDeclaringClass().isInBootImage();
050      if (!(isFinalField && isInitializedField)) {
051        String msg = "Error reading field " + field;
052        VM._assert(VM.NOT_REACHED, msg);
053      }
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        throwNoSuchFieldExceptionWithCause(field, e);
135      } catch (IllegalAccessException e) {
136        throwNoSuchFieldExceptionWithCause(field, e);
137      } catch (NoSuchFieldError e) {
138        throwNoSuchFieldExceptionWithCause(field, e);
139      } catch (ClassNotFoundException e) {
140        throwNoSuchFieldExceptionWithCause(field, e);
141      } catch (NoClassDefFoundError e) {
142        throwNoSuchFieldExceptionWithCause(field, e);
143      } catch (IllegalAccessError e) {
144        throwNoSuchFieldExceptionWithCause(field, e);
145      }
146      assertNotReached();
147      return null;
148    }
149  }
150
151  /**
152   * Returns a constant operand with the current value of a static field.
153   *
154   * @param field the static field whose current value we want to read
155   * @return a constant operand representing the current value of the field.
156   * @throws NoSuchFieldException when the field could not be found
157   */
158  public static ConstantOperand getStaticFieldValue(RVMField field) throws NoSuchFieldException {
159    if (VM.VerifyAssertions) {
160      boolean fieldIsReady = field.getDeclaringClass().isInitialized() ||
161          field.getDeclaringClass().isInBootImage();
162      boolean isFinalField = field.isFinal();
163      boolean isStaticField = field.isStatic();
164      if (!(isFinalField && isStaticField && fieldIsReady)) {
165        String msg = "Error reading field " + field;
166        VM._assert(VM.NOT_REACHED, msg);
167      }
168    }
169
170    TypeReference fieldType = field.getType();
171    Offset off = field.getOffset();
172    if ((fieldType == TypeReference.Address) ||
173        (fieldType == TypeReference.Word) ||
174        (fieldType == TypeReference.Offset) ||
175        (fieldType == TypeReference.Extent)) {
176      Address val = getAddressStaticFieldValue(field);
177      return new AddressConstantOperand(val);
178    } else if (fieldType.isIntLikeType()) {
179      int val = getIntStaticFieldValue(field);
180      return new IntConstantOperand(val);
181    } else if (fieldType.isLongType()) {
182      long val = getLongStaticFieldValue(field);
183      return new LongConstantOperand(val);
184    } else if (fieldType.isFloatType()) {
185      float val = getFloatStaticFieldValue(field);
186      return new FloatConstantOperand(val, off);
187    } else if (fieldType.isDoubleType()) {
188      double val = getDoubleStaticFieldValue(field);
189      return new DoubleConstantOperand(val, off);
190    } else { // Reference type
191      if (VM.VerifyAssertions) VM._assert(fieldType.isReferenceType());
192      Object val = getObjectStaticFieldValue(field);
193      if (val == null) {
194        return new NullConstantOperand();
195      } else if (fieldType == TypeReference.JavaLangString) {
196        return new StringConstantOperand((String) val, off);
197      } else if (fieldType == TypeReference.JavaLangClass) {
198        Class<?> klass = (Class<?>) getObjectStaticFieldValue(field);
199        RVMType type;
200        if (VM.runningVM) {
201          type = java.lang.JikesRVMSupport.getTypeForClass(klass);
202        } else {
203          type = TypeReference.findOrCreate(klass).resolve();
204        }
205        return new ClassConstantOperand(type.getClassForType(), off);
206      } else {
207        return new ObjectConstantOperand(val, off);
208      }
209    }
210  }
211
212  /**
213   * Returns the current contents of an int-like static field.
214   *
215   * @param field a static field
216   * @return the current value of the field
217   * @throws NoSuchFieldException when the field could not be found
218   */
219  public static int getIntStaticFieldValue(RVMField field) throws NoSuchFieldException {
220    if (VM.runningVM) {
221      return Statics.getSlotContentsAsInt(field.getOffset());
222    } else {
223      try {
224        Field f = getJDKField(field);
225        TypeReference fieldType = field.getType();
226        if (fieldType.isBooleanType()) {
227          boolean val = f.getBoolean(null);
228          return val ? 1 : 0;
229        } else if (fieldType.isByteType()) {
230          return f.getByte(null);
231        } else if (fieldType.isShortType()) {
232          return f.getShort(null);
233        } else if (fieldType.isIntType()) {
234          return f.getInt(null);
235        } else if (fieldType.isCharType()) {
236          return f.getChar(null);
237        } else {
238          throw new OptimizingCompilerException("Unsupported type " + field + "\n");
239        }
240      } catch (IllegalAccessException e) {
241        throwOptimizingCompilerExceptionBecauseOfIllegalAccess(field, e);
242      } catch (IllegalArgumentException e) {
243        throwOptimizingCompilerExceptionBecauseOfIllegalAccess(field, e);
244      }
245      assertNotReached();
246      return 0;
247    }
248  }
249
250  /**
251   * Returns the current contents of a float static field.
252   *
253   * @param field a static field
254   * @return the current value of the field
255   * @throws NoSuchFieldException when the field could not be found
256   */
257  public static float getFloatStaticFieldValue(RVMField field) throws NoSuchFieldException {
258    if (VM.runningVM) {
259      int bits = Statics.getSlotContentsAsInt(field.getOffset());
260      return Magic.intBitsAsFloat(bits);
261    } else {
262      try {
263        return getJDKField(field).getFloat(null);
264      } catch (IllegalAccessException e) {
265        throwOptimizingCompilerExceptionBecauseOfIllegalAccess(field, e);
266      } catch (IllegalArgumentException e) {
267        throwOptimizingCompilerExceptionBecauseOfIllegalAccess(field, e);
268      }
269      assertNotReached();
270      return 0f;
271    }
272  }
273
274  /**
275   * Returns the current contents of a long static field.
276   *
277   * @param field a static field
278   * @return the current value of the field
279   * @throws NoSuchFieldException when the field could not be found
280   */
281  public static long getLongStaticFieldValue(RVMField field) throws NoSuchFieldException {
282    if (VM.runningVM) {
283      return Statics.getSlotContentsAsLong(field.getOffset());
284    } else {
285      try {
286        return getJDKField(field).getLong(null);
287      } catch (IllegalAccessException e) {
288        throwOptimizingCompilerExceptionBecauseOfIllegalAccess(field, e);
289      } catch (IllegalArgumentException e) {
290        throwOptimizingCompilerExceptionBecauseOfIllegalAccess(field, e);
291      }
292      assertNotReached();
293      return 0L;
294    }
295  }
296
297  /**
298   * Returns the current contents of a double static field.
299   *
300   * @param field a static field
301   * @return the current value of the field
302   * @throws NoSuchFieldException when the field could not be found
303   */
304  public static double getDoubleStaticFieldValue(RVMField field) throws NoSuchFieldException {
305    if (VM.runningVM) {
306      long bits = Statics.getSlotContentsAsLong(field.getOffset());
307      return Magic.longBitsAsDouble(bits);
308    } else {
309      try {
310        return getJDKField(field).getDouble(null);
311      } catch (IllegalAccessException e) {
312        throwOptimizingCompilerExceptionBecauseOfIllegalAccess(field, e);
313      } catch (IllegalArgumentException e) {
314        throwOptimizingCompilerExceptionBecauseOfIllegalAccess(field, e);
315      }
316      assertNotReached();
317      return 0d;
318    }
319  }
320
321  /**
322   * Returns the current contents of a reference static field.
323   *
324   * @param field a static field
325   * @return the current value of the field
326   * @throws NoSuchFieldException when the field could not be found
327   */
328  public static Object getObjectStaticFieldValue(RVMField field) throws NoSuchFieldException {
329    if (VM.runningVM) {
330      return Statics.getSlotContentsAsObject(field.getOffset());
331    } else {
332      try {
333        return getJDKField(field).get(null);
334      } catch (IllegalAccessException e) {
335        throwOptimizingCompilerExceptionBecauseOfIllegalAccess(field, e);
336      } catch (IllegalArgumentException e) {
337        throwOptimizingCompilerExceptionBecauseOfIllegalAccess(field, e);
338      }
339      assertNotReached();
340      return null;
341    }
342  }
343
344  /**
345   * Returns the current contents of a Address static field.
346   *
347   * @param field a static field
348   * @return the current value of the field
349   * @throws NoSuchFieldException when the field could not be found
350   */
351  public static Address getAddressStaticFieldValue(RVMField field) throws NoSuchFieldException {
352    if (VM.runningVM) {
353      return Statics.getSlotContentsAsAddress(field.getOffset());
354    } else {
355      try {
356        Object unboxed = getJDKField(field).get(null);
357        if (unboxed instanceof Address) {
358          return (Address) unboxed;
359        } else if (unboxed instanceof Word) {
360          return ((Word) unboxed).toAddress();
361        } else if (unboxed instanceof Extent) {
362          return ((Extent) unboxed).toWord().toAddress();
363        } else if (unboxed instanceof Offset) {
364          return ((Offset) unboxed).toWord().toAddress();
365        } else {
366          if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED);
367          return Address.zero();
368        }
369      } catch (IllegalAccessException e) {
370        throwOptimizingCompilerExceptionBecauseOfIllegalAccess(field, e);
371      } catch (IllegalArgumentException e) {
372        throwOptimizingCompilerExceptionBecauseOfIllegalAccess(field, e);
373      }
374      assertNotReached();
375      return Address.zero();
376    }
377  }
378
379  /**
380   * Does a static field null contain {@code null}?
381   *
382   * @param field a static field
383   * @return {@code true} if the field contains {@code null}, {@code false} otherwise
384   * @throws NoSuchFieldException when the field could not be found
385   */
386  public static boolean isStaticFieldNull(RVMField field) throws NoSuchFieldException {
387    return getObjectStaticFieldValue(field) == null;
388  }
389
390  /**
391   * Get the type of an object contained in a static field.
392   *
393   * @param field a static field
394   * @return type of value contained in the field
395   * @throws NoSuchFieldException when the field could not be found
396   */
397  public static TypeReference getTypeFromStaticField(RVMField field) throws NoSuchFieldException {
398    Object o = getObjectStaticFieldValue(field);
399    if (o == null) return TypeReference.NULL_TYPE;
400    if (VM.runningVM) {
401      return Magic.getObjectType(o).getTypeRef();
402    } else {
403      return TypeReference.findOrCreate(o.getClass());
404    }
405  }
406
407  /**
408   * Converts a RVMField to a java.lang.reflect.Field.
409   *
410   * @param field the internal field representation
411   * @return the java.lang field representation
412   * @throws NoSuchFieldException when the field could not be found
413   */
414  private static Field getJDKField(RVMField field) throws NoSuchFieldException {
415    try {
416      String cn = field.getDeclaringClass().toString();
417      if (VM.BuildForGnuClasspath &&
418          field.getDeclaringClass().getClassForType().equals(java.lang.reflect.Proxy.class) &&
419          field.getName().toString().equals("proxyClasses")) {
420        // Avoid confusing bootstrap JVM and classpath fields
421        throw new NoSuchFieldException(field.toString());
422      }
423      Field f = Class.forName(cn).getDeclaredField(field.getName().toString());
424      f.setAccessible(true);
425      return f;
426    } catch (NoSuchFieldError e) {
427      throwNoSuchFieldExceptionWithCause(field, e);
428    } catch (ClassNotFoundException e) {
429      throwNoSuchFieldExceptionWithCause(field, e);
430    } catch (NoClassDefFoundError e) {
431      throwNoSuchFieldExceptionWithCause(field, e);
432    } catch (IllegalAccessError e) {
433      throwNoSuchFieldExceptionWithCause(field, e);
434    } catch (UnsatisfiedLinkError e) {
435      throwNoSuchFieldExceptionWithCause(field, e);
436    }
437    assertNotReached();
438    return null;
439  }
440
441  private static void throwOptimizingCompilerExceptionBecauseOfIllegalAccess(
442      RVMField field, Throwable e) {
443    throw new OptimizingCompilerException("Accessing " + field + " caused " + e);
444  }
445
446  private static void throwNoSuchFieldExceptionWithCause(RVMField field, Throwable cause)
447      throws NoSuchFieldException {
448    NoSuchFieldException e = new NoSuchFieldException(field.toString());
449    e.initCause(cause);
450    throw e;
451  }
452
453  private static void assertNotReached() {
454    if (VM.VerifyAssertions) {
455      VM._assert(VM.NOT_REACHED, "Exception should have been thrown beforehand");
456    } else {
457      VM.sysFail("An exception should have been thrown before this point was reached!");
458    }
459  }
460
461}