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