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 }