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