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 }