1 /*
   2  * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package org.graalvm.compiler.core.common;
  24 
  25 import static org.graalvm.compiler.core.common.UnsafeAccess.UNSAFE;
  26 
  27 import java.util.ArrayList;
  28 import java.util.Collections;
  29 
  30 import org.graalvm.compiler.debug.GraalError;
  31 
  32 import sun.misc.Unsafe;
  33 
  34 /**
  35  * Describes fields in a class, primarily for access via {@link Unsafe}.
  36  */
  37 public class Fields {
  38 
  39     /**
  40      * Offsets used with {@link Unsafe} to access the fields.
  41      */
  42     protected final long[] offsets;
  43 
  44     /**
  45      * The names of the fields.
  46      */
  47     private final String[] names;
  48 
  49     /**
  50      * The types of the fields.
  51      */
  52     private final Class<?>[] types;
  53 
  54     private final Class<?>[] declaringClasses;
  55 
  56     public static Fields forClass(Class<?> clazz, Class<?> endClazz, boolean includeTransient, FieldsScanner.CalcOffset calcOffset) {
  57         FieldsScanner scanner = new FieldsScanner(calcOffset == null ? new FieldsScanner.DefaultCalcOffset() : calcOffset);
  58         scanner.scan(clazz, endClazz, includeTransient);
  59         return new Fields(scanner.data);
  60     }
  61 
  62     public Fields(ArrayList<? extends FieldsScanner.FieldInfo> fields) {
  63         Collections.sort(fields);
  64         this.offsets = new long[fields.size()];
  65         this.names = new String[offsets.length];
  66         this.types = new Class<?>[offsets.length];
  67         this.declaringClasses = new Class<?>[offsets.length];
  68         int index = 0;
  69         for (FieldsScanner.FieldInfo f : fields) {
  70             offsets[index] = f.offset;
  71             names[index] = f.name;
  72             types[index] = f.type;
  73             declaringClasses[index] = f.declaringClass;
  74             index++;
  75         }
  76     }
  77 
  78     /**
  79      * Gets the number of fields represented by this object.
  80      */
  81     public int getCount() {
  82         return offsets.length;
  83     }
  84 
  85     public static void translateInto(Fields fields, ArrayList<FieldsScanner.FieldInfo> infos) {
  86         for (int index = 0; index < fields.getCount(); index++) {
  87             infos.add(new FieldsScanner.FieldInfo(fields.offsets[index], fields.names[index], fields.types[index], fields.declaringClasses[index]));
  88         }
  89     }
  90 
  91     /**
  92      * Function enabling an object field value to be replaced with another value when being copied
  93      * within {@link Fields#copy(Object, Object, ObjectTransformer)}.
  94      */
  95     @FunctionalInterface
  96     public interface ObjectTransformer {
  97         Object apply(int index, Object from);
  98     }
  99 
 100     /**
 101      * Copies fields from {@code from} to {@code to}, both of which must be of the same type.
 102      *
 103      * @param from the object from which the fields should be copied
 104      * @param to the object to which the fields should be copied
 105      */
 106     public void copy(Object from, Object to) {
 107         copy(from, to, null);
 108     }
 109 
 110     /**
 111      * Copies fields from {@code from} to {@code to}, both of which must be of the same type.
 112      *
 113      * @param from the object from which the fields should be copied
 114      * @param to the object to which the fields should be copied
 115      * @param trans function to applied to object field values as they are copied. If {@code null},
 116      *            the value is copied unchanged.
 117      */
 118     public void copy(Object from, Object to, ObjectTransformer trans) {
 119         assert from.getClass() == to.getClass();
 120         for (int index = 0; index < offsets.length; index++) {
 121             long offset = offsets[index];
 122             Class<?> type = types[index];
 123             if (type.isPrimitive()) {
 124                 if (type == Integer.TYPE) {
 125                     UNSAFE.putInt(to, offset, UNSAFE.getInt(from, offset));
 126                 } else if (type == Long.TYPE) {
 127                     UNSAFE.putLong(to, offset, UNSAFE.getLong(from, offset));
 128                 } else if (type == Boolean.TYPE) {
 129                     UNSAFE.putBoolean(to, offset, UNSAFE.getBoolean(from, offset));
 130                 } else if (type == Float.TYPE) {
 131                     UNSAFE.putFloat(to, offset, UNSAFE.getFloat(from, offset));
 132                 } else if (type == Double.TYPE) {
 133                     UNSAFE.putDouble(to, offset, UNSAFE.getDouble(from, offset));
 134                 } else if (type == Short.TYPE) {
 135                     UNSAFE.putShort(to, offset, UNSAFE.getShort(from, offset));
 136                 } else if (type == Character.TYPE) {
 137                     UNSAFE.putChar(to, offset, UNSAFE.getChar(from, offset));
 138                 } else if (type == Byte.TYPE) {
 139                     UNSAFE.putByte(to, offset, UNSAFE.getByte(from, offset));
 140                 } else {
 141                     assert false : "unhandled property type: " + type;
 142                 }
 143             } else {
 144                 Object obj = UNSAFE.getObject(from, offset);
 145                 UNSAFE.putObject(to, offset, trans == null ? obj : trans.apply(index, obj));
 146             }
 147         }
 148     }
 149 
 150     /**
 151      * Gets the value of a field for a given object.
 152      *
 153      * @param object the object whose field is to be read
 154      * @param index the index of the field (between 0 and {@link #getCount()})
 155      * @return the value of the specified field which will be boxed if the field type is primitive
 156      */
 157     public Object get(Object object, int index) {
 158         long offset = offsets[index];
 159         Class<?> type = types[index];
 160         Object value = null;
 161         if (type.isPrimitive()) {
 162             if (type == Integer.TYPE) {
 163                 value = UNSAFE.getInt(object, offset);
 164             } else if (type == Long.TYPE) {
 165                 value = UNSAFE.getLong(object, offset);
 166             } else if (type == Boolean.TYPE) {
 167                 value = UNSAFE.getBoolean(object, offset);
 168             } else if (type == Float.TYPE) {
 169                 value = UNSAFE.getFloat(object, offset);
 170             } else if (type == Double.TYPE) {
 171                 value = UNSAFE.getDouble(object, offset);
 172             } else if (type == Short.TYPE) {
 173                 value = UNSAFE.getShort(object, offset);
 174             } else if (type == Character.TYPE) {
 175                 value = UNSAFE.getChar(object, offset);
 176             } else if (type == Byte.TYPE) {
 177                 value = UNSAFE.getByte(object, offset);
 178             } else {
 179                 assert false : "unhandled property type: " + type;
 180             }
 181         } else {
 182             value = UNSAFE.getObject(object, offset);
 183         }
 184         return value;
 185     }
 186 
 187     /**
 188      * Gets the value of a field for a given object.
 189      *
 190      * @param object the object whose field is to be read
 191      * @param index the index of the field (between 0 and {@link #getCount()})
 192      * @return the value of the specified field which will be boxed if the field type is primitive
 193      */
 194     public long getRawPrimitive(Object object, int index) {
 195         long offset = offsets[index];
 196         Class<?> type = types[index];
 197 
 198         if (type == Integer.TYPE) {
 199             return UNSAFE.getInt(object, offset);
 200         } else if (type == Long.TYPE) {
 201             return UNSAFE.getLong(object, offset);
 202         } else if (type == Boolean.TYPE) {
 203             return UNSAFE.getBoolean(object, offset) ? 1 : 0;
 204         } else if (type == Float.TYPE) {
 205             return Float.floatToRawIntBits(UNSAFE.getFloat(object, offset));
 206         } else if (type == Double.TYPE) {
 207             return Double.doubleToRawLongBits(UNSAFE.getDouble(object, offset));
 208         } else if (type == Short.TYPE) {
 209             return UNSAFE.getShort(object, offset);
 210         } else if (type == Character.TYPE) {
 211             return UNSAFE.getChar(object, offset);
 212         } else if (type == Byte.TYPE) {
 213             return UNSAFE.getByte(object, offset);
 214         } else {
 215             throw GraalError.shouldNotReachHere();
 216         }
 217     }
 218 
 219     /**
 220      * Determines if a field in the domain of this object is the same as the field denoted by the
 221      * same index in another {@link Fields} object.
 222      */
 223     public boolean isSame(Fields other, int index) {
 224         return other.offsets[index] == offsets[index];
 225     }
 226 
 227     public long[] getOffsets() {
 228         return offsets;
 229     }
 230 
 231     /**
 232      * Gets the name of a field.
 233      *
 234      * @param index index of a field
 235      */
 236     public String getName(int index) {
 237         return names[index];
 238     }
 239 
 240     /**
 241      * Gets the type of a field.
 242      *
 243      * @param index index of a field
 244      */
 245     public Class<?> getType(int index) {
 246         return types[index];
 247     }
 248 
 249     public Class<?> getDeclaringClass(int index) {
 250         return declaringClasses[index];
 251     }
 252 
 253     /**
 254      * Checks that a given field is assignable from a given value.
 255      *
 256      * @param index the index of the field to check
 257      * @param value a value that will be assigned to the field
 258      */
 259     private boolean checkAssignableFrom(Object object, int index, Object value) {
 260         assert value == null || getType(index).isAssignableFrom(value.getClass()) : String.format("Field %s.%s of type %s is not assignable from %s", object.getClass().getSimpleName(),
 261                         getName(index), getType(index).getSimpleName(), value.getClass().getSimpleName());
 262         return true;
 263     }
 264 
 265     public void set(Object object, int index, Object value) {
 266         long offset = offsets[index];
 267         Class<?> type = types[index];
 268         if (type.isPrimitive()) {
 269             if (type == Integer.TYPE) {
 270                 UNSAFE.putInt(object, offset, (Integer) value);
 271             } else if (type == Long.TYPE) {
 272                 UNSAFE.putLong(object, offset, (Long) value);
 273             } else if (type == Boolean.TYPE) {
 274                 UNSAFE.putBoolean(object, offset, (Boolean) value);
 275             } else if (type == Float.TYPE) {
 276                 UNSAFE.putFloat(object, offset, (Float) value);
 277             } else if (type == Double.TYPE) {
 278                 UNSAFE.putDouble(object, offset, (Double) value);
 279             } else if (type == Short.TYPE) {
 280                 UNSAFE.putShort(object, offset, (Short) value);
 281             } else if (type == Character.TYPE) {
 282                 UNSAFE.putChar(object, offset, (Character) value);
 283             } else if (type == Byte.TYPE) {
 284                 UNSAFE.putByte(object, offset, (Byte) value);
 285             } else {
 286                 assert false : "unhandled property type: " + type;
 287             }
 288         } else {
 289             assert checkAssignableFrom(object, index, value);
 290             UNSAFE.putObject(object, offset, value);
 291         }
 292     }
 293 
 294     public void setRawPrimitive(Object object, int index, long value) {
 295         long offset = offsets[index];
 296         Class<?> type = types[index];
 297         if (type == Integer.TYPE) {
 298             UNSAFE.putInt(object, offset, (int) value);
 299         } else if (type == Long.TYPE) {
 300             UNSAFE.putLong(object, offset, value);
 301         } else if (type == Boolean.TYPE) {
 302             UNSAFE.putBoolean(object, offset, value != 0);
 303         } else if (type == Float.TYPE) {
 304             UNSAFE.putFloat(object, offset, Float.intBitsToFloat((int) value));
 305         } else if (type == Double.TYPE) {
 306             UNSAFE.putDouble(object, offset, Double.longBitsToDouble(value));
 307         } else if (type == Short.TYPE) {
 308             UNSAFE.putShort(object, offset, (short) value);
 309         } else if (type == Character.TYPE) {
 310             UNSAFE.putChar(object, offset, (char) value);
 311         } else if (type == Byte.TYPE) {
 312             UNSAFE.putByte(object, offset, (byte) value);
 313         } else {
 314             throw GraalError.shouldNotReachHere();
 315         }
 316     }
 317 
 318     @Override
 319     public String toString() {
 320         StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('[');
 321         appendFields(sb);
 322         return sb.append(']').toString();
 323     }
 324 
 325     public void appendFields(StringBuilder sb) {
 326         for (int i = 0; i < offsets.length; i++) {
 327             sb.append(i == 0 ? "" : ", ").append(getName(i)).append('@').append(offsets[i]);
 328         }
 329     }
 330 
 331     public boolean getBoolean(Object n, int i) {
 332         assert types[i] == boolean.class;
 333         return UNSAFE.getBoolean(n, offsets[i]);
 334     }
 335 
 336     public byte getByte(Object n, int i) {
 337         assert types[i] == byte.class;
 338         return UNSAFE.getByte(n, offsets[i]);
 339     }
 340 
 341     public short getShort(Object n, int i) {
 342         assert types[i] == short.class;
 343         return UNSAFE.getShort(n, offsets[i]);
 344     }
 345 
 346     public char getChar(Object n, int i) {
 347         assert types[i] == char.class;
 348         return UNSAFE.getChar(n, offsets[i]);
 349     }
 350 
 351     public int getInt(Object n, int i) {
 352         assert types[i] == int.class;
 353         return UNSAFE.getInt(n, offsets[i]);
 354     }
 355 
 356     public long getLong(Object n, int i) {
 357         assert types[i] == long.class;
 358         return UNSAFE.getLong(n, offsets[i]);
 359     }
 360 
 361     public float getFloat(Object n, int i) {
 362         assert types[i] == float.class;
 363         return UNSAFE.getFloat(n, offsets[i]);
 364     }
 365 
 366     public double getDouble(Object n, int i) {
 367         assert types[i] == double.class;
 368         return UNSAFE.getDouble(n, offsets[i]);
 369     }
 370 
 371     public Object getObject(Object object, int i) {
 372         assert !types[i].isPrimitive();
 373         return UNSAFE.getObject(object, offsets[i]);
 374     }
 375 
 376     public void putObject(Object object, int i, Object value) {
 377         assert checkAssignableFrom(object, i, value);
 378         UNSAFE.putObject(object, offsets[i], value);
 379     }
 380 }